W bash, jak mogę przekonwertować Unicode Codepoint [0-9A-F] na znak do druku?

Odpowiedzi:

16

Możesz użyć echa basha lub / bin / echo z GNU coreutils w połączeniu z iconv:

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

Domyślnie iconv konwertuje na kodowanie ustawień regionalnych. Być może bardziej przenośnym niż poleganiem na konkretnej powłoce lub poleceniu echa jest Perl. Większość systemów UNIX, o których wiem, ma dostępny Perl, a nawet ma kilka portów Windows.

perl -C -e 'print chr 0x0965'

Przez większość czasu, gdy muszę to zrobić, jestem w edytorze takim jak Vim / GVim, który ma wbudowane wsparcie. W trybie wstawiania naciśnij Ctrl-V, a następnie u, a następnie wpisz cztery znaki szesnastkowe. Jeśli chcesz znaku poza U + FFFF, użyj dużej litery U i wpisz 8 znaków szesnastkowych. Vim obsługuje również niestandardowe, łatwe do tworzenia mapy klawiszy. Konwertuje serię znaków na inny symbol. Na przykład mam opracowaną mapę klawiszy o nazwie www, która konwertuje TM na ™, (C) na ©, (R) na ® i tak dalej. Mam też mapę klawiszy dla Klingona, kiedy to staje się konieczne. Jestem pewien, że Emacs ma coś podobnego. Jeśli korzystasz z aplikacji GTK +, która zawiera GVim i GNOME Terminal, możesz spróbować Control-Shift-u, a następnie 4 znaków szesnastkowych, aby utworzyć znak Unicode. Jestem pewien, że KDE / Qt ma coś podobnego.

AKTUALIZACJA: Od wersji Bash 4.2 wydaje się być wbudowaną funkcją:

echo $'\u0965'

AKTUALIZACJA: W dzisiejszych czasach prawdopodobnie język Python byłby preferowany od Perla. Działa to zarówno w Pythonie 2, jak i 3:

python -c 'print(u"\u0965")'
penguin359
źródło
Dzięki ... perlowy w ładnym i zwięzłym, ale trochę mnie to zastanawia, jak wie, jak traktować wartość jako UTF-16BE .. Myślę, że to właśnie oznacza "chr" ...
Peter.O
@fred to dobra uwaga. Przykład Perla jest wrażliwy na ustawienia regionalne. Opcja -C włącza pełne przetwarzanie Unicode, ale przykład działa, ponieważ moje ustawienia regionalne używają przykładu Unicode. Jeśli ustawię LANG na C, pojawi się ostrzeżenie o szerokim znaku w druku, ale nadal będzie drukowany. Jeśli chr 0xa2drukuję w ustawieniach regionalnych UTF-8, otrzymuję znak centów ¢, ale jeśli używam LANG = C, dostaję , ponieważ wypisuje bajt 0xa2, który jest nieprawidłowy w UTF-8. Przykład Vim / GVim jest częściowo wrażliwy na ustawienia regionalne. Bardziej poprawnie, do kodowania pliku. Jeśli zacząłeś Vima w lokalizacji innej niż UTF-8, będziesz musiał:set encoding=utf-8
penguin359,
@ fred Powinienem zauważyć, że Perl traktuje wartość chr jako punkt kodowy Unicode, jeśli Perl jest uruchamiany w ustawieniach regionalnych Unicode takich jak UTF-8. Punkt kodowy to unikalny numer reprezentujący znak i niepowiązany z żadnym kodowaniem, takim jak UTF-16BE lub UTF-8. Konwertuje go na prawidłowe kodowanie podczas drukowania. Na przykład znak klinowy A to punkt kodowy U + 012000. Mogę go używać chr 0x12000w Perlu (zakładając, że Unicode jest aktywny) do jego reprezentowania. W UTF-16BE jest to 0xd8, 0x08, 0xdc i 0x00. Twój znak to U + 0965, który akurat jest bajtami 0x09, a następnie 0x65 w UTF-16BE.
penguin359,
@ penguin359 .. Dzięki, pewnego dnia (mam nadzieję) przyjrzę się perlowi. Wydaje się niezgłębnie tajemniczy, ale potem tak samo zrobił sed i regex, początkowo, a teraz jest dość łatwy ... może to trochę jak vim; stroma krzywa uczenia się, a następnie proste żeglowanie .... Dobrze jest przeczytać wyjaśnienie ... toruje drogę ..
Peter.O
Właśnie (ponownie) odkryłem, że funkcja printf Stevena D nie poradzi sobie z blokiem ASCII zakresu Unicode, więc twoja perlodpowiedź jest teraz najlepsza (jak na moje szczególne wymagania). Wcześniej wykluczyłem printf (miesiące temu) , ale o tym zapomniałem. Oto pytanie / odpowiedź na temat jego ograniczeń ... Dlaczego printf zgłasza błąd we wszystkich kodowych
punktach
13

