如何使用* nix中的控制台工具将\ uXXXX unicode转换为UTF-8

时间:2012-01-09 21:53:09

标签: linux json unix unicode encoding

我使用curl获取一些网址响应,它是JSON响应,它包含unicode转义的国家字符,例如 \u0144 (ń) \u00f3 (ó) < / strong>即可。

如何将它们转换为 UTF-8 或任何其他编码以保存到文件中?

11 个答案:

答案 0 :(得分:38)

可能有点难看,但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)。

答案 1 :(得分:33)

我发现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

答案 2 :(得分:30)

我不知道您使用的是哪种发行版,但应包含 uni2ascii

$ sudo apt-get install uni2ascii

它只依赖于libc6,所以它是一个轻量级的解决方案(un2ascii i386 4.18-2在Ubuntu上是55 kB)!

然后使用它:

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

答案 3 :(得分:19)

假设\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 字符)。

答案 4 :(得分:10)

不要依赖正则表达式:JSON有一些带有\u转义和非BMP代码点的奇怪转角情况。 (具体来说,JSON将使用两个 \u转义编码一个代码点。如果假设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"

答案 5 :(得分:8)

使用/usr/bin/printf "\u0160ini\u010di Ho\u0161i - A\u017e sa skon\u010d\u00ed zima"进行正确的unicode-to-utf8转换。

答案 6 :(得分:3)

现在我有了最好的答案!使用jq

Windows:

type in.json | jq > out.json

Lunix:

cat in.json | jq > out.json

使用perl / python的答案肯定更快。如果没有参数,它将格式化json并将\ uXXXX转换为utf8。它也可以用于执行json查询。很好的工具!

答案 7 :(得分:1)

  

序言:对该问题的提升答案中没有一个解决了Telegram-bot-bash中的longstanding issue问题。只有Thanatos的python解决方案有效!

     

这是因为JSON将使用两个\ u转义符对一个代码点进行编码


在这里您会找到{strong>两个来代替echo -eprintf '%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
具有Thanatos Phyton变体的

MIXED 解决方案:

# 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

答案 8 :(得分:-1)

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

答案 9 :(得分:-1)

使用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

与两个bash的内置测试成功printf/usr/bin/printf在我的Linux发行版(Fedora的29)。


<强> UPDATE 2019年4月17日:我的解决办法假定Unicode转义像\uxxxx\Uxxxxxxxx; BMP以外的unicode字符需要后者。然而,OP的问题涉及到一个JSON流。 JSON的Unicode转义序列使用UTF16,这需要超过所述BMP代理对

考虑Unicode字符('GRINNING FACE WITH SMILING EYES' (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 

因此我现在考虑撤回我的回答并赞同使用的斯密特Johnth的回答jq为最佳答案。

答案 10 :(得分:-2)

适用于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()