代码之家  ›  专栏  ›  技术社区  ›  ChrisF

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

  •  1
  • ChrisF  · 技术社区  · 15 年前

    进一步 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
    

    输出为_,后跟一个正方形(不能打印的字符 当前字体 )

    我已经将同一个文件导出到媒体猴子的播放列表中,它将“_”写为“__”—我假设这是正确的(如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 回复  |  直到 15 年前
        1
  •  2
  •   bobince    15 年前

    StreamWriter 已经将发送的字符转换为utf-8___,这就是它的全部用途。投掷 WriteUTF8 走开,它坏了,没用。

    ( 写入器8 正在获取字符,将其转换为UTF-8字节,将每个字节转换为当前代码页中映射到的字符,然后对每个 那些 以UTF-8格式显示的字符。因此,在最好的情况下,您有一个双UTF-8编码的字符串;在最坏的情况下,您完全丢失了系统代码页库中未映射的字节;尤其是对DBCS代码页不利。)

    媒体猴子的问题可能是它根本不支持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"));
    }
    
        2
  •  3
  •   Jon Skeet    15 年前

    使用 Convert.ToChar 这几乎肯定是个坏主意。你基本上是两次编码。

    你应该 任何一个 自己执行转换,然后直接写入流, 你应该让 StreamWriter 进行转换。你为什么用 流写器 如果你想自己进行转换的话?

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

    编辑:以下是原始代码的情况:

    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写到文件里。

        3
  •  2
  •   BalusC    15 年前

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

    希望这能提供一些见解。

        4
  •  2
  •   Aaronaught    15 年前

    更根本的问题是以方法的名义:

     private static void WriteUTF8(...)
    

    .M3U文件不是UTF-8。它们是拉丁语-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 行,除非这是媒体猴子的一些奇怪的要求(它绝对不是基本的M3U规范的一部分)。

        5
  •  0
  •   ChrisF    15 年前

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

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

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