Bash 4.2 (wydany w 2011) dodano wsparcie dla echo -e '\u0965', printf '\u0965', printf %b '\u0965'a echo $'\u0965'także pracy.

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

o   $'...', echo, and printf understand \uXXXX and \UXXXXXXXX escape sequences.
Lri
źródło
Dzięki ... Nadal używam przede wszystkim bash 4.1.5 w Ubuntu 10.04, ale z pewnością dobrze wiedzieć, że jest teraz dostępny w wersji 4.2. (+1)
Peter.O
1
+1; zwróć uwagę, że w bash 4.2.xwersjach występuje błąd, w którym wartości między 0x80i 0xff( 128 - 255) - tj. w rozszerzonym zakresie ASCII - NIE są poprawnie zakodowane w UTF8 i zamiast tego po prostu przechodzą, w wyniku czego powstają nieprawidłowe znaki UTF8, które są renderowane przez niektóre terminale ?. Od (przynajmniej) 4.3.11zostało to naprawione; jeśli echo $'\ued'renderuje í, błąd nie jest obecny.
mklement0
5

Jeśli masz jądra GNU, spróbuj printf:

$ printf '\u0965\n'

echo może wykonać zadanie, jeśli twoja konsola używa UTF-8 i masz kodowanie UTF-8:

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

Tabelę kodowania szesnastkowego Unicode do UTF-8 można znaleźć tutaj: http://www.utf8-chartable.de/ . Możesz przekonwertować punkty kodu Unicode na heksadecymalne przy użyciu wielu języków skryptowych. Oto przykład z użyciem Pythona:

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

Poniżej znajduje się skrypt Perla, który przekonwertuje argumenty na poprawną wartość szesnastkową (tutaj wiele niepotrzebnych nawiasów):

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

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

Na przykład,

./uni2utf 0965
e0a5a5

Oczywiście, jeśli masz Perla lub Pythona, możesz również użyć ich do wydrukowania znaków.

