¿Es válido UTF-8 modificado?

9

UTF-8 es una forma relativamente simple de codificar puntos de código Unicode en un formato de ancho variable, de modo que no confunda fácilmente el código que no conoce Unicode.

Descripción general de UTF-8

  • Los bytes en el rango de 1-0x7F, inclusive, son normalmente válidos
  • Los bytes con el patrón de bits 10XX XXXXse consideran bytes de continuación, y los seis bits menos significativos se utilizan para codificar parte de un punto de código. No deben aparecer a menos que sean esperados por un byte anterior.
  • Los bytes con el patrón 110X XXXXesperan un byte de continuación después
  • Los bytes con el patrón 1110 XXXXesperan dos bytes de continuación después
  • Los bytes con el patrón 1111 0XXXesperan tres bytes de continuación después
  • Todos los demás bytes no son válidos y no deberían aparecer en ninguna parte de una secuencia UTF-8. Los grupos de 5, 6 y 7 bytes son posibles en teoría, pero no se permitirán a los efectos de este desafío.

Codificaciones demasiado largas

UTF-8 también requiere que un punto de código se represente con el número mínimo de bytes. Cualquier secuencia de bytes que pueda representarse con menos bytes no es válida. El UTF-8 modificado agrega una excepción a esto para los caracteres nulos (U + 0000), que deben representarse como C0 80(representación hexadecimal), y en su lugar no permite que aparezcan bytes nulos en cualquier parte de la secuencia. (Esto lo hace compatible con cadenas terminadas en nulo)

Desafío

Debe crear un programa que, cuando se le dé una cadena de bytes, determinará si esa cadena representa UTF-8 modificado válido y devolverá un valor verdadero si es válido y un valor falso de lo contrario. Tenga en cuenta que debe verificar si hay codificaciones demasiado largas y bytes nulos (ya que se trata de UTF-8 modificado). No necesita decodificar los valores UTF-8.

Ejemplos

41 42 43  ==> yes (all bytes are in the 0-0x7F range)
00 01 02  ==> no (there is a null byte in the stream)
80 7F 41  ==> no (there is a continuation byte without a starter byte)
D9 84 10  ==> yes (the correct number of continuation bytes follow a starter byte)
F0 81 82 41  ==> no (there are not enough continuation bytes after F0)
EF 8A A7 91  ==> no (too many continuation bytes)
E1 E1 01  ==> no (starter byte where a continuation byte is expected)
E0 80 87  ==> no (overlong encoding)
41 C0 80  ==> yes (null byte encoded with the only legal overlong encoding)
F8 42 43  ==> no (invalid byte 'F8')

Reglas

  • Aplican reglas estándar y lagunas
  • La entrada y la salida pueden estar en cualquier formato conveniente siempre que se puedan leer todos los valores en el rango de bytes sin signo (0-255).
    • Es posible que deba usar una matriz o un archivo en lugar de una cadena terminada en nulo. Debe poder leer bytes nulos.
  • ¡El código más corto gana!
  • Tenga en cuenta que el uso de incorporados para decodificar el UTF-8 no está garantizado para cumplir con los requisitos aquí establecidos. Es posible que deba solucionarlo y crear casos especiales.

EDITAR: bonificación adicional por no usar incorporados que decodifican UTF-8

EDIT2: se eliminó la bonificación ya que solo la respuesta de Rust calificó y es difícil de definir.

Carne de res
fuente
He estado esperando este.
Adám
Es posible que desee agregar un caso de prueba con un byte no válido en el rango 0xF8-0xFF.
Arnauld
2
Parece que los sustitutos (0xD800 - 0xDFFF) y los puntos de código más allá de 0x10FFFF están permitidos, en contra de la especificación "moderna" UTF-8. Creo que esto debería aclararse, idealmente con casos de prueba adicionales.
nwellnhof
más ejemplos serían útiles
don bright
"Los bytes en el rango de 0-0x7F, inclusive, son normalmente válidos" ¿se supone que es 1 a 0x7f?
Don brillante

Respuestas:

2

Elixir , 69 bytes

import String
&valid? replace replace(&1,<<0>>,"\xFF"),"\xC0\x80","0"

Pruébalo en línea!

Utiliza la función de validación de cadena incorporada. Toma entrada como elixir binario.

Kirill L.
fuente
1

APL (Dyalog Unicode) , 41 SBCS de 39 bytes

Función de prefijo tácito anónimo. Toma una cadena Unicode como argumento donde los puntos de código de los caracteres representan los bytes de entrada.

