Попытка заставить libmecab.dll (MeCab) работать с C#

Я пытаюсь использовать японский морфологический анализатор MeCab в программе на C# (Visual Studio 2010 Express, Windows 7), и что-то не так с кодировкой. Если мой ввод (вставлен в текстовое поле) это:

一方, 広義の「ネコ」は, ネコ類 (ネコ科動物) の一部, あるいはその全ての獣を指す包括的名称を指す.

Тогда мой вывод (в другом текстовом поле) выглядит так:

? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ 続, *, *, *, *, *
(©ž, ​​サ変接続,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ 続, *, *, *, *, *) © ©,, ‚¥ ¥ç¶š,*,*,*,*,*? Ae ©ž,ã,μ変 AEZ ¥ç¶š,*,*,*,*,*????????????????????????? Ae ©ž,ã,μ変 AEZ ¥ 続, *, *, *, *, *
EOS

Я предполагаю, что этот текст в какой-то другой кодировке ошибочно принимается за текст в кодировке UTF-8. Но если предположить, что это EUC-JP и использовать Encoding.Convert для преобразования его в UTF-8, это не изменит выходных данных; Предполагая, что это Shift-JIS и делая то же самое, мы получаем разную тарабарщину. Кроме того, хотя он определенно обрабатывает текст - именно так должен форматироваться вывод MeCab - он также не интерпретирует ввод как UTF-8. Если бы это было так, в выходных данных не было бы всех идентичных строк, начинающихся с односимвольных "соединений", которые он явно не может идентифицировать.

Я получаю еще один другой набор тарабарщины, когда пропускаю предложение через командную строку MeCab. Но, опять же, это просто ряд одиночных вопросительных знаков и круглых скобок, идущих вниз по левому краю, поэтому проблема не только в том, что командная строка Windows не поддерживает шрифты с японскими символами; опять же, он просто не читает ввод как UTF-8. (Я установил MeCab в режиме UTF-8.)

Соответствующие части кода выглядят так:

[DllImport ("libmecab.dll", CallingConvention = CallingConvention.Cdecl)]
private extern static IntPtr mecab_new2 (строка аргумента);
[DllImport("libmecab.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.AnsiBStr)]
приватная внешняя статическая строка mecab_sparse_tostr(IntPtr m, строка str);
[DllImport("libmecab.dll", CallingConvention = CallingConvention.Cdecl)]
приватный внешний статический void mecab_destroy(IntPtr m);

приватная строка meCabParse(строка jpnText)
{
    IntPtr mecab = mecab_new2("");
    строка parsedText = mecab_sparse_tostr(mecab, jpnText);

    mecab_destroy(MeCab);
    вернуть parsedText;
}

(С точки зрения того, чтобы поиграть с правдоподобно выглядящими вещами, чтобы увидеть, если они имеют значение, я попытался переключить "UnmanagedType.AnsiBStr" на "UnmanagedType.BStr", который выдает ошибку "AccessViolationException ishandled", и добавив "CharSet=CharSet.Unicode"для параметров DllImport, которые превратили вывод в просто"EOS".)

Вот как я делал преобразование:

// 65001 = кодовая страница UTF-8, 20932 = кодовая страница EUC-JP
приватная строка convertEncoding(строка sourceString, int sourceCodepage, int targetCodepage)
{
    Encoding sourceEncoding = Encoding.GetEncoding(sourceCodepage); 
    Encoding 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)];

    // массив символов в строку в кодировке targt
    targetEncoding.GetChars(targetBytes, 0, targetBytes.Length, targetChars, 0);
    string targetString = новая строка (targetChars);

    return targetString;
}

приватная строка meCabParse(строка jpnText)
{
    // преобразовать текст из строки из UTF-8 в EUC-JP
    jpnText = convertEncoding(jpnText, 65001, 20932);

    IntPtr mecab = mecab_new2("");
    строка parsedText = mecab_sparse_tostr(mecab, jpnText);

    // и конвертировать обратно в UTF-8
    parsedText = convertEncoding(parsedText, 20932, 65001);

    mecab_destroy(MeCab);
}

Предложения / насмешки?

1 ответ

Решение

Я наткнулся на эту тему в поисках способа сделать то же самое. Я использовал ваш код в качестве отправной точки и этот пост в блоге, чтобы выяснить, как упорядочить строки 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;
    }
}
Другие вопросы по тегам