No bash, como converter um ponto de código Unicode [0-9A-F] em um caractere imprimível?


23

Eu tenho uma lista de pontos de código Unicode, mas não conheço uma maneira "simples" de converter esses valores hexadecimais nos caracteres reais que eles representam ...

Ouvi dizer que o zsh tem echo -e '\u0965', mas uso o bash 4.1.

Existe algo tão simples quanto o método zsh, para o bash?


Respostas:


16

Você pode usar o eco do bash ou / bin / echo do GNU coreutils em combinação com iconv:

echo -ne '\x09\x65' | iconv -f utf-16be

Por padrão, o iconv converte em sua codificação de localidades. Talvez mais portátil do que confiar em um shell ou comando de eco específico seja o Perl. A maioria dos sistemas UNIX que conheço possui o Perl disponível e ele ainda possui várias portas do Windows.

perl -C -e 'print chr 0x0965'

Na maioria das vezes, quando preciso fazer isso, estou em um editor como o Vim / GVim, que possui suporte embutido. Enquanto estiver no modo de inserção, pressione Ctrl-V seguido de u e digite quatro caracteres hexadecimais. Se você deseja um caractere além de U + FFFF, use um U maiúsculo e digite 8 caracteres hexadecimais. O Vim também suporta mapas personalizados fáceis de criar. Ele converte uma série de caracteres em outro símbolo. Por exemplo, eu tenho um mapa-chave que desenvolvi chamado www, converte TM para ™, (C) para ©, (R) para ® e assim por diante. Eu também tenho um mapa-chave para o Klingon para quando isso se tornar necessário. Tenho certeza que o Emacs tem algo parecido. Se você estiver em um aplicativo GTK + que inclui o GVim e o Terminal GNOME, tente Control-Shift-u seguido de 4 caracteres hexadecimais para criar um caractere Unicode. Tenho certeza que o KDE / Qt tem algo parecido.

ATUALIZAÇÃO: A partir do Bash 4.2, parece ser um recurso incorporado agora:

echo $'\u0965'

ATUALIZAÇÃO: Além disso, hoje em dia um exemplo em Python provavelmente seria preferido em relação ao Perl. Isso funciona no Python 2 e 3:

python -c 'print(u"\u0965")'

Obrigado ... o perl é simpático e conciso, mas fiquei um pouco confuso sobre como ele sabe tratar o valor como UTF-16BE .. Acho que é isso que significa "chr" ...
Peter.O

@ Fred, esse é um bom argumento. O exemplo Perl é sensível ao local. O -C permite o processamento Unicode completo, mas o exemplo funciona porque meu código de idioma usa um exemplo Unicode. Se eu definir LANG como C, recebo um aviso sobre um caractere amplo na impressão, mas ele ainda é impresso. Se eu imprimir chr 0xa2em um local UTF-8, recebo um sinal de centavos ¢, mas se eu usar LANG = C, recebo porque imprime o byte 0xa2 que é inválido no UTF-8. O exemplo Vim / GVim é semi-sensível ao código do idioma. Mais corretamente, para a codificação do arquivo. Se você começou Vim em um local não-UTF-8, você precisará:set encoding=utf-8
penguin359

@fred Devo salientar que o Perl trata o valor de chr como um ponto de código Unicode se o Perl for iniciado em um código de idioma Unicode como o UTF-8. Um ponto de código é o número exclusivo que representa um caractere e não está vinculado a nenhuma codificação, como UTF-16BE ou UTF-8. Ele a converte na codificação correta quando a imprime. Por exemplo, o sinal cuneiforme A é o ponto de código U + 012000. Eu posso usar chr 0x12000no Perl (assumindo que o Unicode está ativo) para representá-lo. No UTF-16BE, são 0xd8, 0x08, 0xdc e 0x00. Seu personagem é U + 0965, que são os bytes 0x09 seguidos por 0x65 em UTF-16BE.
precisa saber é o seguinte

@ penguin359 .. Obrigado, um dia (espero), vou dar uma boa olhada no perl .. Parece insondável enigmático, mas o mesmo aconteceu com sed e regex, inicialmente, e agora é bem fácil ... talvez seja um pouco como vim; uma curva de aprendizagem, vela, em seguida, simples .... É bom ler sua explicação ... abre o caminho ..
Peter.O

Acabei de (re) descobrir que a impressão de Steven D não suporta o bloco ASCII da faixa unicode, então sua perlresposta agora é a melhor (para meus requisitos particulares) .. Eu já havia descartado a impressão (meses atrás) , mas eu tinha esquecido. Aqui está a pergunta / resposta sobre seus limites ... Por que printf relata um erro em todos, exceto em três (Unicode-range ASCII) Unicode Codepoints
Peter

