
Ich habe einen Datensatz, der Kontaktinformationen von Studenten enthält. Der Beispieldatensatz sieht wie folgt aus:
First Name, Last Name, Address, Phone Number
John, Doe, "House # 11, Street xyz, Road, Area",00000000
Sara, Taylor, "Jake Lake%, Apartment #22, Main Road, Area XYZ", 00000000
Ich führe den folgenden Befehl aus, um ihn zu ersetzen,in der Adressspalte an|um es in die Datenbank zu laden.
awk '!(NR%2){gsub(",","|")} {printf RFS $0} {RFS="\""}' RS=\" fileName.txt > output.txt
Das Problem, mit dem ich konfrontiert bin, ist, dass jedes Mal, wenn ich diesen Befehl ausführe, der folgende Fehler zurückgegeben wird: Zunächst lief es einwandfrei
awk: run time error: not enough arguments passed to printf(""Jake Lake%, Apartment #22, Main Road, Area XYZ")
Gibt es dafür eine Lösung? Mir ist aufgefallen, dass%kommt in der Adresse, ist das das Problem?
Antwort1
- Aus Gründen der Robustheit sollten Sie niemals
printf $0
verwenden, sondern immerprintf "%s", $0
stattdessen, da ersteres fehlschlägt, wenn Ihre Eingabeprintf
Formatierungszeichen enthält (wie Sie gerade sehen). Dasselbe gilt für die Verwendungprintf
mit beliebigen Eingabedaten. - Verwenden Sie aus Gründen der Übersichtlichkeit und Robustheit niemals Variablennamen ausschließlich in Großbuchstaben,
RFS
um beispielsweise Konflikte mit integrierten Variablennamen zu vermeiden und Ihren Code nicht zu verschleiern, indem Sie den Eindruck erwecken, Sie würden eine integrierte Variable verwenden, obwohl dies nicht der Fall ist. - Legen Sie aus Gründen der Lesbarkeit keine Variablen, z. B.
RS
, nach Ihrem Skript fest, es sei denn, Sie müssen sie für unterschiedliche Eingabedateien auf unterschiedliche Werte festlegen. Legen Sie Variablen vor oder am Anfang Ihres Skripts fest, damit wir beim Lesen Ihres Skripts sehen, dass sie festgelegt werden, bevor wir sehen, dass sie verwendet werden. - Aus Gründen der Effizienz, Einfachheit und Robustheit ist das erste Argument für *sub() ein regulärer Ausdruck und kein String. Verwenden Sie daher darum herum den regulären Ausdruck (
/.../
) und nicht den String ("..."
), es sei denn, Sie BRAUCHEN aus irgendeinem Grund einen dynamischen statt eines statischen regulären Ausdrucks. - Wenn Sie zwei Variablen haben, die denselben Wert haben müssen, z. B.
RS
undRFS
, sollten Sie diese aus Gründen der Übersichtlichkeit und Wartbarkeit nicht separat auf denselben Wert setzen, z. B.RS="\""; RFS="\""
, sondern entweder gemeinsam auf diesen Wert, z. B. ,RS=RFS="\""
oder eine auf den anderen, z. B.RS="\""; RFS=RS
.
So schreiben Sie den Code in Ihrer Frage richtig:
$ awk -v RS='"' '!(NR%2){gsub(/,/,"|")} {printf "%s%s", rfs, $0; rfs=RS}' file
First Name, Last Name, Address, Phone Number
John, Doe, "House # 11| Street xyz| Road| Area",00000000
Sara, Taylor, "Jake Lake%| Apartment #22| Main Road| Area XYZ", 00000000
Um mehr als das mit einer CSV-Datei unter Verwendung von awk zu tun, sieheWas ist der robusteste Weg, CSV mithilfe von AWK effizient zu analysieren?.
Antwort2
Der Fehler, den Sie erhalten, ist auf die Verwendung der Verkettung des Werts von RFS
(einer leeren Variable) und $0
als Formatzeichenfolge mit zurückzuführen printf
.
Ihre Datei ist eine gültige CSV-Datei, abgesehen von Leerzeichen nach einigen der Trennkommas (was die Anführungszeichen des Address
Felds durcheinander bringt; ein Feld in Anführungszeichen muss das erste Anführungszeichen direkt nach dem Trennzeichen haben). Wir können dies korrigieren, indem wir csvformat
(Teil von csvkit vonhttps://csvkit.readthedocs.io/en/latest/):
$ csvformat --skipinitialspace file.csv >fixed-file.csv
$ cat fixed-file.csv
First Name,Last Name,Address,Phone Number
John,Doe,"House # 11, Street xyz, Road, Area",00000000
Sara,Taylor,"Jake Lake%, Apartment #22, Main Road, Area XYZ",00000000
Eine Datenbank, die CSV analysieren kann, sollte dies so lesen können, wie es ist.
Möchten Sie dennoch alle eingebetteten Kommas durch ersetzen |
, ändern Sie einfach das Dateitrennzeichen in etwas anderes als ein Komma (ich verwende im Folgenden Tabulatoren), ändern Sie alle verbleibenden Kommas in Pipes und verwenden Sie anschließend wieder Kommas als Trennzeichen.
Wir können dies direkt mit den Originaldaten tun:
$ csvformat --skipinitialspace --out-tabs file.csv | tr ',' '|' | csvformat --tabs >fixed-file.csv
$ cat fixed-file.csv
First Name,Last Name,Address,Phone Number
John,Doe,House # 11| Street xyz| Road| Area,00000000
Sara,Taylor,Jake Lake%| Apartment #22| Main Road| Area XYZ,00000000
Die Kurzvarianten der verschiedenen verwendeten Langoptionen sind -S
for --skipinitialspace
, -T
for --out-tabs
und -t
for --tabs
.