the requestmapping online know how check charset c utf-8

requestmapping - ¿Cómo detectar UTF-8 en C simple?



how to know if a text file is utf 8 (10)

Estoy buscando un fragmento de código en el antiguo C simple que detecta que la cadena dada está en codificación UTF-8. Conozco la solución con expresiones regulares, pero por diversas razones sería mejor evitar usar cualquier cosa que no sea C en este caso particular.

La solución con expresiones regulares se ve así (advertencia: se omiten varias comprobaciones):

#define UTF8_DETECT_REGEXP "^([/x09/x0A/x0D/x20-/x7E]|[/xC2-/xDF][/x80-/xBF]|/xE0[/xA0-/xBF][/x80-/xBF]|[/xE1-/xEC/xEE/xEF][/x80-/xBF]{2}|/xED[/x80-/x9F][/x80-/xBF]|/xF0[/x90-/xBF][/x80-/xBF]{2}|[/xF1-/xF3][/x80-/xBF]{3}|/xF4[/x80-/x8F][/x80-/xBF]{2})*$" const char *error; int error_off; int rc; int vect[100]; utf8_re = pcre_compile(UTF8_DETECT_REGEXP, PCRE_CASELESS, &error, &error_off, NULL); utf8_pe = pcre_study(utf8_re, 0, &error); rc = pcre_exec(utf8_re, utf8_pe, str, len, 0, 0, vect, sizeof(vect)/sizeof(vect[0])); if (rc > 0) { printf("string is in UTF8/n"); } else { printf("string is not in UTF8/n") }


El siguiente programa lee cadenas utf-8 (ascii, caracteres no ascii como euro, etc ...) de stdin. Cada línea se pasa a func_find_utf8. Como los caracteres utf-8 son caracteres multi-bytes, la función func_find_utf8 comprueba los bits de caracteres para encontrar que el carácter sea ascii o no-ascii. Si el charcter no es ascii, conozca el ancho de bytes. Pase el ancho de bytes y ubíquelo para funcionar print_non_ascii.

#include<stdio.h> #include<string.h> /* UTF-8 : BYTE_BITS*/ /* B0_BYTE : 0XXXXXXX */ /* B1_BYTE : 10XXXXXX */ /* B2_BYTE : 110XXXXX */ /* B3_BYTE : 1110XXXX */ /* B4_BYTE : 11110XXX */ /* B5_BYTE : 111110XX */ /* B6_BYTE : 1111110X */ #define B0_BYTE 0x00 #define B1_BYTE 0x80 #define B2_BYTE 0xC0 #define B3_BYTE 0xE0 #define B4_BYTE 0xF0 #define B5_BYTE 0xF8 #define B6_BYTE 0xFC #define B7_BYTE 0xFE /* Please tune this as per number of lines input */ #define MAX_UTF8_STR 10 /* 600 is used because 6byteX100chars */ #define MAX_UTF8_CHR 600 void func_find_utf8 (char *ptr_to_str); void print_non_ascii (int bytes, char *pbyte); char strbuf[MAX_UTF8_STR][MAX_UTF8_CHR]; int main (int ac, char *av[]) { int i = 0; char no_newln_str[MAX_UTF8_CHR]; i = 0; printf ("/n/nYou can enter utf-8 string or Q/q to QUIT/n/n"); while (i < MAX_UTF8_STR) { fgets (strbuf[i], MAX_UTF8_CHR, stdin); if (!strlen (strbuf[i])) break; if ((strbuf[i][0] == ''Q'') || (strbuf[i][0] == ''q'')) break; strcpy (no_newln_str, strbuf[i]); no_newln_str[strlen (no_newln_str) - 1] = 0; func_find_utf8 (no_newln_str); ++i; } return 1; } void func_find_utf8 (char *ptr_to_str) { int found_non_ascii; char *pbyte; pbyte = ptr_to_str; found_non_ascii = 0; while (*pbyte) { if ((*pbyte & B1_BYTE) == B0_BYTE) { pbyte++; continue; } else { found_non_ascii = 1; if ((*pbyte & B7_BYTE) == B6_BYTE) { print_non_ascii (6, pbyte); pbyte += 6; continue; } if ((*pbyte & B6_BYTE) == B5_BYTE) { print_non_ascii (5, pbyte); pbyte += 5; continue; } if ((*pbyte & B5_BYTE) == B4_BYTE) { print_non_ascii (4, pbyte); pbyte += 4; continue; } if ((*pbyte & B4_BYTE) == B3_BYTE) { print_non_ascii (3, pbyte); pbyte += 3; continue; } if ((*pbyte & B3_BYTE) == B2_BYTE) { print_non_ascii (2, pbyte); pbyte += 2; continue; } } } if (found_non_ascii) printf (" These are Non Ascci chars/n"); } void print_non_ascii (int bytes, char *pbyte) { char store[6]; int i; memset (store, 0, 6); memcpy (store, pbyte, bytes); i = 0; while (i < bytes) printf ("%c", store[i++]); printf ("%c", '' ''); fflush (stdout); }


