c# - 有没有比 FileStream.WriteByte() 方法更快的替代方法或任何方法来加速 C# 中的 FileStream.WriteByte()?
问题描述
我是 C# 编程的初学者。最近我创建了一个简单的文件加密程序和一个解密程序。首先,我将一个文件放入 FileStream 并根据密码更改文件的每个字节。这是我的代码
加密器的代码
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));
}
}
}
解密器代码
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;
}
}
}
问题是这两个程序都相当慢,加密或解密大文件需要几个小时。有什么建议可以加快这个过程吗?
解决方案
这是我能想到的在循环中使用 a 的最糟糕的方法FileStream
:
int b_input = file.ReadByte();
file.Position--;
file.WriteByte(encrypt_byte(b_input,password,file.Position));
file.Position++;
好吧,第一行还不错。第二行导致流刷新其写入缓冲区并使其读取缓冲区无效。然后我们写入一个新字节并再次调整位置,导致另一个刷新和失效(并导致我们跳过每隔一个字节引导,因为WriteByte
已经更新了文件位置)。
简单的解决方案是File.ReadAllBytes
在内存中使用和操作。但是,正如您自己观察到的,这不适用于大文件。
相反,您应该使用大小合理的缓冲区(例如var buffer = new byte[4096];
),然后使用file.Read
以块1读取文件。如果您还可以使用指向第二个文件的单独流作为写入端,则更好,这样您仍然可以从两侧的缓冲区中受益。
您必须维护自己对文件中位置的计数,而不是依赖文件的属性,并且您的循环应该在返回Position
时终止(但请确保您始终注意该值并且只处理那么多缓冲区) .Read
0
最后,关闭两个流,删除旧文件并移动/重命名新文件以替换它。
请注意,这种“加密”适用于玩具用途,但不应认真用于任何事情。您已经知道System.Security.Cryptography
命名空间。对于任何严肃的加密工作,您应该在那里寻找现有的课程,而不是自己动手。
1也考虑使用Async
变体。大部分时间仍然会花在做 I/O上。在发生这种情况时阻塞线程不会为系统增加太多价值。
推荐阅读
- reactjs - 使用 react.js 以电子邮件形式发送警报
- c# - 如何从外部获取数据到我的 web api 中?
- laravel - 如何安装新星面包屑?
- spring-security - Spring-Security:StrictHttpFirewall 不允许如果 url 包含?(例如有查询参数)
- django - Django:计算货物的数量
- python - Python权限错误
- salesforce - Salesforce - 错误“DML 需要 SObject 或 SObject 列表类型:帐户”
- rest - 错误 SoftLayer_Product_Order。(SoftLayer_Product_Order::verifyOrder)
- javascript - 如何使用用于 excel 插件的 excel javascript api 将列从一个工作表复制到另一个工作表
- python - Python Flask SQLAlchemy 未提交对数据库的更改