В bash, как я могу преобразовать кодовую точку Unicode [0-9A-F] в печатный символ?

23

У меня есть список кодовых точек Unicode, но я не знаю "простого" способа конвертировать эти шестнадцатеричные значения в действительные символы, которые они представляют ...

Я слышал, что Zsh имеет echo -e '\u0965', но я использую Bash 4.1.

Есть ли что-то такое же простое, как метод zsh, для bash?

Peter.O
источник

Ответы:

16

Вы можете использовать echo или / bin / echo от bash из GNU coreutils в сочетании с iconv:

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

По умолчанию iconv конвертируется в вашу кодировку локалей. Возможно, более переносимым, чем использование определенной команды оболочки или эха, является Perl. В большинстве любых UNIX-систем, о которых я знаю, Perl доступен и даже имеет несколько портов Windows.

perl -C -e 'print chr 0x0965'

Большую часть времени, когда мне нужно сделать это, я нахожусь в редакторе, таком как Vim / GVim, который имеет встроенную поддержку. В режиме вставки нажмите Ctrl-V, затем u, затем введите четыре шестнадцатеричных символа. Если вы хотите, чтобы символ выходил за пределы U + FFFF, используйте заглавные буквы U и введите 8 шестнадцатеричных символов. Vim также поддерживает пользовательские карты ключей. Он преобразует серию символов в другой символ. Например, у меня есть карта ключей, которую я разработал, которая называется www, она преобразует TM в ™, (C) в ©, (R) в ® и так далее. У меня также есть карта ключей для клингона, когда это становится необходимым. Я уверен, что в Emacs есть нечто подобное. Если вы находитесь в приложении GTK +, которое включает в себя GVim и GNOME Terminal, вы можете попробовать Control-Shift-u, за которым следуют 4 шестнадцатеричных символа, чтобы создать символ Unicode. Я уверен, что в KDE / Qt есть что-то похожее.

ОБНОВЛЕНИЕ: Начиная с Bash 4.2, теперь это встроенная функция:

echo $'\u0965'

ОБНОВЛЕНИЕ: Кроме того, в настоящее время пример Python, вероятно, предпочтительнее Perl. Это работает как в Python 2, так и в 3:

python -c 'print(u"\u0965")'
penguin359
источник
Спасибо ... perl в хорошем и кратком изложении, но я немного озадачен тем, как он знает, как трактовать значение как UTF-16BE .. Я думаю, это то, что означает "chr" ...
Peter.O
@ Фред, это хорошая мысль. Пример Perl чувствителен к локали. -C включает полную обработку Unicode, но пример работает, потому что моя локаль использует пример Unicode. Если я установлю LANG на C, я получу предупреждение о широком символе при печати, но он все еще печатается. Если я печатаю chr 0xa2в локали UTF-8, я получаю знак центов ¢, но если я использую LANG = C, я получаю , потому что он печатает байт 0xa2, который недопустим в UTF-8. Пример Vim / GVim полу-чувствителен к локали. Вернее, к кодировке файлов. Если вы запустили Vim не в UTF-8, вам нужно будет:set encoding=utf-8
penguin359
@fred Я должен отметить, что Perl обрабатывает значение chr как кодовую точку Unicode, если Perl запускается в локали Unicode, такой как UTF-8. Кодовая точка - это уникальный номер, который представляет символ и не связан с какой-либо одной кодировкой, такой как UTF-16BE или UTF-8. Он конвертирует его в правильную кодировку при распечатке. Например, клинописный знак A является кодовой точкой U + 012000. Я могу использовать chr 0x12000в Perl (при условии, что Unicode активен), чтобы представить его. В UTF-16BE это 0xd8, 0x08, 0xdc и 0x00. Ваш персонаж - U + 0965, который как раз и состоит из байтов 0x09, за которыми следует 0x65 в UTF-16BE.
penguin359
@ penguin359 .. Спасибо, однажды (надеюсь) я хорошо посмотрю на Perl .. Это кажется непостижимо загадочным, но потом так и сделали sed и regex, изначально, а теперь это довольно просто ... может быть, немного как vim; крутая кривая обучения, затем плавание под парусом ... Хорошо читать ваше объяснение ... оно прокладывает путь ...
Peter.O
Я только что (повторно) обнаружил, что printf soultion Стивена Ди не будет обрабатывать ASCII-блок диапазона Юникода, поэтому ваш perlответ сейчас лучший (для моих конкретных требований). Ранее я исключал printf (несколько месяцев назад) , но я забыл об этом. Вот квест / ответ о его пределах ... Почему printf сообщает об ошибке на всех, кроме трех (ASCII-диапазона) кодовых точках Unicode
Peter.O
13

