【问题标题】:"É" not getting converted to two bytes correctly“É”未正确转换为两个字节
【发布时间】:2011-02-10 14:33:32
【问题描述】:

关于this question,我还有一个补充问题。

我发现一首标题中带有“É”的曲目。

我的代码:

var playList = new StreamWriter(playlist, false, Encoding.UTF8);

-

private static void WriteUTF8(StreamWriter playList, string output)
{
    byte[] byteArray = Encoding.UTF8.GetBytes(output);
    foreach (byte b in byteArray)
    {
        playList.Write(Convert.ToChar(b));
    }
}

将其转换为以下字节:

195
137

输出为 Ã 后跟一个正方形(这是一个无法以当前字体打印的字符)。

我已将同一个文件导出到 Media Monkey 中的播放列表中,它会将“É”写为“É”——我认为这是正确的(正如 KennyTM 指出的那样)。

我的问题是,如何获得“‰”符号输出?我需要选择不同的字体吗?如果需要,选择哪一种?

更新

人们似乎没有抓住重点。

我可以使用

将“É”写入文件
playList.WriteLine("É");

这不是问题。

问题在于 Media Monkey 要求文件格式如下:

#EXTINFUTF8:140,Yann Tiersen - Comptine D'Un Autre Été: L'Après Midi
#EXTINF:140,Yann Tiersen - Comptine D'Un Autre Été: L'Après Midi
#UTF8:04-Comptine D'Un Autre Été- L'Après Midi.mp3
04-Comptine D'Un Autre Été- L'Après Midi.mp3

所有“high-ascii”(缺少更好的术语)都写成一对字符。

更新 2

我应该将c9 替换为c3 89

我打算把我实际得到的东西,但在为此进行测试时,我设法得到一个测试程序,以“按原样”正确格式输出文本。所以我需要做更多的调查。

【问题讨论】:

  • 在 Windows-1252 中为 137 (0x89)。
  • @KennyTM - 我假设 ‰ 是 137。我想知道如何获得该字符输出。我要改字体吗?
  • 这是 É 的正确字节(拉丁文大写字母 E 带尖音 0xc3 89),问题一定出在您的显示代码中 - 这是如何完成的?
  • @Scott - 我知道“É”是“É”的正确字节。问题是我没有正确显示第二个字节,无论是在调试器输出窗口、Media Monkey 还是 Notepad++ 中,其他所有转换都正确完成。
  • @ChrisF:您需要告诉不同的应用程序将文件解码为 UTF-8。

标签: c# unicode fonts


【解决方案1】:

好的,首先感谢大家的帮助和耐心。

我终于让它正常工作了。我已经实现了 bobince 解决方案的一个版本,这就是他获得接受的原因(向其他所有人投票)。这是我的代码:

var playList = new StreamWriter(playlist, false, Encoding.Default);
playList.WriteLine("#EXTM3U");

foreach (string track in tracks)
{
    // Read ID3 tags from file
    var info = new FileProperties(track);

    // Write extended info (#EXTINF:<time>,<artist> - <title>
    if (Encoding.UTF8.GetBytes(info.Artist).Length != info.Artist.Length ||
        Encoding.UTF8.GetBytes(info.Title).Length != info.Title.Length)
    {
        playList.Close();
        playList = new StreamWriter(playlist, true, Encoding.UTF8);

        playList.WriteLine(string.Format("#EXTINFUTF8:{0},{1} - {2}",
                           info.Duration, info.Artist, info.Title));

        playList.Close();
        playList = new StreamWriter(playlist, true, Encoding.Default);
    }

    playList.WriteLine(string.Format("#EXTINF:{0},{1} - {2}",
                       info.Duration, info.Artist, info.Title));

    // Write the name of the file (removing the drive letter)
    string file = Path.GetFileName(track);
    if (Encoding.UTF8.GetBytes(file).Length != file.Length)
    {
        playList.Close();
        playList = new StreamWriter(playlist, true, Encoding.UTF8);

        playList.WriteLine(string.Format("#UTF8:{0}", file));

        playList.Close();
        playList = new StreamWriter(playlist, true, Encoding.Default);
    }

    playList.WriteLine(file);
}

playList.Close();

如您所见,我假设我不必编写 UTF8,但当我这样做时,我会关闭流并使用 UTF8 编码重新打开它。然后,我在写下违规行后,使用默认编码关闭并重新打开它。