13

Bash 4.2 (lançado em 2011) adicionado suporte para echo -e '\u0965', printf '\u0965', printf %b '\u0965'e echo $'\u0965'também de trabalho.

http://tiswww.case.edu/php/chet/bash/FAQ :

o   $'...', echo, and printf understand \uXXXX and \UXXXXXXXX escape sequences.

Obrigado ... Ainda estou usando principalmente o bash 4.1.5 no Ubuntu 10.04, mas certamente é bom saber que agora está disponível no 4.2. (+1)
Peter.O 14/03

1
+1; note que as bash 4.2.xversões possuem um erro em que os valores entre 0x80e 0xff( 128 - 255) - ou seja, no intervalo ASCII estendido - NÃO são codificados corretamente em UTF8 e, em vez disso, passam apenas, resultando em um caractere UTF8 inválido que alguns terminais processam ?. A partir de (pelo menos) 4.3.11isso foi corrigido; se echo $'\ued'processado í, o bug não está presente.
Mklement0

5

Se você possui GNU coreutils, tente printf:

$ printf '\u0965\n'

echo Você pode executar o trabalho se o console estiver usando UTF-8 e você tiver a codificação UTF-8:

$ echo -e '\xE0\xA5\xA5'

Você pode encontrar uma tabela de codificações hexadecimais Unicode para UTF-8 aqui: http://www.utf8-chartable.de/ . Você pode converter os pontos de código Unicode em hexadecimal usando várias linguagens de script. Aqui está um exemplo usando python:

python -c "print(unichr(int('0965', 16)).encode('utf-8').encode('hex'))"

A seguir, um script Perl que converterá argumentos para o valor hexadecimal correto (muitos parênteses desnecessários aqui):

#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Encode;

foreach (@ARGV) {
    say unpack('H*', encode('utf8', chr(hex($_))))
}

Por exemplo,

./uni2utf 0965
e0a5a5

Obviamente, se você possui Perl ou Python, também pode usá-los para imprimir os caracteres.


