我正在使用 CsvHelper 库写入 CSV 文件。但是,我的一些 CSV 文件具有不同的编码,导致写入数据中出现随机字符。下面是一个代码示例,说明了我目前在项目中如何处理这个问题。我该如何解决?
public async Task WriteAsync<T>(string path, T record)
{
bool containsNewLines = ContainsNewLines(path);
using (var stream = File.Open(path, FileMode.Append))
using (var writer = new StreamWriter(stream, Encoding.UTF8))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
if (!containsNewLines)
{
await csv.NextRecordAsync();
}
csv.WriteRecord(record);
await csv.NextRecordAsync();
}
}
private bool ContainsNewLines(string filePath)
{
using (var reader = new StreamReader(filePath))
{
string content = reader.ReadToEnd();
return content.EndsWith(Environment.NewLine);
}
}
问题是,没有万无一失的方法来确定文件的编码。正如 Panagiotis Kanavos 所说,最好的答案是要么要求所有文件都采用单一编码,比如
UTF8
,要么让文件的创建者以某种方式为您提供文件的编码。
也就是说,可以猜测编码。这是对 Berthier Lemieux 的答案 的修改,用于检测文件编码。该方法读取整个文件并假设它是
UTF8
编码。如果阅读器在读取 UTF8
时抛出异常,则默认为您首选的 ANSI
编码。这种方法最适合我确定 UTF8
和 ANSI
编码之间的差异。它不会检测所有类型的编码。
public Encoding DetectFileEncoding(string fileName, Encoding defaultEncoding)
{
var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8",
new EncoderExceptionFallback(), new DecoderExceptionFallback());
using (var reader = new StreamReader(fileName, Utf8EncodingVerifier,
detectEncodingFromByteOrderMarks: true, bufferSize: 1024))
{
try
{
while (!reader.EndOfStream)
{
_ = reader.ReadLine();
}
return reader.CurrentEncoding;
}
catch (Exception)
{
// Failed to decode the file using the BOM/UT8.
// return default encoding
return defaultEncoding;
}
}
}
然后您可以使用
DetectFileEncoding
设置 StreamWriter
的编码。如果您的文件不太可能采用 Latin1 (ISO-8859-1)
编码,那么您可以使用最适合您的默认编码。
public async Task WriteAsync<T>(string path, T record)
{
bool containsNewLines = ContainsNewLines(path);
Encoding fileEncoding = DetectFileEncoding(path, Encoding.Latin1);
using (var stream = File.Open(path, FileMode.Append))
using (var writer = new StreamWriter(stream, fileEncoding))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
if (!containsNewLines)
{
await csv.NextRecordAsync();
}
csv.WriteRecord(record);
await csv.NextRecordAsync();
}
}