2

我正在尝试在 C# 程序(Visual Studio 2010 Express,Windows 7)中使用日语形态分析器MeCab,但编码出现问题。如果我的输入(粘贴到文本框中)是这样的:

一方、広义の动物ネコは、ネコ类(ネコ科)の部分、あるいはその全ての獣を指す包括名字を指す。</pre>

然后我的输出(在另一个文本框中)如下所示:

? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
( åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
) åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
??????????????????????????? åè©ž,ã,µå¤‰æŽ¥ç¶š,*,*,*,*,*
EOS

我猜那是一些其他编码的文本被误认为是 UTF-8 编码的文本。但是假设它是 EUC-JP 并使用 Encoding.Convert 将其转换为 UTF-8 并不会改变输出;假设它是 Shift-JIS 并且做同样的事情会产生不同的乱码。此外,虽然它确实在处理文本 - 这就是 MeCab 输出应该被格式化的方式 - 它似乎也没有将输入解释为 UTF-8。如果这样做,输出中就不会出现所有以单字符“复合”开头的相同行,而这显然无法识别。

当我通过 MeCab 的命令行运行这句话时,我又得到了另一组看起来不同的乱码。但是,再一次,它只是左侧的一排问号和括号,所以这不仅仅是 Windows 命令行不支持带有日文字符的字体的问题。再次,它只是没有以 UTF-8 格式读取输入。(我确实以 UTF-8 模式安装了 MeCab。)

代码的相关部分如下所示:

[DllImport("libmecab.dll", CallingConvention = CallingConvention.Cdecl)]
私有外部静态 IntPtr mecab_new2(字符串 arg);
[DllImport("libmecab.dll", CallingConvention = CallingConvention.Cdecl)]
[返回:MarshalAs(UnmanagedType.AnsiBStr)]
私有外部静态字符串 mecab_sparse_tostr(IntPtr m, string str);
[DllImport("libmecab.dll", CallingConvention = CallingConvention.Cdecl)]
私人外部静态无效mecab_destroy(IntPtr m);

私有字符串 meCabParse(字符串 jpnText)
{
    IntPtr mecab = mecab_new2("");
    string parsedText = mecab_sparse_tostr(mecab, jpnText);

    mecab_destroy(mecab);
    返回解析文本;
}

(在摆弄看似合理的东西以查看它们是否有所作为方面,我尝试将“UnmanagedType.AnsiBStr”切换为“UnmanagedType.BStr”,这会给出错误“AccessViolationException was unhandled”,并添加“CharSet= CharSet.Unicode" 到 DllImport 参数,它把输出变成了 "EOS"。)

这就是我进行转换的方式:

// 65001 = UTF-8 代码页,20932 = EUC-JP 代码页
私有字符串转换编码(字符串 sourceString,int sourceCodepage,int targetCodepage)
{
    编码 sourceEncoding = Encoding.GetEncoding(sourceCodepage);
    编码 targetEncoding = Encoding.GetEncoding(targetCodepage);

    // 将源字符串转换为字节数组
    byte[] sourceBytes = sourceEncoding.GetBytes(sourceString);

    // 将这些字节转换为目标编码
    byte[] targetBytes = Encoding.Convert(sourceEncoding, targetEncoding, sourceBytes);

    // 字节数组转字符数组
    char[] targetChars = new char[targetEncoding.GetCharCount(targetBytes, 0, targetBytes.Length)];

    //char数组到目标编码的字符串
    targetEncoding.GetChars(targetBytes, 0, targetBytes.Length, targetChars, 0);
    字符串目标字符串 = 新字符串(目标字符);

    返回目标字符串;
}

私有字符串 meCabParse(字符串 jpnText)
{
    // 将字符串中的文本从 UTF-8 转换为 EUC-JP
    jpnText = convertEncoding(jpnText, 65001, 20932);

    IntPtr mecab = mecab_new2("");
    string parsedText = mecab_sparse_tostr(mecab, jpnText);

    // annnd 转换回 UTF-8
    parsedText = convertEncoding(parsedText, 20932, 65001);

    mecab_destroy(mecab);
}

建议/嘲讽?

4

1 回答 1

2

我遇到了这个线程,正在寻找一种方法来做同样的事情。我使用您的代码作为起点,这篇博文用于弄清楚如何编组 UTF8 字符串。

以下代码为我提供了正确编码的输出:

public class Mecab
{
    [DllImport("libmecab.dll", CallingConvention = CallingConvention.Cdecl, CharSet=CharSet.Unicode)]
    private extern static IntPtr mecab_new2(string arg);
    [DllImport("libmecab.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    private extern static IntPtr mecab_sparse_tostr(IntPtr m, byte[] str);
    [DllImport("libmecab.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    private extern static void mecab_destroy(IntPtr m);

    public static String Parse(String input)
    {
        IntPtr mecab = mecab_new2("");
        IntPtr nativeStr = mecab_sparse_tostr(mecab, Encoding.UTF8.GetBytes(input));
        int size = nativeArraySize(nativeStr) - 1;
        byte[] data = new byte[size];
        Marshal.Copy(nativeStr, data, 0, size);

        mecab_destroy(mecab);

        return Encoding.UTF8.GetString(data);
    }

    private static int nativeArraySize(IntPtr ptr)
    {
        int size = 0;
        while (Marshal.ReadByte(ptr, size) > 0)
            size++;

        return size;
    }
}
于 2011-06-20T13:35:05.293 回答