【问题标题】:c# encoding issues with CsvHelper使用 CsvHelper 的 c# 编码问题
【发布时间】:2021-03-08 03:48:11
【问题描述】:
var orders = new List<Order>();
....
orders.Add(...)


string csvstring;
using (var ms = new MemoryStream())
using (var wr = new StreamWriter(stream, Encoding.UTF8))
using (var csvWriter = new CsvWriter(wr, CultureInfo.InvariantCulture, false))
{
    

csvWriter.WriteRecords(orders);
    csvstring = Encoding.UTF8.GetString(stream.ToArray());
}

然后

sftp.WriteAllText(fileNameAbsolutePath, csvstring, Encoding.UTF8);

在 sftp 中创建的文件的内容以“feff”开头。 " orders.csv: text/plain; charset=utf-8".

这是问题的第一部分。我正在寻找的是将此 UTF8 转换为 IS0-8859-1 为 最终文件中预期的字符集是 IS0-8859-1。

我应该做这样的事情吗?

byte[] bytesSS = Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding("ISO-8859-1"), Encoding.UTF8.GetBytes(csvstring));

string s1 = Encoding.GetEncoding("ISO-8859-1").GetString(bytesSS, 0, bytesSS.Length);

尝试在 Google 上搜索“&lt;feff&gt;”,但我完全不了解 BOM 的概念以及解决此问题的方法。

【问题讨论】:

  • 如果你想写成IS0-8859-1,你为什么要到处指定UTF8?
  • 不涉及FEFF,CsvHelper 也不会发出 HTTP Content-Type 标头。所有应用程序都可以处理 UTF8。也没有理由使用Encoding.UTF8.GetString。那个sftp 变量是什么?为什么要用WriteAllText 给它写信?
  • 如果要创建 Latin1 文件,请在 StreamWriter 中使用正确的编码。无论sftp 是什么,我都怀疑它有一种将字节写入流的方式。如果要将文件上传到 SFTP 站点,可以使用 Stream.CopyTo(Stream) 将内存流的内容复制到目标流
  • 我把两个问题混在一起,造成了混乱。也许我们现在可以在查看转换之前先讨论 UTF8 编码的第一个问题(请暂时忽略拉丁转换)。首先,我检查了流的当前编码(使用 new StreamReader(stream).CurrentEncoding),发现它是 UTF8;然后尝试使用相同的编码将文件写入 sftp;但它以“feff”结束
  • 如果FEFF 的意思是一个 byte 值 0xFEFF,它很可能是一个字节顺序标记。默认情况下,StreamWriter 不会发出 BOM,而是发出 Encoding.UTF8 specifies its use。原因是虽然 UTF8 文件不应该使用 BOM,但 应用程序 直到遇到第一个意外字符时才知道文件是否为 UTF8。

标签: c# encoding iso-8859-1 csvhelper byte-order-mark


【解决方案1】:

我不知道您使用哪个 SFTP 类作为 .NET 本身没有 SFTP 客户端。我假设您使用 this one 只是因为它在 Google 搜索 sftp WriteAllText 时排在首位。

如果要创建具有特定编码的文件,请在 StreamWriter 构造函数中指定它而不是 UTF8:

using (var ms = new MemoryStream())
using (var wr = new StreamWriter(stream, Encoding.GetEncoding("ISO-8859-1")))
using (var csvWriter = new CsvWriter(wr, CultureInfo.InvariantCulture, false))
{    
    csvWriter.WriteRecords(orders);
}

另一方面,UTF8 和 Latin1(或任何代码页)对 0-127 范围内的字符使用完全相同的值。如果您只想发送英文文本,那么无论您使用哪种编码都不会有任何区别。如果实际要求是创建一个没有 BOM 的 UTF8 文件,您可以使用适当的 UTF8Encoding constructor 来指定它:

var utf8NoBom=new UTF8Encoding(false);
using (var ms = new MemoryStream())
using (var wr = new StreamWriter(stream, utf8NoBom)))
using (var csvWriter = new CsvWriter(wr, CultureInfo.InvariantCulture, false))
{    
    csvWriter.WriteRecords(orders);
}

所有 SFTP 客户端都有(或应该有)一种使用流上传数据的方法。这意味着您可以使用Stream.CopyTo 将数据从内存流复制到上传流。假设OpenWrite可用,可以修改代码为:

using (var ms = new MemoryStream())
{
    using (var wr = new StreamWriter(stream, Encoding.GetEncoding("ISO-8859-1")))
    using (var csvWriter = new CsvWriter(wr, CultureInfo.InvariantCulture, false))
    {    
        csvWriter.WriteRecords(orders);
    }

    ms.Position=0;

    using(var stream=sftp.OpenWrite(somePath))
    {
        ms.CopyTo(stream);
    }
}

当 CsvHelper 完成时,MemoryStream 的位置在流的末尾,CopyTo 不会复制任何内容。通过使用ms.Position,您可以将位置移动到流的开头。

【讨论】:

  • 谢谢!它现在可以在没有 BOM 的情况下工作。从 sftp 选择文件的应用程序期望它在 ISO-8859-1 中。我尝试改用拉丁语,效果也很好。你能解释一下拉丁文的编码是如何在没有从 Unicode 进行任何转换的情况下工作的吗,因为 .NET 中的默认编码是 Unicode(订单对象默认为 Unicode)
  • 因为 ISO-8859-1 是 Latin1 的“官方”名称。至于将 Unicode 转换为 Latin1,这就是 StreamWriter 或 StreamReader 中的 Encoding 参数所做的 - 它指定用于将字符串转换为字节的编码类,反之亦然。默认为不带 BOM 的 UTF8,对于非重音英文文本,它与 Latin1 没有区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-27
  • 1970-01-01
  • 2011-07-07
相关资源
最近更新 更多