简体   繁体   English

有没有比FileStream.WriteByte()方法更快的替代方法,或者有什么方法可以在C#中加快FileStream.WriteByte()的速度?

[英]Is there any faster alternative to FileStream.WriteByte() method or any way to speed up the FileStream.WriteByte() in C#?

I'm a beginner of C# programming. 我是C#编程的初学者。 Recently I created a simple file encrypting program and a decrypting program. 最近,我创建了一个简单的文件加密程序和一个解密程序。 First I took a file into a FileStream and altered the each byte of the file according to a password. 首先,我将一个文件放入FileStream中,并根据密码更改了文件的每个字节。 Here's what my code 这是我的代码

Code of the Encrypter 加密程式码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;

namespace Encrypter
{
    class Program
    {
        static void Main(string[] args)
        {         
            //Actual code starts from here
            string file_path = @args[0];

            string[] splited_filepath = file_path.Split('\\');
            string file_name = splited_filepath[splited_filepath.GetLength(0) - 1];

            FileStream file;
            if (File.Exists(file_path))
            {
                file = new FileStream(file_path, FileMode.Open);
                if (!file.CanWrite)
                {
                    Console.WriteLine("The file exists, but not writable!");
                    return;
                    //goto exit;
                }
            }
            else
            {
                Console.WriteLine("The file does not exist!\n{0}", file_path);
                return;
                //goto exit;
            }

            Console.Write("Enter the password : ");
            string password = Console.ReadLine();

            if(password == "")
            {
                Console.WriteLine("Password Cannot be empty!");
                return;
            }

            try
            {
                while(file.Position < file.Length)
                { 
                    int b_input = file.ReadByte();
                    file.Position--;
                    file.WriteByte(encrypt_byte(b_input,password,file.Position));
                    file.Position++;
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
                //goto exit;
            }

            file.Close();
            string encrypted_file_name = base64_enc(file_name) + compute_hash(password, new MD5CryptoServiceProvider());
            string encrypted_file_path = create_encrypted_file_path(encrypted_file_name, splited_filepath);

            try
            {
                File.Move(file_path, encrypted_file_path);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            //exit:;
        }

        static string create_encrypted_file_path(string enc_filename, string[] splited_fp)
        {
            string output = "";
            for(int a = 0; a < splited_fp.GetLength(0) - 1; a++)
            {
                output += (splited_fp[a] + "\\");
            }
            output += enc_filename;
            return output;
        }

        //Encrypting method starts here
        static byte encrypt_byte(int input, string pass, long pos)
        {
            char[] pass_char = pass.ToArray();
            int char_pos = Convert.ToInt32(pos % pass_char.GetLength(0));
            byte output = Convert.ToByte(
                (input + Convert.ToInt32(pass_char[char_pos])) % 256
                );
            return output;
        }

        static string compute_hash(string input,HashAlgorithm algorithm)
        {
            return BitConverter.ToString(algorithm.ComputeHash(Encoding.UTF8.GetBytes(input)));
        }

        //base64 encoding method
        static string base64_enc(string input)
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(input));
        }
    }
}

Code of the decrypter 解密器的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;

