Pregunta

Tengo una cadena de varios bytes que contiene una mezcla de caracteres japoneses y latinos. Estoy tratando de copiar partes de esta cadena en una ubicación de memoria separada. Como se trata de una cadena de varios bytes, algunos de los caracteres usan un byte y otros caracteres usan dos. Al copiar partes de la cadena, no debo copiar '' la mitad '' caracteres japoneses Para poder hacer esto correctamente, necesito poder determinar dónde comienzan y terminan los caracteres de la cadena de varios bytes.

Como ejemplo, si la cadena contiene 3 caracteres que requieren [2 bytes] [2 bytes] [1 byte], debo copiar 2, 4 o 5 bytes a la otra ubicación y no 3, ya que si fuera copiando 3 Copiaría solo la mitad del segundo carácter.

Para averiguar en qué parte de la cadena de caracteres de varios bytes comienza y termina, estoy tratando de usar la función API de Windows CharNext y CharNextExA pero sin suerte. Cuando uso estas funciones, navegan por mi cadena un byte a la vez, en lugar de un carácter a la vez. Según MSDN, se supone que CharNext La función CharNext recupera un puntero al siguiente carácter en una cadena. .

Aquí hay un código para ilustrar este problema:

#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);
   }
}

(ignore la pérdida de memoria y la falta de verificación de errores)

Ahora, en el ejemplo anterior, esperaría que characterCount se convierta en 6, ya que ese es el número de caracteres en la cadena asiática. Pero en cambio, characterCount se convierte en 18 porque mbString contiene 18 caracteres:

門阜陀阿阻附

No entiendo cómo se supone que funciona. ¿Cómo se supone que CharNext debe saber si " & # 233; & # 8211; & # 8364; & # 233; " en la cadena hay una versión codificada de un carácter japonés, o de hecho los caracteres & # 233; & # 8211; & # 8364; y & # 233 ;?

Algunas notas:

  • He leído la publicación del blog de Joels sobre lo que todo desarrollador necesita saber sobre Unicode. Sin embargo, es posible que haya entendido mal algo en él.
  • Si todo lo que quisiera hacer fuera contar los caracteres, podría contarlos directamente en la cadena asiática. Tenga en cuenta que mi objetivo real es copiar partes de la cadena de varios bytes en una ubicación separada. La ubicación separada solo admite múltiples bytes, no widechar.
  • Si convierto el contenido de mbString de nuevo a ancho char usando MultiByteToWideChar, obtengo la cadena correcta (& # 38272; & # 38428; & # 38464; & # 38463; & # 38459; & # 38468;), que indica que no hay nada malo con mbString.

EDITAR: Aparentemente, las funciones CharNext no son compatibles con UTF-8, pero Microsoft olvidó documentar eso. Lancé / copié pegué juntos mi propia rutina, que no usaré y que necesita mejorar. Supongo que es fácilmente bloqueable.

  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;
  }
¿Fue útil?

Solución

Aquí hay una muy buena explicación de lo que está sucediendo aquí en el Ordenarlo todo blog : ¿CharNextExA está roto? . En resumen, CharNext no está diseñado para funcionar con cadenas UTF8.

Otros consejos

Hasta donde puedo determinar (google y experimentación), CharNextExA en realidad no funciona con UTF-8, solo admite codificaciones multibyte que usan pares de bytes de plomo / trail más cortos o caracteres de un solo byte.

UTF-8 es una codificación bastante regular, hay muchas bibliotecas que harán lo que quieras, pero también es bastante fácil de crear la tuya propia.

Eche un vistazo aquí unicode.org , en particular la tabla 3-7 para formularios de secuencia válidos.

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.
    // ...
}

Dado que CharNextExA doesn ' No funciona con UTF-8 , puede analizarlo usted mismo. Simplemente omita los caracteres que tienen 10 en los dos bits superiores. Puede ver el patrón en la definición de UTF-8: http://en.wikipedia.org / wiki / Utf-8

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

Esta no es una respuesta directa a su pregunta, pero puede encontrar útil el siguiente tutorial, ciertamente lo hice. De hecho, la información proporcionada aquí es suficiente para que pueda atravesar la cadena de múltiples bytes usted mismo con facilidad:

Tutorial completo de cadenas

Intente usar 932 para la página de códigos. No creo que CP_UTF8 sea una página de códigos real, y puede que solo funcione para WideCharToMultibyte () y viceversa. También puede probar isleadByte (), pero eso requiere establecer la configuración regional correctamente o establecer la página de códigos predeterminada correctamente. He utilizado con éxito IsDBCSLeadByteEx (), pero nunca con CP_UTF8.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top