Как мне правильно использовать CharNext в Windows API?

StackOverflow https://stackoverflow.com/questions/1235798

  •  22-07-2019
  •  | 
  •  

Вопрос

У меня есть многобайтовая строка, содержащая смесь японских и латинских символов.Я пытаюсь скопировать части этой строки в отдельную ячейку памяти.Поскольку это многобайтовая строка, некоторые символы используют один байт, а другие символы - два.При копировании частей строки я не должен копировать японские символы "наполовину".Чтобы иметь возможность сделать это правильно, мне нужно иметь возможность определять, где в многобайтовой строке начинаются и заканчиваются символы.

Например, если строка содержит 3 символа, для которых требуется [2 байта] [2 байта] [1 байт], я должен скопировать либо 2, либо 4, либо 5 байт в другое место, а не 3, поскольку, если бы я копировал 3, я бы скопировал только половину второго символа.

Чтобы выяснить, где в многобайтовой строке начинаются и заканчиваются символы, я пытаюсь использовать функции Windows API CharNext и CharNextExA, но безуспешно.Когда я использую эти функции, они перемещаются по моей строке по одному байту за раз, а не по одному символу за раз.Согласно MSDN, CharNext должен Функция CharNext извлекает указатель на следующий символ в строке..

Вот некоторый код, иллюстрирующий эту проблему:

#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <string.h>

/* string consisting of six "asian" characters */
wchar_t wcsString[] = L"\u9580\u961c\u9640\u963f\u963b\u9644";

int main() 
{
   // Convert the asian string from wide char to multi-byte.
   LPSTR mbString = new char[1000];
   WideCharToMultiByte( CP_UTF8, 0, wcsString, -1, mbString, 100,  NULL, NULL);

   // Count the number of characters in the string.
   int characterCount = 0;
   LPSTR currentCharacter = mbString;
   while (*currentCharacter)
   {
      characterCount++;

     currentCharacter = CharNextExA(CP_UTF8, currentCharacter, 0);
   }
}

(пожалуйста, игнорируйте утечку памяти и неспособность выполнить проверку на ошибки.)

Теперь, в приведенном выше примере, я бы ожидал, что characterCount станет 6, поскольку это количество символов в азиатской строке.Но вместо этого characterCount становится 18, потому что mbString содержит 18 символов:

門阜陀阿阻附

Я не понимаю, как это должно работать.Как CharNext должен знать, является ли "é–€ é" в строке закодированной версией японского символа или на самом деле символами é – € и é?

Некоторые заметки:

  • Я прочитал сообщение в блоге Джоэлса о том, что каждый разработчик должен знать о Unicode.Хотя, возможно, я что-то в нем неправильно понял.
  • Если бы все, что я хотел сделать, это посчитать символы, я мог бы посчитать символы в азиатской строке напрямую.Имейте в виду, что моя настоящая цель - скопировать части многобайтовой строки в отдельное место.Отдельное расположение поддерживает только многобайтовый формат, а не widechar.
  • Если я преобразовываю содержимое mbString обратно в wide char с помощью MultiByteToWideChar, я получаю правильную строку (門阜陀阿阻附), которая указывает, что с mbString все в порядке.

Редактировать:Очевидно, что функции CharNext не поддерживают UTF-8, но Microsoft забыла задокументировать это.Я скинул / скопировал вместе свою собственную процедуру, которую я не буду использовать и которая нуждается в улучшении.Я предполагаю, что его легко разбить.

  LPSTR CharMoveNext(LPSTR szString)
  {
     if (szString == 0 || *szString == 0)
        return 0;

     if ( (szString[0] & 0x80) == 0x00)
        return szString + 1;
     else if ( (szString[0] & 0xE0) == 0xC0)
        return szString + 2;
     else if ( (szString[0] & 0xF0) == 0xE0)
        return szString + 3;
     else if ( (szString[0] & 0xF8) == 0xF0)
        return szString + 4;
     else
        return szString +1;
  }
Это было полезно?

Решение

Вот действительно хорошее объяснение того, что происходит здесь, в Разбираемся во всем этом блог: CharNextExA сломан?.Короче говоря, CharNext не предназначен для работы со строками UTF8.

Другие советы

Насколько я могу определить (google и эксперименты), CharNextExA на самом деле не работает с UTF-8, поддерживаются только многобайтовые кодировки, которые используют более короткие пары байт начала / окончания или однобайтовые символы.

UTF-8 - довольно обычная кодировка, существует множество библиотек, которые будут делать то, что вы хотите, но также довольно легко создать свою собственную.

Взгляните сюда unicode.org, в частности, таблица 3-7 для допустимых форм последовательности.

const char* NextUtf8( const char* in )
{
    if( in == NULL || *in == '\0' )
        return in;

    unsigned char uc = static_cast<unsigned char>(*in);

    if( uc < 0x80 )
    {
        return in + 1;
    }
    else if( uc < 0xc2 )
    {
         // throw error? invalid lead byte
    }
    else if( uc < 0xe0 )
    {
        // check in[1] for validity( 0x80 .. 0xBF )
        return in + 2;
    }
    else if( uc < 0xe1 )
    {
        // check in[1] for validity( 0xA0 .. 0xBF )
        // check in[2] for validity( 0x80 .. 0xBF )
        return in + 3;
    }
    else // ... etc.
    // ...
}

Учитывая , что CharNextExA не работает с UTF-8, вы можете разобрать это сами.Просто пропустите символы, у которых есть 10 в двух верхних битах.Вы можете увидеть шаблон в определении UTF-8: http://en.wikipedia.org/wiki/Utf-8

LPSTR CharMoveNext(LPSTR szString)
{
    ++szString;
    while ((*szString & 0xc0) == 0x80)
        ++szString;
    return szString;
}

Это не прямой ответ на ваш вопрос, но вы можете найти следующий урок полезным, я, безусловно, сделал это.На самом деле представленной здесь информации достаточно, чтобы вы могли с легкостью самостоятельно просматривать многобайтовую строку:

Полное руководство по строкам

Попробуйте использовать 932 для кодовой страницы.Я не думаю, что CP_UTF8 - это настоящая кодовая страница, и она может работать только для WideCharToMultiByte() и обратно.Вы также можете попробовать isleadByte() , но для этого требуется либо правильно установить языковой стандарт, либо правильно установить кодовую страницу по умолчанию.Я успешно использовал IsDBCSLeadByteEx(), но никогда с CP_UTF8.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top