Obrigado .. O echonão fará o que eu quero, pois os Codepoints são Big-Endian UTF-16 de 2 bytes .. mas você me lembrou que existem 2 funções printf! (Eu pensei que o printf poderia fazer isso, e parece que eu estava invocando o errado) ... $(which printf)funciona ... Obrigado pelo exemplo do python ... mas por isso (minha curva de aprendizado), estou tentando ficar o mais próximo possível possível "bash" como a única linguagem de escrita envolvida .. (quando estiver confortável o suficiente com bash, ficarei preso no Python ... btw, .encode('hex')está um passo além do que eu preciso .. (achei que parecia um pouco ocupado em lá :)
Peter.O

Sim, o .encode ('hex') era apenas para obter o código hexadecimal que parecia funcionar com eco para mim. Fico feliz que pelo menos parte disso foi útil.
Steven D

Acabei de ver seu snippet perl .. obrigado ... é bom ter essas várias soluções apresentadas ... A printf é exatamente o que eu estava procurando (um único comando, como no exemplo zsh) ... .. eu possa um poço postar minha não-usando-outra-scripting em língua método que funciona em um fluxo de dados sextavadas (no \ u, etc) ..
Peter.O

Eu particularmente gosto da brevidade do printfexposto acima, mas ele não manipula valores abaixo da ... I've just re-discovered something I already knew (but dropped off the radar)... Here is a Question I asked about 4 months ago; [Why does printf report an error on all but three (ASCII-range) Unicode Codepoints](http://askubuntu.com/questions/20806/why-does-printf-report-an-error-on-all-but-three-ascii-range-unicode-codepoints)... So *penguin359's* solução `` \ u00A0 perl` está muito boa agora :) .. É uma invocação única e depois de "fácil digitar", então darei ele o green-tick paraperl
Peter.O

2

UPDATE: Aqui está uma maneira básica de fazer um único valor Unicode ... (por "bash" quero dizer: não usar nenhuma outra linguagem de script) .. obrigado a Gilles por uma sugestão neste Q / A do askubuntu .
De acordo com este link : recode (Obsoletes iconv, dos2unix, unix2dos) .. Edit: mas conforme o comentário abaixo, "obsoletos" pode significar apenas "alternativa"

      echo -n 0x0965 |recode UTF-16BE/x4..UTF-8

Aqui está um método para processar um dump hexadecimal bruto como entrada (por exemplo, sem prefixos de escape como \ u0965 e no \ x09 \ x65) ..
xxdé um utilitário dump hexadecimal (fornecido com vim-common) que pode reverter um dump hexadecimal bruto para os caracteres que o dump representa ... Os pontos de código Unicode são UTF-16BigEndian, que é exatamente o que é um dump hexadecimal.
xxdNo modo de reversão, aceita um fluxo de valores hexadecimais com quebras de linha. que são ignorados.

Esse script cria um fluxo UTF-16BE, que depois reverte para os caracteres originais.
A última linha contém os dois comandos necessários; xxdeiconv

for line in \
  "Matsuo Basho (1644-1694)" \
  "  pond" \
  "  frog jumps in" \
  "  plop!"
do 
  echo "$line" |iconv -f "$(locale charmap)" -t "UTF-16BE" |xxd -ps -u 
done |
#    (---this is the **revert** code---) 
tee >(xxd -p -u -r |iconv -f "UTF-16BE") ;echo

Aqui está a saída (mostrando a entrada hexadecimal UTF-16BE, primeiro).
Nota; xxdsegmenta sua própria saída com uma nova linha em 60 dígitos hexadecimais ... A opção de reversão ignora essas novas linhas .. ignora todas / todas as novas linhas (como os dígitos hexadecimais).

004D0061007400730075006F00200042006100730068006F002000280031
003600340034002D00310036003900340029000A
002000200070006F006E0064000A
0020002000660072006F00670020006A0075006D0070007300200069006E
000A
002000200070006C006F00700021000A

Matsuo Basho (1644-1694)
  pond
  frog jumps in
  plop!

Como parece que você usou as informações de penguin359 em sua resposta, considere marcar a resposta dele como correta e não a minha.
Steven D

@ Steven D: um comentário digno de nota, mas "parece" é a palavra operativa. Estou usando o iconv assim há alguns dias, o que me fez pensar se existe um único comando. Eu fiz o processamento de arquivo inteiro semelhante no Windows (C ++), por isso tenho um entendimento razoável do Unicode. Eu estava realmente seguindo um bashmétodo rápido e simples . Por "bash", quero dizer: usando a linguagem de script bash; não python / perl de dentro do bash). Adicionei isso como resposta, pois pode ter algum valor para alguém que está lendo esta página. É uma linha única para um arquivo inteiro. Você printfé a melhor resposta para mim.
precisa saber é o seguinte

2
Eu não diria que recodificar obsoletos iconv, na verdade recodificar é mais antigo que iconv, e atualmente iconv é muito mais comumente instalado por padrão do que recodificar (por exemplo, no Linux, iconv é quase sempre instalado porque vem com libc).
Gilles 'SO- stop be evil'

Graças .. Eu estava pensando sobre isso .. Essa página web não é exatamente a referência definitiva ... por isso é mais de uma alternativa ...
Peter.O

1

Supondo que a codificação padrão para o seu sistema operacional seja UTF-8 (verdadeira para a maioria das distribuições atuais), você pode usar o bash diretamente para converter qualquer ponto de código UNICODE:

echo -e "Unicode Character 'DEVANAGARI DOUBLE DANDA' (U+0965) \U0965"

Obviamente, o glifo aparecerá corretamente apenas se você tiver a fonte correta. A partir do bash 4.3, todos os pontos de código funcionarão corretamente. E essas duas opções internas também funcionarão:

printf "%b" "Unicode Character (U+0965) \U0965 \n"
echo $'Unicode Character (U+0965) \U0965'

Observe que, para o bash 4.2, os pontos de código Unicode de 0x80para 0xFFsão codificados incorretamente (bug do bash). Para solucionar esse problema, você deve dar uma olhada no programa neste site (também é bom para uma análise aprofundada da questão da conversão de números em caracteres.


Funciona para mim no bash 4.3 e no zsh. Existe um relatório de bug para o bash 4.2 ao qual você pode vincular?
Mikel

Isso parece-me que o bug correto: https://lists.gnu.org/archive/html/bug-bash/2012-02/msg00035.htmlDescrição: \ U e \ U incorretamente valores codificar entre \ U80 e \ uff

0

Usando a substituição Pattern na versão 4.2 do bash (e superior):

${parameter/pattern/string}

conforme descrito aqui http://steve-parker.org/sh/tips/pattern-substitution/

UNICODE_HEX="U+02211"
printf ${UNICODE_HEX/U+/"\U"}


UNICODE_HEX="U+03BB"
printf ${UNICODE_HEX/U+/"\U"}
λ         

1
Observe que, conforme declarado em uma resposta anterior , isso funciona apenas na versão 4.2 do bash (e superior). De fato, isso acrescenta pouco à resposta anterior.
G-Man diz 'Reinstate Monica'
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.