Bash 4.2 (выпущена в 2011 году) добавлена поддержка echo -e '\u0965', printf '\u0965', printf %b '\u0965'и echo $'\u0965'также работать.

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

o   $'...', echo, and printf understand \uXXXX and \UXXXXXXXX escape sequences.
LRI
источник
Спасибо ... Я до сих пор в основном использую bash 4.1.5 в Ubuntu 10.04, но, безусловно, приятно знать, что он теперь доступен в 4.2. (+1)
Peter.O
1
+1; обратите внимание, что в bash 4.2.xверсиях есть ошибка, при которой значения между 0x80and 0xff( 128 - 255) - то есть в расширенном диапазоне ASCII - НЕ корректно кодируются в UTF8, а вместо этого просто передаются, что приводит к неверному символу UTF8, который некоторые терминалы отображают как ?. По состоянию на (по крайней мере) 4.3.11это было исправлено; если echo $'\ued'рендерит í, то ошибки нет .
mklement0
5

Если у вас есть GNU coreutils, попробуйте printf:

$ printf '\u0965\n'

echo может выполнить эту работу, если ваша консоль использует UTF-8 и у вас есть кодировка UTF-8:

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

Вы можете найти таблицу шестнадцатеричных кодировок Unicode в UTF-8 здесь: http://www.utf8-chartable.de/ . Вы можете преобразовать кодовые точки Unicode в шестнадцатеричные, используя несколько языков сценариев. Вот пример использования Python:

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

Ниже приведен скрипт Perl, который преобразует аргументы в правильное шестнадцатеричное значение (здесь много лишних скобок):

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

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

Например,

./uni2utf 0965
e0a5a5

Конечно, если у вас есть Perl или Python, вы также можете использовать их для печати символов.

