web-dev-qa-db-ja.com

* nixのコンソールツールを使用して\ uXXXXユニコードをUTF-8に変換する方法

curlを使用してURL応答を取得します。これはJSON応答であり、\u0144 (ń)および\u00f3 (ó)

それらをTF-8または他のエンコーディングに変換してファイルに保存するにはどうすればよいですか?

47
Krzysztof Wolny

使用しているディストリビューションはわかりませんが、ni2asciiを含める必要があります。

$ Sudo apt-get install uni2ascii

Libc6のみに依存しているため、軽量なソリューションです(uni2ascii i386 4.18-2はUbuntuで55,0 kB)!

それを使用するには:

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

少しいかもしれませんが、echo -eはそれを行う必要があります。

echo -en "$(curl $URL)"

-eはエスケープを解釈し、-nは改行echoが通常追加することを抑制します。

注:\uエスケープは、bash組み込みechoで機能しますが、/usr/bin/echoでは機能しません。

コメントで指摘したように、これはbash 4.2+であり、4.2.xには0x00ff/17値(0x80-0xff)を処理するバグがあります。

40
Kevin

JDKのnative2asciiが最適な方法であることがわかりました。

native2ascii -encoding UTF-8 -reverse src.txt dest.txt

詳細な説明はこちら: http://docs.Oracle.com/javase/1.5.0/docs/tooldocs/windows/native2ascii.html

更新:JDK9以降では使用できなくなりました: https://bugs.openjdk.Java.net/browse/JDK-8074431

36
Krzysztof Wolny

\uの後に必ず4桁の16進数が続くと仮定します。

#!/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桁の16進数を対応する文字に置き換えます。 e接尾辞を使用すると、置換は文字列として扱われるのではなく、式として評価されます。 gは、最初のオカレンスではなく、すべてのオカレンスを置き換えることを示します。

上記を$PATHのどこかにあるファイルに保存できます(chmod +xを忘れないでください)。標準入力(またはコマンドラインで指定された1つ以上のファイル)を標準出力にフィルターします。

繰り返しますが、これは、表現が常に\uの後に正確に4桁の16進数が続くことを前提としています。そのように表現できるよりも多くのUnicode文字がありますが、\u12345はUnicode文字0x1234(ETHIOPIC SYLLABLE SEE)の後に数字5が続くことを想定しています。

C構文では、汎用文字名は、\uに4桁の16進数が続くか、\Uに8桁の16進数が続くかのいずれかです。 JSONレスポンスが同じスキームを使用しているかどうかわかりません。基本的な多言語面(最初の2つ)以外でUnicode文字をエンコードする方法(またはどうか)を見つける必要があります。16 文字)。

20
Keith Thompson

正規表現に依存しないでください。JSONには、\uエスケープと非BMPコードポイントを持つ奇妙なコーナーケースがあります。 (具体的には、JSONはtwo\uエスケープを使用して1つのコードポイントをエンコードします)1つのエスケープシーケンスが1つのコードポイントに変換されると仮定すると、そのようなテキストは運命づけられます。

選択した言語の完全なJSONパーサーを使用すると、はるかに堅牢になります。

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

これは実際にこの短いpythonスクリプトにデータを供給するだけです。

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"
11
Thanatos

つかいます /usr/bin/printf "\u0160ini\u010di Ho\u0161i - A\u017e sa skon\u010d\u00ed zima"適切なunicodeからutf8への変換を取得します。

9
andrej

今、私は最高の答えを持っています! jq を使用

Windows:

type in.json | jq > out.json

ルニックス:

cat in.json | jq > out.json

Perl/pythonを使用した回答よりも確実に高速です。パラメータなしでjsonをフォーマットし、\ uXXXXをutf8に変換します。 JSONクエリの実行にも使用できます。非常に素晴らしいツール!

5
Smit Johnth

序文:この質問に対する推奨された回答は、telegram-bot-bashの 長年の問題 を解決しませんでした。 Thanatos のpythonソリューションのみが機能しました!

これは、JSONが2つの\ uエスケープを使用して1つのコードポイントをエンコードするためです


ここにありますtwoecho -eおよびprintf '%s'

[〜#〜] pure [〜#〜]関数としてのbashバリアント。スクリプトの上部に貼り付け、それを使用してbashでJSON文字列をデコードします。

#!/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

[〜#〜] mixed [〜#〜]ソリューションとThanatosのPhytonバリアント:

# 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"))'
 }

-


他の昇進したsoutionsを提唱する人のためのテストケースが機能します:

# 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
1
Kay Marquardt
iconv -f Unicode fullOrders.csv > fullOrders-utf8.csv
0
Tanguy

POSIXで義務付けられているb変換指定子を使用します。

追加の変換指定文字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

私のLinuxディストリビューション(Fedora 29)で、bashの組み込みprintf/usr/bin/printfの両方で正常にテストされました。


UPDATE 2019-04-17:私のソリューションでは、\uxxxx\UxxxxxxxxなどのUnicodeエスケープを想定しています。後者は、BMPを超えるUnicode文字に必要です。ただし、OPの質問にはJSONストリームが関係していました。 JSONのUnicodeエスケープシーケンスはUTF16を使用しますが、これにはBMP以外のサロゲートペアが必要です。

ユニコード文字を検討してください???? ( 「笑顔で顔を微笑む」(U + 1F601) )。この文字の\Uエスケープシーケンスは\U0001F601です。以下のようにPOSIXで義務付けられた%b指定子を使用して印刷できます。

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

ただし、JSONでは、この文字のエスケープシーケンスにはUTF16サロゲートペアが含まれます:\uD83D\uDE01

シェルレベルでJSONストリームを操作するには、jqツールが優れています。

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

したがって、私は今、私の答えを検討から撤回し、jqを最良の答えとして使用するというスミット・ジョンスの答えを支持します。

0
Robin A. Meade