{0::0⋄×⌊/'UTF-8'UCS2UCS⍵}'À\x80'RA

Pruébalo en línea!

'À\x80'⎕R⎕AR eplace C0 80s con la mayúscula A lphabet

{... } aplique la siguiente función anónima, donde el argumento es :

0:: Si ocurre algún error:

  0 devolver cero

 tratar:

  ⎕UCS⍵ convertir la cadena a puntos de código

  'UTF-8'⎕UCS⍣2 interpretar como bytes UTF-8 y convertir el texto resultante de nuevo a bytes

  ⌊/ byte más bajo (cero si hay un byte nulo, positivo si no, "infinito" si una cadena vacía)

  × signo (cero si el byte nulo está presente, uno si no)

Adán
fuente
¿No volvería esto a ser verdad D9 C0 80 84 C0 80 10?
Neil
@Neil Ciertamente . ¿Es eso incorrecto porque la eliminación C0 80hace que los bytes no relacionados sean adyacentes de una manera válida, aunque no son válidos cuando están separados? Editar: actualizado para solucionarlo sin costo de bytes.
Adám
Algunos de los personajes aparecen en mi pantalla como rectángulos o cajas, ¿es eso normal? Estoy en Firefox en Linux. APL es un lenguaje muy interesante.
Don brillante
@donbright En mi experiencia, los caracteres APL siempre se representan correctamente, incluso si a veces son menos que hermosos, por lo que esos cuadros probablemente sean solo Quad s, de los cuales debería haber cuatro en el código principal. Debe rendir como este . Y sí, APL es increíble y muy divertido. También puedes aprenderlo de manera fácil y rápida, solo ven al APL Orchard .
Adám
Sí, son quads. Gracias.
don brillante
0

Python 2 , 104 102 bytes

''.join(chr(int(c,16))for c in input().replace('00','-').replace('C0 80','0').split()).decode('utf-8')

Pruébalo en línea!

Salidas a través del código de salida

TFeld
fuente
0

Óxido - 191 bytes 313 bytes

Por comentario debajo del original no funcionó correctamente. Versión nueva y mejorada. No se utilizan bibliotecas, porque The Mighty Rust no tiene necesidad de usted ni de sus bibliotecas. Este código utiliza la coincidencia de patrones con una máquina de estados. Al extraer descaradamente la especificación UTF8 , después de encontrarla por referencia y discusión por Jon Skeet , podemos copiar la especificación casi carácter por carácter en un bloque de coincidencia de patrón de coincidencia de óxido. Al final, agregamos el requisito especial Mutf8 de Beefster para que C0 80 se considere válido. Sin golf:

/* http://www.unicode.org/versions/corrigendum1.html
 Code Points        1st Byte    2nd Byte    3rd Byte    4th Byte
U+0000..U+007F      00..7F           
U+0080..U+07FF      C2..DF      80..BF           
U+0800..U+0FFF      E0          A0..BF      80..BF       
U+1000..U+FFFF      E1..EF      80..BF      80..BF       
U+10000..U+3FFFF    F0          90..BF      80..BF      80..BF
U+40000..U+FFFFF    F1..F3      80..BF      80..BF      80..BF
U+100000..U+10FFFF  F4          80..8F      80..BF      80..BF
*/

let m=|v:&Vec<u8>|v.iter().fold(0, |s, b| match (s, b) {
        (0, 0x01..=0x7F) => 0,
        (0, 0xc2..=0xdf) => 1,
        (0, 0xe0) => 2,
        (0, 0xe1..=0xef) => 4,
        (0, 0xf0) => 5,
        (0, 0xf1..=0xf3) => 6,
        (0, 0xf4) => 7,
        (1, 0x80..=0xbf) => 0,
        (2, 0xa0..=0xbf) => 1,
        (4, 0x80..=0xbf) => 1,
        (5, 0x90..=0xbf) => 4,
        (6, 0x80..=0xbf) => 4,
        (7, 0x80..=0x8f) => 4,
        (0, 0xc0) => 8, // beefster mutf8 null
        (8, 0x80) => 0, // beefster mutf8 null
        _ => -1,
    })==0;

Pruébalo en el patio de óxido

don brillante
fuente
Props para hacerlo manualmente, pero creo que su verificación excesiva es incorrecta.
Beefster
Su desafío, querido señor, me provoca imitación, y concluyo esta carta desafiándole, a mi vez, a que se fije en un hombre que expondrá su pregunta sin rodeos ( bit.ly/2T8tXhO )
don bright