Попытка заставить 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;
}
}