namespace Decrypter
{
    class Program
    {
        static void Main(string[] args)
        {
            //Actual code starts from here
            string file_path = args[0];

            string[] splited_filepath = file_path.Split('\\');
            string file_name = splited_filepath[splited_filepath.GetLength(0) - 1];

            string encrypted_filename = file_name.Substring(0, file_name.Length - 47);
            string hashed_password = file_name.Substring(file_name.Length - 47);

            FileStream file;
            if (File.Exists(file_path))
            {
                file = new FileStream(file_path, FileMode.Open);
                if (!file.CanWrite)
                {
                    Console.WriteLine("The file exists, but not writable!");
                    return;
                    //goto exit;
                }
            }
            else
            {
                Console.WriteLine("The file does not exist!\n{0}", file_path);
                Console.ReadLine();
                return;
                //goto exit;
            }

            Console.Write("Enter the password : ");
            string password = Console.ReadLine();
            if(password == "")
            {
                Console.WriteLine("Password cannot be empty!");
                return;
            }

            //Varify the password
            if(compute_hash(password,new MD5CryptoServiceProvider()) != hashed_password)
            {
                Console.WriteLine(compute_hash(password, new MD5CryptoServiceProvider()));
                Console.WriteLine(hashed_password);
                Console.WriteLine("Invalid password!");
                return;
            }

            try
            {
                while(file.Position < file.Length)
                {                
                    int b_input = file.ReadByte();
                    file.Position--;
                    file.WriteByte(decrypt_byte(b_input, password, file.Position));
                    file.Position++;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
                return;
                //goto exit;
            }
            file.Close();
            string decrypted_filename = base64_dec(encrypted_filename);
            string decrypted_filepath = create_decrypted_file_path(decrypted_filename, splited_filepath);

            try
            {
                File.Move(file_path, decrypted_filepath);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }

            //exit:;
        }

        //Decrypting method starts here
        static byte decrypt_byte(int input, string pass, long pos)
        {
            char[] pass_char = pass.ToArray();
            int char_pos = Convert.ToInt32(pos % pass_char.GetLength(0));
            byte output = Convert.ToByte(
                get_output_byte(input - Convert.ToInt32(pass_char[char_pos]))
                );
            return output;
        }

        //a simple method to get the numaric value of the output byte
        static int get_output_byte(int number)
        {
            if (number < 0) return 256 + number;
            else return number;
        }

        static string compute_hash(string input, HashAlgorithm algorithm)
        {
            return BitConverter.ToString(algorithm.ComputeHash(Encoding.UTF8.GetBytes(input)));
        }

        //base64 decoding method
        static string base64_dec(string input)
        {
            return Encoding.UTF8.GetString(Convert.FromBase64String(input));
        }

        static string create_decrypted_file_path(string enc_filename, string[] splited_fp)
        {
            string output = "";
            for (int a = 0; a < splited_fp.GetLength(0) - 1; a++)
            {
                output += (splited_fp[a] + "\\");
            }
            output += enc_filename;
            return output;
        }
    }
}

The problem is both of these programs are considerably slow and it takes hours to encrypt or decrypt large files. 问题在于这两个程序都相当慢,并且需要花费数小时来加密或解密大文件。 Any suggestions to speed up the process? 有什么建议可以加快这个过程吗?

This is about the worst possible way I can think of to use a FileStream in a loop: 大约是最坏的方式,我能想到的使用FileStream在一个循环:

int b_input = file.ReadByte();
file.Position--;
file.WriteByte(encrypt_byte(b_input,password,file.Position));
file.Position++;

Okay, the first line isn't bad. 好吧,第一行还不错。 The second line causes the stream to flush its write buffer and invalidate its read buffer. 第二行使流刷新其写缓冲区并使它的读缓冲区无效。 Then we write a new byte and adjust the position again, causing another flush and invalidation (and causes us to skip every other byte to boot, because the WriteByte already updated the file position). 然后,我们写入一个新字节并再次调整位置,从而导致另一个刷新和无效操作(并导致我们跳过其他所有字节来启动,因为WriteByte已更新文件位置)。

Simple solution is to use File.ReadAllBytes and just operate in memory. 一个简单的解决方案是使用File.ReadAllBytes并仅在内存中进行操作。 However, as you yourself observed, this doesn't work well for large files. 但是,正如您自己观察到的那样,这不适用于大文件。

Instead, you should work with a reasonably sized buffer (say var buffer = new byte[4096]; ) and then use file.Read to read the file in chunks 1 . 相反,您应该使用大小适当的缓冲区(例如var buffer = new byte[4096]; ),然后使用file.Read读取块1中的文件。 It's better if you can also use a separate stream pointing at a second file for the write side so that you still benefit from buffers on both sides. 最好还可以使用指向第二个文件的单独流作为写入侧,以便仍可以从两侧的缓冲区中受益。

You have to maintain your own count of the position within the file rather than relying on the file's Position property, and your loop should terminate when Read returns 0 (but make sure you're always paying attention to that value and only process that much of the buffer). 您必须维护自己在文件中位置的数量,而不是依靠文件的Position属性,并且循环应在Read返回0时终止(但请确保您始终注意该值并且仅处理该值的大部分)缓冲区)。

Finally, close both streams, delete the old file and move/rename the new file to replace it. 最后,关闭两个流,删除旧文件并移动/重命名新文件以替换它。


Note that this sort of "encryption" is suitable for toy usage but shouldn't be seriously used for anything. 请注意,这种“加密”适用于玩具使用,但不应认真用于任何东西。 You're already aware of the System.Security.Cryptography namespace. 您已经知道System.Security.Cryptography命名空间。 For any serious encryption work, you should be looking for existing classes in there, not rolling your own. 对于任何认真的加密工作,您应该在其中寻找现有的类,而不要自己滚动。


1 Consider also using Async variants. 1考虑也使用Async变量。 Most of the time will still be spent doing I/O . 大部分时间仍然会花在I / O上 Blocking a thread whilst that's happening doesn't add much value to the system. 在这种情况下阻塞线程不会为系统增加太多价值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM