Подтвердить что ты не робот

Как конвертировать Unicode\uXXXX в UTF-8 с помощью консольных инструментов в * nix

Я использую curl, чтобы получить некоторый ответ по URL-адресу, его ответ JSON и содержит национальные символы с символом Unicode, такие как \u0144 (ń) и \u00f3 (ó).

Как преобразовать их в UTF-8 или любую другую кодировку для сохранения в файл?

4b9b3361

Ответ 1

Я не знаю, какое распределение вы используете, но uni2ascii должен быть включен.

$ sudo apt-get install uni2ascii

Это зависит только от libc6, поэтому это облегченное решение (uni2ascii i386 4.18-2 - 55,0 kB на Ubuntu)!

Затем, чтобы использовать его:

$ echo 'Character 1: \u0144, Character 2: \u00f3' | ascii2uni -a U -q
Character 1: ń, Character 2: ó

Ответ 2

Может быть немного уродливым, но echo -e должен это сделать:

echo -en "$(curl $URL)"

-e интерпретирует escape-последовательности, -n обычно подавляет новую строку echo.

Примечание. Побег \u работает в bash встроенном echo, но не /usr/bin/echo.

Как указано в комментариях, это bash 4.2+, а 4.2.x имеют ошибку с ошибками 0x00ff/17 (0x80-0xff).

Ответ 4

Предполагая, что за \u всегда следуют ровно 4 шестнадцатеричные цифры:

#!/usr/bin/perl

use strict;
use warnings;

binmode(STDOUT, ':utf8');

while (<>) {
    s/\\u([0-9a-fA-F]{4})/chr(hex($1))/eg;
    print;
}

binmode переводит стандартный вывод в режим UTF-8. Команда s... заменяет каждое вхождение \u за которым следуют 4 шестнадцатеричных цифры, соответствующим символом. Суффикс e приводит к тому, что замена будет оцениваться как выражение, а не как строка; g говорит заменить все вхождения, а не только первое.

Вы можете сохранить вышеупомянутое в файл где-нибудь в вашем $PATH (не забудьте chmod +x). Он фильтрует стандартный ввод (или один или несколько файлов, названных в командной строке) для стандартного вывода.

Опять же, это предполагает, что за представлением всегда \u следуют ровно 4 шестнадцатеричные цифры. Есть больше символов Unicode, чем можно представить таким образом, но я предполагаю, что \u12345 будет обозначать символ Unicode 0x1234 (ETHIOPIC SYLLABLE SEE), за которым следует цифра 5.

В синтаксисе C универсальным символьным именем является либо \u за которым следуют ровно 4 шестнадцатеричные цифры, либо \U за которыми следуют ровно 8 шестнадцатеричных цифр. Я не знаю, используют ли ваши ответы JSON ту же схему. Вы, вероятно, должны выяснить, как (или ли) он кодирует символы Unicode вне базовой многоязычной плоскости (первые 2 16 символов).

Ответ 5

Не полагайтесь на регулярные выражения: JSON имеет некоторые странные угловые шкалы с \u экранами и кодами без BMP. (в частности, JSON будет кодировать одну кодовую точку с помощью двух экранов \u). Если вы предположите, что 1 escape-последовательность переводится в 1 кодовую точку, вы обречены на такой текст.

Использование полного анализатора JSON на выбранном вами языке значительно более устойчиво:

$ echo '["foo bar \u0144\n"]' | python -c 'import json, sys; sys.stdout.write(json.load(sys.stdin)[0].encode("utf-8"))'

Это действительно просто подает данные на этот короткий питон script:

import json
import sys

data = json.load(sys.stdin)
data = data[0] # change this to find your string in the JSON
sys.stdout.write(data.encode('utf-8'))

Из которого вы можете сохранить как foo.py и позвонить как curl ... | foo.py

Пример, который нарушит большинство других попыток в этом вопросе: "\ud83d\udca3":

% printf '"\\ud83d\\udca3"' | python2 -c 'import json, sys; sys.stdout.write(json.load(sys.stdin)[0].encode("utf-8"))'; echo
💣
# echo will result in corrupt output:
% echo -e $(printf '"\\ud83d\\udca3"') 
"������"
# native2ascii won't even try (this is correct for its intended use case, however, just not ours):
% printf '"\\ud83d\\udca3"' | native2ascii -encoding utf-8 -reverse
"\ud83d\udca3"

Ответ 6

используйте /usr/bin/printf "\u0160ini\u010di Ho\u0161i - A\u017e sa skon\u010d\u00ed zima", чтобы получить правильное преобразование unicode-to-utf8.

Ответ 7

Теперь у меня есть лучший ответ! Используйте JQ

Окна:

type in.json | jq > out.json

Lunix:

cat in.json | jq > out.json

Это, безусловно, быстрее, чем любой ответ, использующий perl/python. Без параметров он форматирует JSON и конвертирует \uXXXX в utf8. Он также может быть использован для выполнения запросов JSON. Очень хороший инструмент!

Ответ 8

Предисловие: Ни один из предложенных ответов на этот вопрос не решил давнюю проблему в telegram-bot-bash. Работало только решение Python от Thanatos !

Это связано с тем, что JSON будет кодировать одну кодовую точку, используя два перехода \u


Здесь вы найдете две замены для echo -e и printf '%s'

Чисто bash вариант как функция. вставьте верхнюю часть вашего скрипта и используйте его для декодирования строк JSON в bash:

#!/bin/bash
#
# pure bash implementaion, done by KayM (@gnadelwartz)
# see https://stackoverflow.com/a/55666449/9381171
  JsonDecode() {
     local out="$1"
     local remain=""   
     local regexp='(.*)\\u[dD]([0-9a-fA-F]{3})\\u[dD]([0-9a-fA-F]{3})(.*)'
     while [[ "${out}" =~ $regexp ]] ; do
           # match 2 \udxxx hex values, calculate new U, then split and replace
           local W1="$(( ( 0xd${BASH_REMATCH[2]} & 0x3ff) <<10 ))"
           local W2="$(( 0xd${BASH_REMATCH[3]} & 0x3ff ))"
           U="$(( ( W1 | W2 ) + 0x10000 ))"
           remain="$(printf '\\U%8.8x' "${U}")${BASH_REMATCH[4]}${remain}"
           out="${BASH_REMATCH[1]}"
     done
     echo -e "${out}${remain}"
  }

# Some tests ===============
$ JsonDecode 'xxx \ud83d\udc25 xxxx' -> xxx 🐥 xxxx
$ JsonDecode '\ud83d\udc25' -> 🐥
$ JsonDecode '\u00e4 \u00e0 \u00f6 \u00f4 \u00fc \u00fb \ud83d\ude03 \ud83d\ude1a \ud83d\ude01 \ud83d\ude02 \ud83d\udc7c \ud83d\ude49 \ud83d\udc4e \ud83d\ude45 \ud83d\udc5d \ud83d\udc28 \ud83d\udc25 \ud83d\udc33 \ud83c\udf0f \ud83c\udf89 \ud83d\udcfb \ud83d\udd0a \ud83d\udcec \u2615 \ud83c\udf51'
ä à ö ô ü û 😃 😚 😁 😂 👼 🙉 👎 🙅 👝 🐨 🐥 🐳 🌏 🎉 📻 🔊 📬 ☕ 🍑

# decode 100x string with 25 JSON UTF-16 vaules
$ time for x in $(seq 1 100); do JsonDecode '\u00e4 \u00e0 \u00f6 \u00f4 \u00fc \u00fb \ud83d\ude03 \ud83d\ude1a \ud83d\ude01 \ud83d\ude02 \ud83d\udc7c \ud83d\ude49 \ud83d\udc4e \ud83d\ude45 \ud83d\udc5d \ud83d\udc28 \ud83d\udc25 \ud83d\udc33 \ud83c\udf0f \ud83c\udf89 \ud83d\udcfb \ud83d\udd0a \ud83d\udcec \u2615 \ud83c\udf51' >/dev/null ; done

real    0m2,195s
user    0m1,635s
sys     0m0,647s

СМЕШАННОЕ решение с вариантом Phyton от Thanatos:

# usage: JsonDecode "your bash string containing \uXXXX extracted from JSON"
 JsonDecode() {
     # wrap string in "", replace " by \"
     printf '"%s\\n"' "${1//\"/\\\"}" |\
     python -c 'import json, sys; sys.stdout.write(json.load(sys.stdin).encode("utf-8"))'
 }

-


Тест для тех, кто защищает другие продвигаемые решения, будет работать:

# test='😁 😘 ❤️ 😊 👍' from JSON
$ export test='\uD83D\uDE01 \uD83D\uDE18 \u2764\uFE0F \uD83D\uDE0A \uD83D\uDC4D'

$ printf '"%s\\n"' "${test}" | python -c 'import json, sys; sys.stdout.write(json.load(sys.stdin).encode("utf-8"))' >phyton.txt
$ echo -e "$test" >echo.txt

$ cat -v phyton.txt
M-pM-^_M-^XM-^A M-pM-^_M-^XM-^X M-bM-^]M-$M-oM-8M-^O M-pM-^_M-^XM-^J M-pM-^_M-^QM-^M

$ cat -v echo.txt
M-mM- M-=M-mM-8M-^A M-mM- M-=M-mM-8M-^X M-bM-^]M-$M-oM-8M-^O M-mM- M-=M-mM-8M-^J M-mM- M-=M-mM-1M-^M

Как вы можете легко увидеть, выходной отличается. другие продвигаемые решения предоставляют тот же неверный вывод для строк JSON, что и echo -e:

$ ascii2uni -a U -q >uni2ascii.txt <<EOF
$test
EOF

$ cat -v uni2ascii.txt
M-mM- M-=M-mM-8M-^A M-mM- M-=M-mM-8M-^X M-bM-^]M-$M-oM-8M-^O M-mM- M-=M-mM-8M-^J M-mM- M-=M-mM-1M-^M

$ printf "$test\n" >printf.txt
$ cat -v printf.txt
M-mM- M-=M-mM-8M-^A M-mM- M-=M-mM-8M-^X M-bM-^]M-$M-oM-8M-^O M-mM- M-=M-mM-8M-^J M-mM- M-=M-mM-1M-^M

$ echo "$test" | iconv -f Unicode >iconf.txt                                                                                     

$ cat -v iconf.txt
M-gM-^UM-^\M-cM-!M-^DM-dM-^PM-3M-gM-^UM-^\M-dM-^UM-^DM-cM-^DM-0M-eM-0M- M-dM-^QM-5M-cM-^LM-8M-eM-1M-^DM-dM-^QM-5M-cM-^EM-^EM-bM-^@M-8M-gM-^UM-^\M-cM-^\M-2M-cM-^PM-6M-gM-^UM-^\M-dM-^UM-^FM-dM-^XM-0M-eM-0M- M-dM-^QM-5M-cM-^LM-8M-eM-1M-^DM-dM-^QM-5M-cM-^AM-^EM-bM-^AM-^AM-gM-^UM-^\M-cM-!M-^DM-dM-^PM-3M-gM-^UM-^\M-dM-^MM-^DM-dM-^PM-4r

Ответ 9

iconv -f Unicode fullOrders.csv > fullOrders-utf8.csv

Ответ 10

Используйте спецификатор преобразования b предписанный POSIX:

Дополнительный символ спецификатора преобразования b должен поддерживаться следующим образом. Аргумент должен быть принят за строку, которая может содержать последовательности с обратной косой чертой.
- http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

expand_escape_sequences() {
  printf %b "$1"
}

Тестовое задание:

s='\u0160ini\u010di Ho\u0161i - A\u017e sa skon\u010d\u00ed zima A percent sign % OK?'
expand_escape_sequences "$s"

# output: Šiniči Hoši - Až sa skončí zima A percent sign % OK?

ПРИМЕЧАНИЕ. Если вы удалите спецификатор формата %b, знак процента вызовет ошибку, такую как:

-bash: printf: 'O': invalid format character

Успешно протестировано с использованием встроенной команды bash printf и /usr/bin/printf в моем дистрибутиве Linux (Fedora 29).


ОБНОВЛЕНИЕ 2019-04-17: Мое решение предполагало, что Юникод экранируется как \uxxxx и \Uxxxxxxxx; последний требуется для символов Юникода за пределами BMP. Тем не менее, вопрос OP был связан с потоком JSON. В escape-последовательностях Юникода JSON используется UTF16, для которого требуются суррогатные пары за пределами BMP.

Рассмотрим символ Юникода 😁 ("УХВАТЫВАЯ ЛИЦО С УЛЫБАЮЩИМИ ГЛАЗАМИ" (U + 1F601)). \U0001F601 последовательность \U для этого символа: \U0001F601. Вы можете напечатать его, используя обязательный спецификатор %b POSIX:

printf %b '\U0001F601'
# Prints 😁 as expected

Однако в JSON escape-последовательность для этого символа включает суррогатную пару \uD83D\uDE01: \uD83D\uDE01

Для работы с потоками JSON на уровне jq инструмент jq превосходен:

echo '["\uD83D\uDE01"]' | jq .
# Prints ["😁"] as expected 

Таким образом, теперь я снимаю свой ответ с рассмотрения и одобряю ответ Смита Джона на использование jq в качестве наилучшего ответа.

Ответ 11

Работает в Windows, также должен работать на * nix. Использует python 2.

#!/usr/bin/env python
from __future__ import unicode_literals
import sys
import json
import codecs

def unescape_json(fname_in, fname_out):
    with file(fname_in, 'rb') as fin:
        js = json.load(fin)
    with codecs.open(fname_out, 'wb', 'utf-8') as fout:
        json.dump(js, fout, ensure_ascii=False)

def usage():
    print "Converts all \\uXXXX codes in json into utf-8"
    print "Usage: .py infile outfile"
    sys.exit(1)

def main():
    try:
        fname_in, fname_out = sys.argv[1:]
    except Exception:
        usage()

    unescape_json(fname_in, fname_out)
    print "Done."

if __name__ == '__main__':
    main()