почему mbstowcs возвращает недопустимый многобайтовый символ

"קמ"ד חיר!" — это копия входной строки, вставленная из печати переменной в gdb. Вызов mbstowcs возвращает -1 с другим вводом как NULL. Любые идеи о том, что не так/как это исправить?

"\327\247\327\236"\327\223 \327\227\327\231\327\250!\000\000\000" — это строка с восьмеричными символами, отличными от ascii.

Язык программы C.


person chacham15    schedule 19.06.2011    source источник
comment
Нам нужен шестнадцатеричный дамп строки, а не скопированный и вставленный текст. Если вы скопируете и вставите строку, она не сохранит двоичные данные.   -  person Dietrich Epp    schedule 19.06.2011
comment
В какой локали ваша программа?   -  person Adam Rosenfield    schedule 19.06.2011
comment
mbstowcs предназначен для взаимодействия с вашей средой выполнения. Если вы получаете строку путем десериализации источника с известной кодировкой, вы должны вместо этого использовать что-то вроде iconv из вашей известной кодировки в WCHAR_T.   -  person Kerrek SB    schedule 01.07.2011


Ответы (2)


Функция mbtowcs не обрабатывает кодировку UTF-8, нет локали, которую вы можете установить для перевода UTF-8 в wchar_t. Поэтому я буду использовать примеры для Windows, но общая идея одинакова для большинства ОС.

В мире многобайтовых наборов символов не может быть одного значения для заданного восьмеричного значения и не может быть одного восьмеричного значения для любого заданного символа. Что означает конкретное восьмеричное значение и как представлен символ (или даже если он может быть представлен), определяется язык.

Когда mbstowcs возвращает ошибку, это в основном говорит вам, что нет расширенного символа, эквивалентного переданному ему многобайтовому символу. Это может означать, что символ UNICODE отсутствует (маловероятно, но не невозможно), или это может означать, что языковой стандарт не определяет символ для данного восьмеричного значения (или последовательности восьмеричных значений в случае многобайтовых символов).

Если вы явно не задали свой язык (позвонив setlocale), то вы получаете локаль в зависимости от конфигурации вашей системы. Чтобы получить текущую локаль, вы можете вызвать _get_current_locale. Как только вы узнаете свою локаль, вы можете выяснить, какой символ (если есть) представляет конкретное восьмеричное значение, а затем вы можете выяснить, каким будет эквивалент UNICODE (если есть).

Один из способов определить проблемный символ — изменить длину, передаваемую в mbstowcs, до тех пор, пока не будет найден единственный символ, вызывающий ошибку. Подход грубой силы может заключаться в том, чтобы начать с length=1 и увеличивать его до тех пор, пока mbstowcs не вернет -1.

Обновление от 25 июля

Из обсуждения комментариев мы обнаружили, что входная строка (скорее всего) закодирована как UTF-8. Хотя первоначальный ответ правильный (насколько это возможно), он не заходит достаточно далеко. В Windows вы не можете создать локаль, которая будет обрабатывать символы, закодированные в UTF-8.

Столкнувшись с UTF-8, вместо вызова mbtowcs мы можем вызвать MultiByteToWideChar с использованием кодовой страницы CP_UTF8, но этот код будет работать только в Windows...

BYTE bytes [] = {0xD7,0x99,0xD7,0x95,0xD7,0x97,0xD7,0x90,0xD7,0x99,0x20,0xD7,0x95,0xD7,0x9B,0xD7,0x98,0xD7,0xA8, 0x00};

int result;

// get length of converted string in characters
result = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, (char *)bytes, 
    sizeof (bytes), NULL, 0);

wchar_t * name = new wchar_t [result];

// convert string
result = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, (char *)bytes, 
    sizeof (bytes), name, result);
person Frank Boyne    schedule 19.06.2011
comment
Строка на самом деле отправляется мне через facebook, а не генерируется локально. Как тогда мне это перевести? - person chacham15; 19.06.2011
comment
Функция mbstowcs не знает (или не заботится), что переводимая строка исходит из Facebook. Все, что он делает, это смотрит на текущую настройку локали и переводит строку, как если бы это была используемая кодировка. Вам нужно выяснить, какую кодовую страницу использует Facebook, а затем установить соответствующий язык. Или, что еще лучше, используйте API Facebook, который предоставляет строки UNICODE вместо многобайтовых строк. Таким образом, нет сомнений в используемой кодировке, и вам не нужно переводить в широкие символы. - person Frank Boyne; 19.06.2011
comment
‹?xml version=1.0 encoding=UTF-8?› ‹fql_query_response xmlns=api.facebook.com/1.0 xmlns:xsi=w3.org/2001/XMLSchema-instance list= True ›‹ ›‹ uid ›100000022063315‹/uid ›‹ Имя ›0xd7 0x99 0xd7 0x95 0xd7 0x97 0xd7 0x90 0xd7 0x99 0x20 0xd7 0x95 0xd7 0x9b 0xd7 0x98 0x20 0xsd7 0x95 0xd7 0x9b 0xd7 0x908 0x08 0x. ответ из фейсбука. Каким-то образом IE/Firefox правильно отображает данные. Здесь где-то указана кодовая страница? - person chacham15; 24.06.2011
comment
Похоже, что содержимое ‹name› может быть закодировано в UTF-8. Вы ожидаете, что имя будет יוחאי וכטר - person Frank Boyne; 25.06.2011
comment
да, но разве ты не говорил, что мне нужна локаль? - person chacham15; 25.06.2011
comment
Да, я это сделал :-( Проблема в том, что я объяснил, как работает mbtowcs, и просто предположил, что mbtowcs была правильной функцией для вызова. Функция mbtowcs не обрабатывает кодировку UTF-8, нет локали, которую вы могли бы установить для нее переведите UTF-8 в wchar_t Я попытаюсь обновить свой ответ каким-нибудь полезным способом. - person Frank Boyne; 26.06.2011

Бьюсь об заклад, это сработает, если вы установите UTF-8 следующим образом:

setlocale(LC_CTYPE, "UTF-8");
person Walter    schedule 05.08.2012
comment
Везде, кроме Windows, да. msdn.microsoft.com/en-CA/library /x99tb11d(v=vs.80).aspx Если вы укажете кодовую страницу, такую ​​как UTF-7 или UTF-8, setlocale завершится ошибкой и вернет NULL. - person Justin; 02.05.2013