如何在C#中使用UTF8编码和BOM获取GetBytes()?

65

我在使用C#的asp.net mvc 2应用程序时遇到了UTF8编码问题。我正在尝试让用户从字符串下载一个简单的文本文件,想要通过下面这行代码获取字节数组:

var x = Encoding.UTF8.GetBytes(csvString);

但是当我使用以下代码将其返回进行下载时:

return File(x, ..., ...);

我得到的文件没有BOM,所以克罗地亚语字符无法正确显示。这是因为我的字节数组在编码后不包含BOM。我尝试手动插入这些字节,然后它就能正确显示,但这不是最好的做法。

我还试过创建UTF8Encoding类实例,并将布尔值(true)传递给其构造函数来包括BOM,但也不起作用。

有没有解决方案?谢谢!

4个回答

160

试试这样:

public ActionResult Download()
{
    var data = Encoding.UTF8.GetBytes("some data");
    var result = Encoding.UTF8.GetPreamble().Concat(data).ToArray();
    return File(result, "application/csv", "foo.csv");
}

原因在于带有布尔参数的UTF8Encoding构造函数并不像你预期的那样工作:

byte[] bytes = new UTF8Encoding(true).GetBytes("a");

结果数组将包含一个值为97的单个字节。没有BOM是因为UTF8不需要BOM。


4
谢谢!我的特殊字符在Excel CSV中无法正常工作,我已经快疯了 :) - Hannes Sachsenhofer
4
为了更加清晰明了,Encoding.UTF8 等同于 new UTF8Encoding(true)。该参数控制 GetPreamble() 是否会输出 BOM。 - user247702
9
жІЎжңүBOMжҳҜеӣ дёәGetBytesдёҚиғҪеҒҮи®ҫжҲ‘们жӯЈеңЁеҶҷе…Ҙж–Ү件гҖӮи°ҒеҶҷе…Ҙж–Ү件еә”иҜҘйҰ–е…ҲиҝӣиЎҢеүҚеҜјж“ҚдҪңпјҲдҫӢеҰӮеғҸStreamWriterдёҖж ·пјүгҖӮ - Dave Van den Eynde
2
为什么内容类型设置为“application/csv”,而不是“text/csv”(如此处所示:http://www.freeformatter.com/mime-types-list.html)?无论如何,这两种方式都不起作用。Excel仍然以无法识别的字符打开它。 - Veverke
1
如果我使用 application/csv 的 contentType,它可以正常工作,但是如果我将其替换为 text/csv,它就停止工作了,也许有人知道为什么? - Ramūnas
显示剩余2条评论

22

我创建了一个简单的扩展程序,用于将任何编码的字符串在写入文件或流时转换为其字节数组表示:

public static class StreamExtensions
{
    public static byte[] ToBytes(this string value, Encoding encoding)
    {
        using (var stream = new MemoryStream())
        using (var sw = new StreamWriter(stream, encoding))
        {
            sw.Write(value);
            sw.Flush();
            return stream.ToArray();
        }
    }
}

使用:

stringValue.ToBytes(Encoding.UTF8)

这也适用于其他编码,比如需要BOM的UTF-16。


1
这实际上是一个非常有用的解决方法。使用带编码的 StreamWriter 解决了我的即时问题,并允许我的文件在 Excel 2013 中打开。 - iCollect.it Ltd
谢谢。这帮助我保存了包含阿拉伯字符的 .csv 文件。使用 Encoding.GetBytes 返回了一个坏文件,其中包含未知字符。 - Markomar

2
UTF-8 不需要 BOM,因为它是由 1 字节的单词序列组成的。UTF-8 = UTF-8BE = UTF-8LE。
相比之下,UTF-16 需要在流的开头添加 BOM 来确定剩余部分是 UTF-16BE 还是 UTF-16LE,因为 UTF-16 是由 2 字节的单词序列组成的,而 BOM 可以确定单词中的字节是 BE 还是 LE。
问题不在于 Encoding.UTF8 类,而在于您所使用的查看文件的程序。

1
UTF-8是一种变宽度编码。它只需要1个字节来编码ASCII字符,但其他编码点将使用多个字节。 - Joel Fillmore
2
使用多个字节编码的代码点具有预定义的顺序(基于U+大端表示)。但是,由于UTF8表示为字节流(而不是作为字或双字的字节序列本身表示),因此字节序的概念不适用。字节序适用于将16位、32位、64位、128位整数表示为字节,而不适用于将代码点表示为字节。 - yfeldblum
抱歉,我以为你用“1字节单词序列”这个短语指的是代码点的存储。感谢澄清。给你的答案和评论点赞。 - Joel Fillmore
1
一些程序使用BOM来检测编码是否为UTF-8。不需要它的程序应该忽略它,因为发出的字符本来就是要被忽略的。老的程序无法处理BOM。 - Dave Van den Eynde
如果你想在Visual Studio中打开一个包含代理对的UTF-8文件,那么它是可以做到的。 - marc hoffman
@yfeldblum 对不起,虽然我同意您对某些程序缺乏编码识别的看法,但当错误的程序像Excel 2016打开CSV文件这样广泛时,像Hovhannes Hakobyan或Darin Dimitrov这样的答案比您的答案更有帮助。 - AFract

-2

请记住,.NET字符串在内存中都是Unicode编码的,因此如果您可以通过调试器正确地查看csvString,则问题可能出在文件写入上。

我认为您应该返回一个与文件相同编码的FileResult。尝试设置返回文件的编码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接