“É”未正确转换为两个字节

时间:2010-04-21 13:12:16

标签: c# unicode fonts

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

所有“高ascii”(为了更好的术语)都写成一对字符。

更新2

我应该将c9替换为c3 89

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

5 个答案:

答案 0 :(得分:3)

这样使用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写入文件。

答案 1 :(得分:2)

我不做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 "É"

希望这能提供一些见解。

答案 2 :(得分:2)

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文件名的媒体播放器。系统代码页中不存在的任何文件名字符(例如,上面的希腊语,在西方Windows上安装)将被修改,并且对于任何不了解#UTF8(和#EXTINFUTF8的任何内容都无法播放线条。

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

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"));
}

答案 3 :(得分:2)

方法名称中更基本的问题是:

 private static void WriteUTF8(...)

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

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

<强>更新

我刚尝试了以下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规范的一部分)。

答案 4 :(得分:0)

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

我终于让它正常工作了。我已经实现了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编码重新打开它。然后,在写完违规行之后,关闭并使用默认编码重新打开它。

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