web-dev-qa-db-fra.com

Comment convertir\uXXXX unicode en UTF-8 à l'aide des outils de la console sous * nix

J'utilise curl pour obtenir une réponse d'URL, c'est une réponse JSON et il contient des caractères nationaux échappés par Unicode tels que\u0144 (ń)et\u00f3 (ó)

Comment puis-je les convertir en UTF-8 ou en un autre encodage à enregistrer dans un fichier?

39
Krzysztof Wolny

Je ne sais pas quelle distribution vous utilisez, mais uni2ascii devrait être inclus.

$ Sudo apt-get install uni2ascii

Cela dépend uniquement de libc6, donc c'est une solution légère (uni2ascii i386 4.18-2 est de 55,0 ko sur Ubuntu)!

Puis l'utiliser:

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

Peut-être un peu moche, mais echo -e devrait le faire:

echo -en "$(curl $URL)"

-e interprète les échappements, -n supprime la nouvelle ligne que echo ajouterait normalement.

Remarque: L’échappement \u fonctionne dans echo, mais pas /usr/bin/echo.

Comme indiqué dans les commentaires, il s’agit de bash 4.2+, et 4.2.x a un bogue gérant les valeurs 0x00ff/17 (0x80-0xff).

37
Kevin

J'ai trouvé native2ascii de JDK comme le meilleur moyen de le faire:

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

La description détaillée est ici: http://docs.Oracle.com/javase/1.5.0/docs/tooldocs/windows/native2ascii.html

Mise à jour: Plus disponible depuis la JDK9: https://bugs.openjdk.Java.net/browse/JDK-8074431

29
Krzysztof Wolny

En supposant que le \u est toujours suivi d’exactement 4 chiffres hexadécimaux:

#!/usr/bin/Perl

use strict;
use warnings;

binmode(STDOUT, ':utf8');

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

La binmode met la sortie standard en mode UTF-8. La commande s... remplace chaque occurrence de \u suivie de 4 chiffres hexadécimaux par le caractère correspondant. Le suffixe e fait en sorte que le remplacement soit évalué comme une expression plutôt que comme une chaîne; le g dit de remplacer toutes les occurrences plutôt que la première.

Vous pouvez enregistrer ce qui précède dans un fichier quelque part dans votre $PATH (n'oubliez pas le chmod +x). Il filtre l'entrée standard (ou un ou plusieurs fichiers nommés sur la ligne de commande) en sortie standard.

20
Keith Thompson

utilisez /usr/bin/printf "\u0160ini\u010di Ho\u0161i - A\u017e sa skon\u010d\u00ed zima" pour obtenir une conversion correcte unicode en utf8.

9
andrej

Ne comptez pas sur les expressions rationnelles: JSON a des cas étranges avec des échappements \u et des points de code non-BMP. (Plus précisément, JSON encodera un point de code en utilisant deux \u échappements) Si vous supposez qu'une séquence d'échappement est traduite en 1 point de code, vous êtes condamné pour ce texte.

L'utilisation d'un analyseur JSON complet dans la langue de votre choix est considérablement plus robuste:

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

Il s’agit en réalité d’alimenter les données dans ce court script 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'))

À partir duquel vous pouvez enregistrer en tant que foo.py et appeler en tant que curl ... | foo.py

Un exemple qui va casser la plupart des autres tentatives de cette question est "\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"
9
Thanatos

Préface: Aucune des réponses promues à cette question n'a résolu un problème de longue date dans telegram-bot-bash. Seule la solution python de Thanatos a fonctionné!

C’est parce que JSON va encoder un point de code en utilisant deux échappements


Vous trouverez ici deux remplaçants pour echo -e et printf '%s'

PURE variante de bash en tant que fonction. collez sur le dessus de votre script et utilisez-le pour décoder vos chaînes JSON dans 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

SOLUTION _ MÉLANGÉE avec la variante de Phyton de 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"))'
 }

-


Le test case pour ceux qui défendent les autres soutions promues fonctionnera:

# 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

Comme vous pouvez facilement voir la sortie est différente. les autres solutions promues génèrent le même résultat incorrect pour les chaînes JSON que 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

maintenant j'ai la meilleure réponse! Utilisez jq

Les fenêtres:

type in.json | jq > out.json

Lunix:

cat in.json | jq > out.json

C'est sûrement plus rapide que toute réponse utilisant Perl/python. Sans paramètres, il formate le JSON et convertit\uXXXX en utf8. Il peut également être utilisé pour effectuer des requêtes JSON. Très bel outil!

1
Smit Johnth
iconv -f Unicode fullOrders.csv > fullOrders-utf8.csv
0
Tanguy

Utilisez le spécificateur de conversion b mandaté par POSIX:

Un caractère de spécificateur de conversion supplémentaire, b, doit être pris en charge comme suit. L'argument doit être considéré comme une chaîne pouvant contenir des séquences d'échappement avec une barre oblique inversée.
- http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

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

Tester:

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?

REMARQUE: Si vous supprimez le spécificateur de format %b, le signe de pourcentage provoquera une erreur telle que:

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

Testé avec succès avec les variables printf et /usr/bin/printf intégrées de bash sur ma distribution linux (Fedora 29).

0
Robin A. Meade