现在我不知道为什么我之前的代码给出了不一致的结果。鉴于每个人(尤其是 Jon)都说它应该一直失败,或者可能一直都在工作。

【讨论】:

    【解决方案2】:

    像这样使用Convert.ToChar 几乎可以肯定是个坏主意。您基本上是在对事物进行两次编码。

    您应该或者自己执行转换,然后直接写入流,或者您应该让StreamWriter 进行转换。如果您尝试自己执行转换,为什么还要使用 StreamWriter

    您是要写入二进制文件还是简单的文本文件?如果它是一个简单的文本文件,只需使用 StreamWriter 并让它进行转换。如果是二进制文件,请使用Stream 而不是StreamWriter,并在需要的地方直接执行文本编码,然后将字节直接写入流。

    编辑:这是您的原始代码发生的情况:

    Encoding.UTF8.GetBytes(text) => byte[] { 0xc3, 0x89 };
    
    Convert.ToChar(0xc3) => char U+00C3
    StreamWriter writes U+00C3 as byte[] { 0xc3, 0x83 };
    
    Convert.ToChar(0x89) => char U+0089
    StreamWriter writes U+00C3 as byte[] { 0xc2, 0x89 };
    

    这就是为什么您将 c3 83 c2 89 写入文件的原因。

    【讨论】:

    • 我正在编写一个简单的文本文件 - 实际上是一个“m3u”文件,它需要将 unicode 字符写为“Ô,后跟第二个字节转换为的任何字符。
    • @ChrisF:你的意思不是很清楚。它是普通的 UTF-8 文件吗?最简单的方法是报告所需的 字节 而不是字符。
    • 认为这是一个普通的 UTF-8 文件。我遇到的所有其他转换都是正确完成的:“é”->“é”、“ó”->“ó”等。只是这个失败了。
    • @ChrisF:如果它只是普通的 UTF-8,那么您根本不需要进行自己的编码。只需将文本写入StreamWriter。但同样,不要将编码视为将一个字符转换为两个字符。它将字符编码为 bytes.
    • 这并不能说明相同的文件在不同的应用程序中显示不同
    【解决方案3】:

    更根本的问题在于方法的名称:

     private static void WriteUTF8(...)
    

    .M3U 文件不是 UTF-8。它们是 Latin-1(或 Windows-1252)。

    您应该使用Encoding.GetEncoding(1252),而不是Encoding.UTF8。然后你可以直接写入流,你不需要任何这种转换怪异。

    更新:

    我刚刚尝试了以下 C# 代码,生成的 .M3U 在 Winamp 和 WMP 中都可以正常打开:

    static void Main(string[] args)
    {
        string fileName = @"C:\Temp\Test.m3u";
        using (StreamWriter writer = new StreamWriter(fileName, false,
            Encoding.GetEncoding(1252)))
        {
            writer.WriteLine("#EXTM3U");
            writer.WriteLine("#EXTINF:140,Yann Tiersen " +
                "- Comptine D'Un Autre Été: L'Après Midi");
            writer.WriteLine("04-Comptine D'Un Autre Été- L'Après Midi.mp3");
        }
    }
    

    所以,正如我所说 - 只需使用正确的编码开始。您不需要所有这些额外的 #EXTINFUTF8#UTF8 行,除非它是 Media Monkey 的一些奇怪要求(它绝对不是基本 M3U 规范的一部分)。

    【讨论】:

    • 这没有帮助 - 我得到“Ã?”而不是 "É" 用于分割字符时。
    • @ChrisF:您是用将编码字节直接写入流的东西替换了整个块,还是只将UTF8 更改为1252 并使用相同的Convert 循环?
    • 我都试过了。我现在完全错了,所以我完全不知所措。
    • @ChrisF:我想我不能麻烦您发布创建StreamStreamWriter 的代码吗?我想自己测试一下,我仍然相当确定这是一个编码问题(尽管它可能看起来是别的东西)。 Winamp(或任何其他媒体播放器)或仅 Media Monkey 中是否也会出现此问题?
    • @Aaronaught - 创建StreamWriter 的代码已经在问题中。我最近才重建我的电脑,所以我只安装了 Media Monkey 和 Windows Media Player。我会在 WMP 中尝试一下。
    【解决方案4】:

    StreamWriter 已经将您发送给它的字符转换为 UTF-8 — 这就是它的全部目的。把WriteUTF8扔掉;它已损坏且无用。

    (WriteUTF8 正在获取字符,将它们转换为 UTF-8 字节,将每个单个字节转换为它在当前代码页中映射到的字符,然后将每个 那些 字符编码为 UTF -8. 所以在最好的情况下,你有一个双重 UTF-8 编码的字符串;在最坏的情况下,你已经完全丢失了未映射到系统代码页曲目中的字节;对 DBCS 代码页尤其不利。)

    您在使用 Media Monkey 时遇到的问题可能只是它根本不支持 UTF-8 或 Unicode 文件名。尝试让它播放(并导出播放列表)包含不适合您系统代码页的字符的文件,例如将文件重命名为 αβγ.mp3

    编辑:

    #EXTINFUTF8:140,Yann Tiersen - Comptine D'Un Autre Été: L'Après Midi
    #EXTINF:140,Yann Tiersen - Comptine D'Un Autre Été: L'Après Midi
    #UTF8:04-Comptine D'Un Autre Été- L'Après Midi.mp3
    04-Comptine D'Un Autre Été- L'Après Midi.mp3
    

    好的,你在同一个文件中得到了混合编码:难怪文本编辑器在打开它时会遇到问题。未注释的行和#EXTINF 行位于系统默认代码页中,用于支持无法读取 Unicode 文件名的媒体播放器。对于不知道#UTF8(以及#EXTINFUTF8的描述)行的任何内容,系统代码页中不存在的任何文件名字符(例如上面的希腊语,在西方Windows安装上)都将被破坏和无法播放.

    因此,如果这是您的目标格式,您将需要获取两种编码并依次使用每种编码,例如:

    private static void writePlaylistEntry(Stream playlist, string filename, int length) {
        Encoding utf8= new UTF8Encoding(false);
        Encoding ansi= Encoding.Default;
        playlist.Write(utf8.GetBytes("#EXTINFUTF8:"+length+","+filename+"\n"));
        playlist.Write(ansi.GetBytes("#EXTINF:"+length+","+filename+"\n"));
        playlist.Write(utf8.GetBytes("#UTF8:"+filename+"\n"));
        playlist.Write(ansi.GetBytes(filename+"\n"));
    }
    

    【讨论】:

    • @bobince - 它确实间接支持 UTF-8 文件名。它写出以#UTF8: 开头的文件名,并将任何 UTF8 字符分成两部分。
    • 我使用什么编码来打开流?
    • 您不使用编码。它是字节流而不是字符 StreamWriter。您不能使用单个 StreamWriter,因为事实证明该文件不只使用一种编码,它会随每一行发生变化。
    【解决方案5】:

    我不使用 C#,但症状告诉我,您确实将其编写为 UTF-8,但输出/控制台/应用程序/无论您使用哪个查看书面输出不是使用 UTF-8,而是使用 ISO-8859-1 来显示它们,并且 MediaMonkey 使用 CP1252 来显示它们。

    如果您在 IDE 控制台中查看它们,则需要将 IDE 配置为使用 UTF-8 作为控制台和文本文件编码。

    更新您显然想将UTF-8 数据写入CP-1252。现在问题/问题更清楚了。同样,我不使用 C#,但 Java 等价物是:

    Writer writer = new OutputStreamWriter(new FileOutputStream("file.ext"), "CP-1252");
    writer.write(someUTF8String); // Will be written as CP-1252. "É" would become "É"
    

    希望这能提供一些见解。

    【讨论】:

    • 这是我的第一个想法,但 Media Monkey 生成的“m3u”文件在 Notepad++ 中正确显示“‰”,与我用来查看我生成的文件的应用程序相同。
    • 您是否指示 Notepad++ 将文件保存/编码为 UTF-8?检查顶部栏中的Format 菜单。
    • 啊 - 检查“编码”菜单显示正确的文件以 ANSI not UTF8 编码。很有趣。
    • 这意味着文件未使用 UTF-8 BOM 保存和/或 Notepad++ 不使用 UTF-8 作为 默认 编码和/或 Notepad++ 无法自动- 猜测实际编码。
    • 我需要“按原样”写一些行,并转换一些行。
    猜你喜欢
    • 1970-01-01
    • 2018-05-07
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 1970-01-01
    • 2014-11-30
    • 2017-01-27
    相关资源
    最近更新 更多