É UTF-8 modificado válido?

9

UTF-8 é uma maneira relativamente simples de codificar pontos de código Unicode em um formato de largura variável, para que não confunda facilmente código que não é compatível com Unicode.

Visão geral do UTF-8

  • Bytes no intervalo de 1-0x7F, inclusive, são normalmente válidos
  • Os bytes com o padrão de bits 10XX XXXXsão considerados bytes de continuação, com os seis bits menos significativos sendo usados ​​para codificar parte de um ponto de código. Eles não devem aparecer, a menos que sejam esperados por um byte anterior.
  • Os bytes com o padrão 110X XXXXesperam um byte de continuação depois
  • Os bytes com o padrão 1110 XXXXesperam dois bytes de continuação depois
  • Os bytes com o padrão 1111 0XXXesperam três bytes de continuação depois
  • Todos os outros bytes são inválidos e não devem aparecer em nenhum lugar de um fluxo UTF-8. Clusters de 5, 6 e 7 bytes são possíveis em teoria, mas não serão permitidos para os propósitos deste desafio.

Codificações excessivas

O UTF-8 também exige que um ponto de código seja representado com o número mínimo de bytes. Qualquer sequência de bytes que possa ser representada com menos bytes não é válida. O UTF-8 modificado adiciona uma exceção a isso para caracteres nulos (U + 0000), que devem ser representados como C0 80(representação hexadecimal)) e, em vez disso, não permite que bytes nulos apareçam em qualquer lugar do fluxo. (Isso o torna compatível com cadeias terminadas em nulo)

Desafio

Você deve criar um programa que, ao receber uma sequência de bytes, determine se essa sequência representa UTF-8 modificado válido e retornará um valor verdadeiro, se válido, e caso contrário, um valor falso. Observe que você deve verificar se há codificações longas e bytes nulos (já que este é UTF-8 modificado). Você não precisa decodificar os valores UTF-8.

Exemplos

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')

Regras

  • Aplicam-se regras e brechas padrão
  • A entrada e a saída podem estar em qualquer formato conveniente, desde que todos os valores no intervalo de bytes não assinados (0-255) possam ser lidos.
    • Pode ser necessário usar uma matriz ou arquivo em vez de uma sequência terminada por nulo. Você precisa ler bytes nulos.
  • O menor código vence!
  • Observe que não é garantido que o uso de built-in para decodificar o UTF-8 esteja em conformidade com os requisitos fornecidos aqui. Pode ser necessário contorná-lo e criar casos especiais.

EDIT: bônus adicional por não usar builtins que decodificam UTF-8

EDIT2: bônus removido, já que apenas a resposta Rust foi qualificada e é difícil de definir.

Beefster
fonte
Eu estive esperando por este.
Adám 16/01/19
Convém adicionar um caso de teste com um byte inválido no intervalo 0xF8-0xFF.
Arnauld
2
Parece que substitutos (0xD800 - 0xDFFF) e pontos de código além de 0x10FFFF são permitidos, ao contrário das especificações "modernas" do UTF-8. Eu acho que isso deve ser esclarecido, idealmente com casos de teste adicionais.
Nwellnhof 17/01/19
mais exemplos seriam úteis
don bright
"Bytes no intervalo de 0-0x7F, inclusive, são normalmente válidos" é suposto ser de 1 a 0x7f?
22619 Don Don Bright

Respostas:

2

Elixir , 69 bytes

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

Experimente online!

Faz uso da função interna de validação de string. Recebe a entrada como Elixir binário.

Kirill L.
fonte
1

APL (Dyalog Unicode) , 41 39 bytes SBCS

Função de prefixo tácito anônimo. Toma uma string Unicode como argumento em que os pontos de código dos caracteres representam os bytes de entrada.

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

Experimente online!

'À\x80'⎕R⎕AR moran Jr. C0 80s com a letra maiúscula Um lphabet

{} Aplique a seguinte função anônima, em que o argumento é :

0:: se ocorrer algum erro:

  0 retornar zero

 tentar:

  ⎕UCS⍵ converter a string em pontos de código

  'UTF-8'⎕UCS⍣2 interpretar como UTF-8 bytes e converter o texto resultante em bytes

  ⌊/ byte mais baixo (zero se houver um byte nulo, positivo se não for, "infinito" se for uma string vazia)

  × sinal (zero se houver byte nulo, um se não estiver)

Adão
fonte
Esse retorno não seria realmente verdadeiro D9 C0 80 84 C0 80 10?
Neil
@ Neil É verdade. Isso está errado porque a remoção C0 80faz com que bytes não relacionados sejam adjacentes de uma maneira que seja válida, embora sejam inválidos quando separados? Editar: atualizado para corrigir isso sem custo de bytes.
Adám 17/01/19
alguns dos personagens estão aparecendo na minha tela apenas como retângulos ou caixas, isso é normal? estou no firefox no linux. APL é uma linguagem muito interessante.
don brilhante
@donbright Na minha experiência, os caracteres APL sempre são renderizados corretamente, mesmo que às vezes menos do que maravilhosamente, então essas caixas provavelmente são apenas Quad s, das quais deve haver quatro no código principal. Deve tornar como esta . E sim, o APL é incrível e muito divertido. Você também pode aprender fácil e rapidamente - basta entrar no APL Orchard .
Adám 22/01/19
sim eles são quads. obrigado.
22819 Don Don Bright
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')

Experimente online!

Saídas via código de saída

TFeld
fonte
0

Ferrugem - 191 bytes 313 bytes

Por comentário abaixo original não funcionou corretamente. Versão nova e melhorada. Nenhuma biblioteca é usada, porque o Mighty Rust não precisa de você e de suas bibliotecas. Esse código usa a correspondência de padrões com uma máquina de estado. Por descaradamente arrancando a especificação UTF8 , depois de encontrar, através de referência e discussão por Jon Skeet , podemos copiar a especificação quase personagem para personagem em um jogo de bloco padrão de jogo ferrugem. No final, adicionamos o requisito especial Mutf8 da Beefster para que o C0 80 seja considerado válido. Ungolfed:

/* 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;

experimentá-lo no parque infantil ferrugem

não brilhante
fonte
Adereços para fazê-lo manualmente, mas acho que sua verificação excessiva está incorreta.
Beefster 23/01/19
Seu desafio, caro senhor, me provoca imitação, e concluo esta carta desafiando você, por minha vez, a se fixar em um homem que exporá sua pergunta de maneira mais contundente ( bit.ly/2T8tXhO )
don bright