Je travaille avec la sortie .csv
de cette requête de données SE qui ressemble à ceci (uniquement avec 5022 entrées):
"{
""id"": 281952,
""title"": ""Flash 11.2 No Longer Supported by Google Play""
}"
"{
""id"": 281993,
""title"": ""Netbeans won't open in Ubuntu""
}"
(Et la ligne ^M
se termine entre [nombre] et "" titre ""). J'ai besoin que ça ressemble à ceci:
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
J'ai corrigé cela dans un certain éditeur de texte qui doit rester sans nom assez facilement, mais je voulais créer un script afin de ne pas le refaire à chaque fois que la requête est actualisée et que d'autres puissent l'utiliser. J'ai utilisé sed
...
Cette série de commandes fonctionne parfaitement (bien qu’elle puisse être inefficace; ce n’est qu’une solution empirique):
# Print the ^M and remove them, write to a new file:
cat -v QueryR* | sed 's/\^M//' > QueryNew
# remove all the other junk:
sed -i 's/{//' QueryNew
sed -i 's/}//' QueryNew
sed -i 's/""//g' QueryNew
sed -i 's/^"//' QueryNew
sed -i '/,/{N;/\n.*title:\s/{s/,\n.*title:\s/,\ /}}' QueryNew
sed -i 's/^\s\+//' QueryNew
sed -i '/^\s*$/d' QueryNew
sed -i 's/^id:\ //' QueryNew
sed -i 's/,\ /,/' QueryNew
sed -i 's/\\//g' QueryNew
Alors, pourquoi ça? Seuls les ^M
et {}
sont supprimés et tout le reste est toujours là.
#!/bin/bash
cat -v QueryR* | sed 's/\^M//' > QueryNew
sed -i '{
s/{//
s/}//
s/""//g
s/^"//
/,/{N;/\n.*title:\s/{s/,\n.*title:\s/,\ /}}
s/^\s\+//
/^\s*$/d
s/^id:\ //
s/,\ /,/
s/\\//g
}' QueryNew
Je suis sûr que mon erreur est vraiment évidente ...
Utiliser cat -v
pour transformer les caractères CR en séquences littérales ^M
me semble fondamentalement moche - si vous devez supprimer les fins de ligne DOS, utilisez dos2unix
, tr
ou sed 's/\r$//
'
Si vous insistez pour utiliser sed, alors je vous suggère d’imprimer les bits que vous voulez plutôt que d’essayer de supprimer tous les bits aléatoires que vous ne voulez pas - par exemple
$ sed -rn -e 's/\"//g' -e 's/(.*): (.*)\r/\2/p' QueryR | paste -d '' - -
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
Vous pouvez obtenir une certaine fantaisie et intégrer la suppression de guillemets à l'extraction clé-valeur en faisant correspondre les guillemets à zéro ou plus à chaque fin de la séquence de valeurs.
$ sed -rn 's/(.*): \"*([^"]*)\"*\r/\2/p' QueryR | paste -d '' - -
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
Vous pouvez obtenir vraiment des fantaisies et émuler la paste
dans sed
en joignant d’abord des paires de lignes sur le ,\r$
se terminant puis en faisant correspondre les paires clé-valeur se multiplient (g
) et de manière non-gourmande
$ sed -rn '/,\r$/ {N; s/([^:]*): \"*([^:"]*)\"*\r\n?/\2/gp}' QueryR
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
(Personnellement, je préférerais l’approche KISS et utiliserais le premier).
FWIW, puisque votre entrée semble trop compter sur JSON, je suggérerais d'installer un analyseur JSON approprié tel que jq
Sudo apt-get install jq
Vous pouvez ensuite faire quelque chose comme
$ sed -e 's/["]["]/"/g' -e 's/"{/{/' -e 's/}"/}/' QueryR | jq '.id, .title' | paste -d, - -
281952,"Flash 11.2 No Longer Supported by Google Play"
281993,"Netbeans won't open in Ubuntu"
qui supprime les guillemets superflus et utilise ensuite jq
pour extraire les champs d’intérêt. Notez que jq
semble gérer les fins de ligne de style DOS. Il n’est donc pas nécessaire de prendre des mesures spéciales pour les supprimer.
Passez à jq '.[]'
pour vider toutes les paires attribut-valeur.
Crédit pour l'inspiration et la syntaxe de base jq
tirée de Surmonter des lignes nouvelles avec grep -o
Je l'ai réparé grâce à Steeldriver & bricoler davantage. Non raffiné mais fonctionne.
sed '{
s/"{//
s/}"//
s/^"//
/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,/}}
s/""//g
s/^\s\+//
/^\s*$/d
s/^id:\ //
s/\\//g
}' QueryR* | tee "$1"
traduction:s/"{//
Supprimer "{
s/}"//
Supprimer }"
s/^"//
Supprimer "
du début de la ligne/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,\ /}}
fait correspondre ,\r
sur une ligne et [whatever]title[whatever]:
sur la ligne suivante, remplace tout cela par ,
s/""//g
Supprimer tous les guillemets doubles restants.s/^\s\+//
Supprimer les espaces du début des lignes/^\s*$/d
Supprimer les lignes videss/^id:\ //
Supprimez id:
et espacez-les/\\//g
Supprimer les barres obliques inverses (caractères d'échappement pour "ajoutés à certains champs de titre)tee "$1"
spécifiez un fichier de sortie lors de l'exécution du script, par exemple ./queryclean newquery.csv
Bien que la question demande sed
, on pourrait contourner les problèmes de sed avec Python:
from __future__ import print_function
import sys
with open(sys.argv[1]) as f:
for line in f:
if '""id""' in line:
print(line.strip().split(':')[1],end="")
if '""title""' in line:
title = " ".join(line.strip().split(':')[1:])
print(title.replace('""'," "))
Ce code est compatible avec python2 et python3, donc cela fonctionnera
Échantillon échantillon:
bash-4.3$ cat questions.txt
"{
""id"": 281952,
""title"": ""Flash 11.2 No Longer Supported by Google Play""
}"
"{
""id"": 281993,
""title"": ""Netbeans won't open in Ubuntu""
}"
bash-4.3$ python3 parse_questions.py questions.txt
281952, Flash 11.2 No Longer Supported by Google Play
281993, Netbeans won't open in Ubuntu
Trois autres approches:
awk
$ awk -F'": ' '/\"id\"/{id=$NF;}
/\"title\"/{
t=$NF;
sub(/^""/,"",t);
sub(/""$/,"",t);
print id,t
}' OFS="" file
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
Perl
$ Perl -lne '$id=$1 if /id"":\s*(\d+)/;
if(/title"":\s*""(.*)""/){print "$id,$1"}' file
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
GNU grep avec les expressions rationnelles compatibles Perl et le langage Perl simple:
$ grep -oP '(id"":\s*\K.*)|(title"":\s*""\K.*(?=""))' file |
Perl -pe 'chomp if $.%2'
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
Ceci est un autre script écrit en Ruby. Il conservera les virgules dans le titre, ce qui peut être facilement importé dans n’importe quel tableur sans casser les colonnes.
csvfile = File.open('query-fixed.csv', 'w')
File.open('QueryResults2.csv') do |f|
content = f.read
content.gsub!(/\r\n?/, "\n")
content.each_line do |line|
id, title = '', ''
if line.match('\"id\"')
id = line.split(':')[1].strip[0..-2]
csvfile.write(id + ',')
end
if line.match('\"title\"')
title = line.partition(':')[2].scan(/"(.*)"/)[0][0]
csvfile.write(title + "\n")
end
end
end
Après l'exécution du programme, la sortie produite ressemblera à ceci
281952,"Flash 11.2 No Longer Supported by Google Play"
281993,"Netbeans won't open in Ubuntu"