3 bytes aleatorios parecen tener un 15.8% de probabilidad de ser UTF-8 válido según mi cálculo:

128 ^ 3 posibles secuencias solo en ASCII = 2097152

2 ^ 16-2 ^ 11 caracteres de UTF-8 de 3 bytes posibles (esto está asumiendo que se permiten pares y caracteres no sustituidos) = 63488

1920 caracteres de 2 bytes UTF-8 antes o después de un carácter ASCII = 1920 * 128 * 2 = 524288

Divida por número de secuencias de 3 bytes = (2097152 + 63488 + 491520) /16777216.0 = 0.1580810546875

En mi humilde opinión, esto sobreestima mucho el número de coincidencias incorrectas, porque el archivo tiene solo 3 bytes de longitud. La intersección desciende a medida que aumenta el número de bytes. También el texto real en un UTF-8 no es aleatorio, hay una gran cantidad de bytes solitarios con el conjunto de bits alto, que no es válido UTF-8.

Una métrica más útil para adivinar las probabilidades de falla es la probabilidad de que una secuencia de bytes con el bit alto establecido sea UTF-8 válida. Obtengo estos valores:

1 byte = 0% # the really important number that is often ignored 2 byte = 11.7% 3 byte = 3.03% (assumes surrogate halves are valid) 4 byte = 1.76% (includes two 2-byte characters)

También es útil intentar encontrar una cadena real legible (en cualquier idioma y cualquier codificación) que también sea una cadena UTF-8 válida. Esto es muy difícil, lo que indica que esto no es un problema con datos reales.


Aquí hay una implementación (con suerte libre de errores) de esta expresión en C simple:

