这是我的代码:
byte[] bytes = File.ReadAllBytes(@"D:\project\wb_header.txt");
byte[] outp = bytes.Where(c => c >= 32 && c < 127).ToArray();
File.WriteAllBytes(@"D:\project\outputfile.txt", outp);
这里我计算wb_header.txt文件中的所有非Ascii字符,并在删除输出文件正在创建的非ascii字符之后。但问题是我不想删除字符,我想用一些字母或ASCII字符替换它来维护与wb_header.txt文件相同的文件格式。怎么做 ?请在此处添加一些代码
答案 0 :(得分:3)
只有当您的原始文件是ASCII时,您的代码才有效,在这种情况下,如果您只想删除不可打印的(我知道的旧定义)字符:
byte[] output = bytes
.Select(c => (c >= 32 && c <= 127) ? c : (byte)63).ToArray();
这删除了所有不可打印的字符,用?替换它们(问号,ASCII码63)。
现在让我们看看为什么您的原始代码不适用于非ASCII文件。文本始终是编码(ASCII,UTF-8,UTF-16和许多其他)。在大多数编码中,前127个值是相同的,因此您的代码可能有效但有些字符需要编码才需要多个字节。例如,这个意大利语句子:Èsù将以UTF-8编码,如下所示:
Bytes Characters 195 136 È 32 115 s 195 185 ù
正如您所看到的,某些字符需要多个字节。对于不同的编码,值当然是不同的。此外,一些文本文件有一个BOM标记来显示它们的编码(它应该被忽略)。有一些技术可以转换,例如, e 中的è(SO上有一些非常好的文章),但不幸的是你永远不能将非ASCII文本视为字节(我不要甚至没有提到远东语言。一个很好的近似可以是首先将文件作为文本读取(如果你不知道它的编码框架可以检查你是否文件以BOM开头,这是一个严重的问题,因为除非你对文件内容有一些强有力的了解,否则你不能猜测,也见this):
string content = File.ReadAllText("file.txt");
现在让我们用ASCII表示它(非ASCII字符将自动替换为?):
byte[] output = Encoding.ASCII.GetBytes(content);
此字节数组将包含不可打印的字符(在[32 ... 127]范围之外),那么您仍可能需要应用相同的过滤器:
byte[] output = Encoding.ASCII.GetBytes(content)
.Select(c => (c >= 32 && c <= 127) ? c : (byte)63).ToArray();
最后说明:此代码根本没有效率。我们读取内存中的所有文件,我们转换为字节数组(在内存中)然后我们创建一个副本(再次在内存中),最后将其写回...对于大于几千字节的文件,您应该直接从文件中读取字符(具有适当的编码。啊......不要忘记UNICODE代理人和修改者......