Стивен Д
источник
Спасибо .. Не echoбуду делать то, что я хочу, так как Codepoints - это 2-байтовые UTF-16 Big-Endian ... но вы напомнили мне, что есть 2 функции printf! (Я думал, что printf может это сделать, и кажется, что я вызывал неправильный) ... $(which printf)работает ... Спасибо за пример с Python ... но за это (моя кривая обучения) я пытаюсь придерживаться как можно ближе насколько возможно "bash" как единственный используемый язык скритинга ... (когда я достаточно комфортно с bash, я застряну в Python ... кстати, .encode('hex')это один шаг за пределы того, что мне нужно ... (я думал, что это выглядело как немного занят там :)
Peter.O
Да, .encode ('hex') был просто для того, чтобы получить шестнадцатеричный код, который, казалось, работал для меня с echo. Рад, что по крайней мере часть этого была полезна.
Стивен Д.
Я только что видел ваш фрагмент perl .. спасибо ... хорошо, что эти различные решения были представлены ... Одна из printf - это именно то, что я искал (одна команда, как в примере с zsh) ... .. Я вполне могу опубликовать мой метод « не использующий другой язык сценариев», который работает с потоком шестнадцатеричных данных (без \ u и т. Д.) ..
Peter.O
Мне особенно нравится краткость printfвышеприведенного, но он не обрабатывает значения ниже `` \ u00A0 ... 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* perl`. Решение выглядит довольно хорошо сейчас :) .. Это один вызов, и я после "легко набираю", поэтому я дам его зеленая галочка дляperl
Питер
2

UPDATE: Вот Баш способ сделать одно значение Unicode ... (по «Баш» Я имею в виду: не использовать любой другой язык сценариев) .. спасибо Жиль для suggeston в этом askubuntu Q / A .
По этой ссылке : перекодировать (Obsoletes iconv, dos2unix, unix2dos) .. Редактировать: но согласно комментарию ниже, "obsoletes" может просто означать "альтернатива"

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

Вот метод для обработки необработанного шестнадцатеричного дампа в качестве входных данных (т. Е. Без префиксных префиксов, таких как; \ u0965 и без \ x09 \ x65) ..
xxdявляется утилитой шестнадцатеричного дампа (в комплекте vim-common), которая может восстановить необработанный шестнадцатеричный дамп символам, которые представляет дамп ... Кодовые точки Unicode - это UTF-16BigEndian, который в точности соответствует Hex-
xxdдампу. В режиме возврата принимает поток шестнадцатеричных значений с разрывами строк, которые игнорируются.

Этот сценарий создает поток UTF-16BE, который затем возвращается к исходным символам.
Последняя строка содержит две необходимые команды; xxdа такжеiconv

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

Вот выходные данные (сначала показаны входные данные в шестнадцатеричном формате UTF-16BE).
Заметка; xxdсегментирует свой вывод новой строкой с 60 шестнадцатеричными цифрами ... Опция возврата игнорирует эти новые строки .. она игнорирует любые / все новые строки (поскольку они не являются шестнадцатеричными цифрами) ..

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

Matsuo Basho (1644-1694)
  pond
  frog jumps in
  plop!
Peter.O
источник
Так как кажется, что вы использовали информацию penguin359 в своем ответе, вы можете пометить его ответ как правильный, а не мой.
Стивен Д
@ Стивен Д: примечательный комментарий, но слово «кажется» - это оперативное слово. Я использую iconv вот так уже пару дней, и мне стало интересно, есть ли одна команда. Я сделал аналогичную обработку всего файла в Windows (C ++), поэтому у меня есть разумное понимание Unicode. Я был действительно после быстрого и простого bashметода. Под «bash» я имею в виду: использование языка сценариев bash; не python / perl изнутри bash). Я добавил это как ответ, потому что это может иметь определенную ценность для того, кто читает эту страницу. Это хорошая строка для всего файла. Ваш printfлучший ответ для меня.
Peter.O
2
Я бы не сказал, что перекодировать устаревшие iconv, на самом деле перекодировка старше, чем iconv, и в наши дни iconv гораздо чаще устанавливается по умолчанию, чем перекодировать (например, в Linux iconv почти всегда устанавливается, поскольку поставляется с libc).
Жиль "ТАК - перестань быть злым"
Спасибо .. Мне было интересно об этом .. Эта веб-страница не совсем
точная
1

Предполагая, что кодировкой по умолчанию для вашей ОС является UTF-8 (верно для большинства современных дистрибутивов), тогда вы можете напрямую использовать bash для преобразования любой кодовой точки UNICODE:

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

Конечно, глиф будет отображаться правильно, только если у вас правильный шрифт. Начиная с bash 4.3, все кодовые точки будут работать правильно. И эти два встроенных варианта также будут работать:

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

Обратите внимание, что для bash 4.2 кодовые точки Unicode от 0x80to 0xFFкодируются неправильно (ошибка bash). Чтобы обойти эту проблему, вы должны взглянуть на программу на этом сайте (также полезно для глубокого изучения вопроса о преобразовании чисел в символы).

HalosGhost
источник
У меня работает в bash 4.3 и zsh. Есть ли сообщение об ошибке для bash 4.2, на которое вы можете сослаться?
Микель
для меня это выглядит как правильная ошибка: https://lists.gnu.org/archive/html/bug-bash/2012-02/msg00035.htmlОписание: \ u и \ U неправильно кодируют значения между \ u80 и \ uff
0

Использование подстановки Pattern в bash версии 4.2 (и выше):

${parameter/pattern/string}

как описано здесь 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"}
λ         
illucent
источник
1
Обратите внимание, что, как указано в предыдущем ответе , это работает только в bash версии 4.2 (и выше). На самом деле, это довольно мало добавляет к предыдущему ответу.
G-Man говорит: «Восстановите Монику»