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?
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: ó
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).
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
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.
utilisez /usr/bin/printf "\u0160ini\u010di Ho\u0161i - A\u017e sa skon\u010d\u00ed zima"
pour obtenir une conversion correcte unicode en utf8.
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"
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
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!
iconv -f Unicode fullOrders.csv > fullOrders-utf8.csv
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).