_Bool is_utf8(const char * string) { if(!string) return 0; const unsigned char * bytes = (const unsigned char *)string; while(*bytes) { if( (// ASCII // use bytes[0] <= 0x7F to allow ASCII control characters bytes[0] == 0x09 || bytes[0] == 0x0A || bytes[0] == 0x0D || (0x20 <= bytes[0] && bytes[0] <= 0x7E) ) ) { bytes += 1; continue; } if( (// non-overlong 2-byte (0xC2 <= bytes[0] && bytes[0] <= 0xDF) && (0x80 <= bytes[1] && bytes[1] <= 0xBF) ) ) { bytes += 2; continue; } if( (// excluding overlongs bytes[0] == 0xE0 && (0xA0 <= bytes[1] && bytes[1] <= 0xBF) && (0x80 <= bytes[2] && bytes[2] <= 0xBF) ) || (// straight 3-byte ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) || bytes[0] == 0xEE || bytes[0] == 0xEF) && (0x80 <= bytes[1] && bytes[1] <= 0xBF) && (0x80 <= bytes[2] && bytes[2] <= 0xBF) ) || (// excluding surrogates bytes[0] == 0xED && (0x80 <= bytes[1] && bytes[1] <= 0x9F) && (0x80 <= bytes[2] && bytes[2] <= 0xBF) ) ) { bytes += 3; continue; } if( (// planes 1-3 bytes[0] == 0xF0 && (0x90 <= bytes[1] && bytes[1] <= 0xBF) && (0x80 <= bytes[2] && bytes[2] <= 0xBF) && (0x80 <= bytes[3] && bytes[3] <= 0xBF) ) || (// planes 4-15 (0xF1 <= bytes[0] && bytes[0] <= 0xF3) && (0x80 <= bytes[1] && bytes[1] <= 0xBF) && (0x80 <= bytes[2] && bytes[2] <= 0xBF) && (0x80 <= bytes[3] && bytes[3] <= 0xBF) ) || (// plane 16 bytes[0] == 0xF4 && (0x80 <= bytes[1] && bytes[1] <= 0x8F) && (0x80 <= bytes[2] && bytes[2] <= 0xBF) && (0x80 <= bytes[3] && bytes[3] <= 0xBF) ) ) { bytes += 4; continue; } return 0; } return 1; }

Tenga en cuenta que esta es una traducción fiel de la expresión regular recomendada por el W3C para la validación de formularios, que de hecho rechaza algunas secuencias UTF-8 válidas (en particular las que contienen caracteres de control ASCII).

Además, incluso después de corregir esto haciendo el cambio mencionado en el comentario, aún asume la terminación cero, lo que impide incrustar caracteres NUL, aunque técnicamente debería ser legal.

Cuando incursioné en la creación de mi propia biblioteca de cadenas, fui con UTF-8 modificado (es decir, codificación NUL como una secuencia de dos bytes demasiado larga). Siéntase libre de usar este encabezado como plantilla para proporcionar una rutina de validación que no sufra de las deficiencias anteriores.


Básicamente verifico si la clave dada (una cadena de 4 caracteres como máximo) concuerda con el formato de este enlace: http://www.fileformat.info/info/unicode/utf8.htm

/* ** Checks if the given string has all bytes like: 10xxxxxx ** where x is either 0 or 1 */ static int chars_are_folow_uni(const unsigned char *chars) { while (*chars) { if ((*chars >> 6) != 0x2) return (0); chars++; } return (1); } int char_is_utf8(const unsigned char *key) { int required_len; if (key[0] >> 7 == 0) required_len = 1; else if (key[0] >> 5 == 0x6) required_len = 2; else if (key[0] >> 4 == 0xE) required_len = 3; else if (key[0] >> 5 == 0x1E) required_len = 4; else return (0); return (strlen(key) == required_len && chars_are_folow_uni(key + 1)); }

Funciona bien para mí:

unsigned char buf[5]; ft_to_utf8(L''歓'', buf); printf("%d/n", char_is_utf8(buf)); // => 1


Es imposible detectar que una matriz dada de bytes sea una cadena UTF-8. Puede determinar de manera confiable que no puede ser UTF-8 válido (lo que no significa que no es inválido UTF-8); y puede determinar que podría ser una secuencia UTF-8 válida, pero esto está sujeto a falsos positivos.

Para un ejemplo simple, use un generador de números aleatorios para generar una matriz de 3 bytes aleatorios y úsela para probar su código. Estos son bytes aleatorios y, por lo tanto, no UTF-8, por lo que cada cadena que su código cree que es "posiblemente UTF-8" es un falso positivo. Supongo que (en estas condiciones) su código estará equivocado más del 12% del tiempo.

Una vez que reconoces que es imposible, puedes empezar a pensar en devolver un nivel de confianza (además de tu predicción). Por ejemplo, su función puede devolver algo como "Estoy 88% seguro de que esto es UTF-8".

Ahora haz esto para todos los demás tipos de datos. Por ejemplo, puede tener una función que compruebe si los datos son UTF-16 que podrían devolver "Tengo 95% de confianza de que esto es UTF-16", y luego decidir eso (porque el 95% es superior al 88%) es más probable que los datos fueran UTF-16 y no UTF-8.

El siguiente paso es agregar trucos para aumentar los niveles de confianza. Por ejemplo, si la cadena parece contener en su mayoría grupos sílabas válidas separadas por espacios en blanco, entonces puede estar mucho más seguro de que en realidad es UTF-8. De la misma manera, si los datos pueden ser HTML, entonces podría verificar cosas que podrían ser marcas HTML válidas y usarlas para aumentar su confianza.

Por supuesto, lo mismo se aplica a otros tipos de datos. Por ejemplo, si los datos tienen un encabezado PE32 o ELF válido, o un encabezado correcto de BMP o JPG o MP3, entonces puede estar mucho más seguro de que no es UTF-8 en absoluto.

Un enfoque mucho mejor es solucionar la causa real del problema. Por ejemplo, puede ser posible agregar algún tipo de identificador de "tipo de documento" al inicio de todos los archivos que le interesan, o tal vez decir "este software asume UTF-8 y no admite nada más"; para que no tengas que hacer conjeturas dudosas en primer lugar.


Este decodificador de Bjoern Hoermann es el más simple que he encontrado. También funciona alimentándolo de un solo byte, así como manteniendo un estado. El estado es muy útil para analizar UTF8 entrando en pedazos en la red.

http://bjoern.hoehrmann.de/utf-8/decoder/dfa/

// Copyright (c) 2008-2009 Bjoern Hoehrmann <[email protected]> // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. #define UTF8_ACCEPT 0 #define UTF8_REJECT 1 static const uint8_t utf8d[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 }; uint32_t inline decode(uint32_t* state, uint32_t* codep, uint32_t byte) { uint32_t type = utf8d[byte]; *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6) : (0xff >> type) & (byte); *state = utf8d[256 + *state*16 + type]; return *state; }

Un validador / detector simple no necesita el punto de código, por lo que podría escribirse así (el estado inicial se establece en UTF8_ACCEPT ):

uint32_t validate_utf8(uint32_t *state, char *str, size_t len) { size_t i; uint32_t type; for (i = 0; i < len; i++) { // We don''t care about the codepoint, so this is // a simplified version of the decode function. type = utf8d[(uint8_t)str[i]]; *state = utf8d[256 + (*state) * 16 + type]; if (*state == UTF8_REJECT) break; } return *state; }

Si el texto es válido, se devuelve UTF8_ACCEPT UTF8_ACCEPT. Si no es válido UTF8_REJECT . Si se necesitan más datos, se devuelve algún otro entero.

Ejemplo de uso con datos de alimentación en fragmentos (por ejemplo, desde la red):

char buf[128]; size_t bytes_read; uint32_t state = UTF8_ACCEPT; // Validate the UTF8 data in chunks. while ((bytes_read = get_new_data(buf, sizeof(buf))) { if (validate_utf8(&state, buf, bytes_read) == UTF8_REJECT)) { fprintf(stderr, "Invalid UTF8 data!/n"); return -1; } } // If everything went well we should have proper UTF8, // the data might instead have ended in the middle of a UTF8 // codepoint. if (state != UTF8_ACCEPT) { fprintf(stderr, "Invalid UTF8, incomplete codepoint/n"); }


No se puede detectar si una cadena dada (o secuencia de bytes) es un texto codificado en UTF-8, como por ejemplo todas y cada una de las series de octetos UTF-8 es también una serie válida (aunque sin sentido) de Latin-1 (o alguna otra codificación) octetos. Sin embargo, no todas las series de octetos Latin-1 válidos son series UTF-8 válidas. Por lo tanto, puede descartar cadenas que no se ajusten al esquema de codificación UTF-8:

U+0000-U+007F 0xxxxxxx U+0080-U+07FF 110yyyxx 10xxxxxx U+0800-U+FFFF 1110yyyy 10yyyyxx 10xxxxxx U+10000-U+10FFFF 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx


Puede usar el detector UTF-8 integrado en Firefox . Se encuentra en el detector universal de caracteres y es prácticamente una biblioteca de C ++. Debería ser extremadamente fácil encontrar la clase que reconoce UTF-8 y tomar solo eso.
Lo que esta clase básicamente hace es detectar secuencias de caracteres que son exclusivas de UTF-8.

  • obtener el último tronco de firefox
  • ve a / mozilla / extensions / universalchardet /
  • encuentra la clase de detector UTF-8 (no recuerdo bien cuál es su nombre exacto)

Sé que es un hilo viejo, pero pensé que publicaría mi solución aquí, ya que creo que es una mejora con respecto a la maravillosa solución de @Christoph (que subí).

No soy un experto, así que es posible que haya leído mal el RFC, pero me parece que se puede usar un mapa de 32 bytes en lugar de un mapa de 256 bytes, lo que permite ahorrar memoria y tiempo.

Esto me llevó a una macro simple que avanza un puntero de cadena por un carácter UTF-8, almacenando el punto de código UTF8 en un entero con signo de 32 bits y almacenando el valor -1 en caso de error.

Aquí está el código con algunos comentarios.

#include <stdint.h> /** * Maps the last 5 bits in a byte (0b11111xxx) to a UTF-8 codepoint length. * * Codepoint length 0 == error. * * The first valid length can be any value between 1 to 4 (5== error). * * An intermidiate (second, third or forth) valid length must be 5. * * To map was populated using the following Ruby script: * * map = []; 32.times { map << 0 }; (0..0b1111).each {|i| map[i] = 1} ; * (0b10000..0b10111).each {|i| map[i] = 5} ; * (0b11000..0b11011).each {|i| map[i] = 2} ; * (0b11100..0b11101).each {|i| map[i] = 3} ; * map[0b11110] = 4; map; */ static uint8_t fio_str_utf8_map[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 3, 3, 4, 0}; /** * Advances the `ptr` by one utf-8 character, placing the value of the UTF-8 * character into the i32 variable (which must be a signed integer with 32bits * or more). On error, `i32` will be equal to `-1` and `ptr` will not step * forwards. * * The `end` value is only used for overflow protection. */ #define FIO_STR_UTF8_CODE_POINT(ptr, end, i32) / switch (fio_str_utf8_map[((uint8_t *)(ptr))[0] >> 3]) { / case 1: / (i32) = ((uint8_t *)(ptr))[0]; / ++(ptr); / break; / case 2: / if (((ptr) + 2 > (end)) || / fio_str_utf8_map[((uint8_t *)(ptr))[1] >> 3] != 5) { / (i32) = -1; / break; / } / (i32) = / ((((uint8_t *)(ptr))[0] & 31) << 6) | (((uint8_t *)(ptr))[1] & 63); / (ptr) += 2; / break; / case 3: / if (((ptr) + 3 > (end)) || / fio_str_utf8_map[((uint8_t *)(ptr))[1] >> 3] != 5 || / fio_str_utf8_map[((uint8_t *)(ptr))[2] >> 3] != 5) { / (i32) = -1; / break; / } / (i32) = ((((uint8_t *)(ptr))[0] & 15) << 12) | / ((((uint8_t *)(ptr))[1] & 63) << 6) | / (((uint8_t *)(ptr))[2] & 63); / (ptr) += 3; / break; / case 4: / if (((ptr) + 4 > (end)) || / fio_str_utf8_map[((uint8_t *)(ptr))[1] >> 3] != 5 || / fio_str_utf8_map[((uint8_t *)(ptr))[2] >> 3] != 5 || / fio_str_utf8_map[((uint8_t *)(ptr))[3] >> 3] != 5) { / (i32) = -1; / break; / } / (i32) = ((((uint8_t *)(ptr))[0] & 7) << 18) | / ((((uint8_t *)(ptr))[1] & 63) << 12) | / ((((uint8_t *)(ptr))[2] & 63) << 6) | / (((uint8_t *)(ptr))[3] & 63); / (ptr) += 4; / break; / default: / (i32) = -1; / break; / } /** Returns 1 if the String is UTF-8 valid and 0 if not. */ inline static size_t fio_str_utf8_valid2(char const *str, size_t length) { if (!str) return 0; if (!length) return 1; const char *const end = str + length; int32_t c = 0; do { FIO_STR_UTF8_CODE_POINT(str, end, c); } while (c > 0 && str < end); return str == end && c >= 0; }


Tendría que analizar la cadena como UTF-8, ver http://www.rfc-editor.org/rfc/rfc3629.txt Es muy simple. Si el análisis falla, no es UTF-8. Hay varias bibliotecas UTF-8 simples que pueden hacer esto.

Tal vez podría simplificarse si usted sabe que la cadena es simple ASCII o contiene caracteres fuera de ASCII que están codificados en UTF-8. En ese caso, a menudo no necesita preocuparse por la diferencia, el diseño de UTF-8 fue que los programas existentes que podrían manejar ASCII, en la mayoría de los casos podrían manejar de forma transparente UTF-8.

Tenga en cuenta que ASCII está codificado en UTF-8 como sí mismo, por lo que ASCII es válido UTF-8.

La cadena de CA puede ser cualquier cosa, es el problema que necesita resolver y que no sabe si el contenido es ASCII, GB 2312, CP437, UTF-16 o cualquiera de las otras docenas de codificaciones de caracteres que hacen que los programas sean más difíciles. ?