Steven D.
źródło
Dzięki .. echoNie zrobię tego, co chcę, ponieważ Codepoints to 2-bajtowe UTF-16 Big-Endian .. ale przypomniałeś mi, że są 2 funkcje printf! (Myślałem, że printf może to zrobić i wydaje się, że wzywam niewłaściwy) ... $(which printf)działa ... Dziękuję za przykład w pythonie .. ale za to (moja krzywa uczenia się) staram się trzymać tak blisko jak to tylko możliwe „bash” jako jedyny zaangażowany język. (kiedy czuję się swobodnie z bash, utknę w Pythonie… btw, .encode('hex')jest o krok dalej niż to, czego potrzebuję… trochę tam zajęci :)
Peter.O
Tak, .encode („hex”) miał po prostu uzyskać kod szesnastkowy, który wydawał mi się działać z echem. Cieszę się, że przynajmniej część tego była pomocna.
Steven D
Właśnie widziałem, jak u mnie fragment perla ... dzięki ... dobrze jest mieć te różne rozwiązania przedstawione ... Printf jedno jest dokładnie tym, czego szukałem (jedno polecenie, jak w przykładzie zsh) ... .. Mogę opublikować moją metodę nieużywania innego języka skryptowego , która działa na strumieniu danych szesnastkowych (no \ u itp.) ..
Peter.O
Szczególnie podoba mi się zwięzłość printfpowyższego, ale nie obsługuje wartości poniżej rozwiązania `` \ 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` 'wygląda teraz całkiem nieźle :) .. To pojedyncze wywołanie, a ja po „łatwym pisaniu”, więc dam mu zielony perl
ptaszek
2

UPDATE: Oto sposób bash zrobić pojedynczą wartość Unicode ... (przez "bash" To znaczy: nie stosując dowolny inny język skryptowy) .. dzięki Gilles dla suggeston w tym askubuntu Q / A .
Zgodnie z tym linkiem : recode (Przestarzałe iconv, dos2unix, unix2dos) .. Edytuj: ale zgodnie z komentarzem poniżej „przestarzałe” mogą oznaczać tylko „alternatywę”

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

Oto metoda przetwarzania surowego zrzutu heksadecymalnego jako danych wejściowych (tj. Bez poprzedzających znaków, takich jak; \ u0965 i no \ x09 \ x65) ..
xxdto narzędzie do zrzutów szesnastkowych (w pakiecie vim-common), które może przywrócić surowy zrzut heksadecymalny do znaków, które zrzut reprezentuje ... Punkty kodowe Unicode są UTF-16BigEndian, co jest dokładnie tym, co jest zrzutem szesnastkowym ..
xxdw trybie przywracania przyjmuje strumień wartości szesnastkowych z podziałem linii. które są ignorowane.

Ten skrypt tworzy strumień UTF-16BE, który następnie powraca do oryginalnych znaków.
Ostatni wiersz zawiera dwa potrzebne polecenia; xxdiiconv

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

Oto dane wyjściowe (najpierw przedstawiające dane zrzutu szesnastkowego UTF-16BE).
Uwaga; xxddzieli swój wynik na nowy wiersz z 60 cyframi szesnastkowymi ... Opcja cofania ignoruje te nowe linie .. ignoruje wszystkie / wszystkie nowe linie (ponieważ nie są to cyfry szesnastkowe) ..

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

Matsuo Basho (1644-1694)
  pond
  frog jumps in
  plop!
Peter.O
źródło
Ponieważ wydaje się, że użyłeś informacji pingwina359 w swojej odpowiedzi, możesz rozważyć oznaczenie jego odpowiedzi jako poprawnej zamiast mojej.
Steven D
@Steven D: godny uwagi komentarz, ale „wydaje się” to słowo operacyjne. Używam iconv w ten sposób od kilku dni, co sprawiło, że zastanawiałem się, czy istnieje jedno polecenie. Zrobiłem podobne przetwarzanie całego pliku w systemie Windows (C ++), więc rozumiem Unicode. Naprawdę szukałem szybkiej i prostej bashmetody. Przez „bash” mam na myśli: używanie języka skryptowego bash; nie python / perl z bash). Dodałem to jako odpowiedź, ponieważ może to mieć wartość dla osoby czytającej tę stronę. Jest to dobra jednowierszowa dla całego pliku. Twoja printfodpowiedź jest dla mnie najlepsza.
Peter.O
2
Nie powiedziałbym, że recode przestarzałe iconv, w rzeczywistości recode jest starsze niż iconv, a obecnie iconv jest instalowane znacznie częściej niż recode (na przykład w Linuksie iconv jest prawie zawsze instalowany, ponieważ jest dostarczany z libc).
Gilles „SO- przestań być zły”
Dzięki .. Zastanawiałem się nad tym ... Ta strona internetowa nie jest definitywnym odniesieniem ... więc jest raczej alternatywą ...
Peter.O
1

Zakładając, że domyślnym kodowaniem dla twojego systemu operacyjnego jest UTF-8 (prawda dla większości obecnych dystrybucji), możesz użyć bash bezpośrednio do konwersji dowolnego kodu UNICODE:

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

Oczywiście glif będzie wyświetlany poprawnie tylko wtedy, gdy masz prawidłową czcionkę. Od wersji bash 4.3 wszystkie punkty kodowe będą działać poprawnie. I te dwie wbudowane opcje również będą działać:

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

Zauważ, że dla bash 4.2 punkty Unicode od 0x80do 0xFFsą niepoprawnie zakodowane (błąd bash). Aby obejść ten problem, musisz rzucić okiem na program w tej witrynie (również dobry do dogłębnego zapoznania się z zagadnieniem konwersji liczb na znaki).

HalosGhost
źródło
Działa dla mnie w bash 4.3 i zsh. Czy istnieje raport o błędzie dla bash 4.2, z którym można się połączyć?
Mikel
wygląda mi to na poprawny błąd: https://lists.gnu.org/archive/html/bug-bash/2012-02/msg00035.htmlOpis: \ u i \ U niepoprawnie kodują wartości między \ u80 a \ uff
0

Używanie podstawiania wzorców w wersji bash 4.2 (i wyższej):

${parameter/pattern/string}

jak opisano tutaj 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"}
λ         
ilucent
źródło
1
Zauważ, że, jak stwierdzono w poprzedniej odpowiedzi , działa to tylko w wersji bash 4.2 (i wyższej). W rzeczywistości stanowi to niewiele w porównaniu do wcześniejszej odpowiedzi.
G-Man mówi „Przywróć Monikę”