Entfernen Sie in einer CSV-Datei nur die Kommas zwischen Zahlen in Anführungszeichen.

Entfernen Sie in einer CSV-Datei nur die Kommas zwischen Zahlen in Anführungszeichen.

In einer Textdatei möchte ich ,(Kommas) und auch "(Anführungszeichen) entfernen (nur wenn die Anführungszeichen durch Kommas getrennte Zahlen enthalten).

56,72,"12,34,54",x,y,"foo,a,b,bar"

Erwartete Ausgabe

56,72,123454,x,y,"foo,a,b,bar"

Notiz:Ich zeige die obige Zeile nur als Beispiel. Meine Textdatei enthält viele Zeilen wie oben und die durch Kommas getrennten Zahlen innerhalb der Anführungszeichen sollten variieren. Das heißt,

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Erwartete Ausgabe:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

nIn den Anführungszeichen sind mehrere Zahlen durch Kommas getrennt. Lassen Sie die Anführungszeichen, die Zeichen enthalten, so wie sie sind .

Ich liebe sedTextverarbeitungstools. Ich freue mich, wenn Sie eine sedLösung dafür posten.

Antwort1

Wenn Perl ok ist, hier eine kurze (und wahrscheinlich schnelle, wenn auch nicht unbedingt einfache :) ) Vorgehensweise:

perl -pe 's:"(\d[\d,]+)":$1=~y/,//dr:eg' file

Das eFlag für den s:::Operator (das nur eine andere Schreibweise für ist s///) bewirkt, dass der Ersatz als Ausdruck behandelt wird, der jedes Mal ausgewertet wird. Dieser Ausdruck nimmt die $1Erfassung aus dem regulären Ausdruck (dem bereits die Anführungszeichen fehlen) und übersetzt ( y///, was auch als geschrieben werden kann ), indem alle Kommas tr///gelöscht ( ) werden. Das Flag für ist erforderlich, damit der Wert die übersetzte Zeichenfolge ist, statt der Anzahl der Übersetzungen./dry

Für diejenigen, die sich durch Perl irgendwie beschmutzt fühlen, hier das Python-Äquivalent. Python ist eigentlich kein Shell-Einzeiler-Tool, aber manchmal kann man es zur Kooperation überreden. Das Folgende kann als eine Zeile geschrieben werden (im Gegensatz zu forSchleifen, die nicht geschrieben werden können), aber das horizontale Scrollen macht es (noch) unleserlicher:

python -c '
import re;
import sys;
r=re.compile("\"(\d+(,\d+)*)\"");
all(not sys.stdout.write(r.sub(lambda m:m.group(1).replace(",",""),l))
    for l in sys.stdin)
' < file

Antwort2

Dies (adaptiert vonHier) sollte tun, was Sie brauchen, obwohl @ricis Perl-Version viel einfacher ist:

$ sed -r ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;ta; s/""/","/g; 
          s/"([0-9]*)",?/\1,/g ' file
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454,
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Erläuterung

  • :a: Definieren Sie ein Label mit dem Namen a.
  • s/(("[0-9,]*",?)*"[0-9,]*),/\1/: Das muss abgebaut werden
    • Verwenden Sie zunächst diese Konstruktion: (foo(bar)), \1wird sein foobarund \2wird sein bar.
    • "[0-9,]*",?: Übereinstimmung mit 0 oder mehr von 0-9oder ,, gefolgt von 0 oder 1 ,.
    • ("[0-9,]*",?)*: Übereinstimmung mit 0 oder mehr der oben genannten.
    • "[0-9,]*: Übereinstimmung mit 0 oder mehr von 0-9oder ,die direkt nach einem kommen"
  • ta;: Gehe zurück zum Etikett aund führe es erneut ausWenndie Ersetzung war erfolgreich.
  • s/""/","/g;: Nachbearbeitung. Ersetzen ""durch ",".
  • s/"([0-9]*)",?/\1,/g: Entfernen Sie alle Anführungszeichen um Zahlen.

Dies lässt sich möglicherweise anhand eines anderen Beispiels leichter verstehen:

$ echo '"1,2,3,4"' | sed -nr ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;p;ta;'
"1,2,34"
"1,234"
"1234"
"1234"

Sie können also eine Zahl finden, die direkt nach einem Anführungszeichen steht und auf die ein Komma und eine weitere Zahl folgen. Fügen Sie die beiden Zahlen zusammen und wiederholen Sie den Vorgang, bis dies nicht mehr möglich ist.

An dieser Stelle halte ich es für sinnvoll, ein Zitat aus info seddem Abschnitt zu erwähnen, in dem erweiterte Funktionen beschrieben werden, wie z. B. das oben verwendete Label (danke fürs Auffinden, @Braiam):

In den meisten Fällen weist die Verwendung dieser Befehle darauf hin, dass Sie wahrscheinlich besser mit einer Sprache wie „awk“ oder Perl programmieren.

Antwort3

Für CSV-Daten würde ich eine Sprache mit einem echten CSV-Parser verwenden. Zum Beispiel mit Ruby:

ruby -rcsv -pe '
  row = CSV::parse_line($_).map {|e| e.delete!(",") if e =~ /^[\d,]+$/; e} 
  $_  = CSV::generate_line(row)
' <<END
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
END
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Antwort4

Verwenden vonRaku(früher bekannt als Perl_6)

~$ raku -pe 's:g/ \" ~ \" (\d+) ** 2..* % "," /{$0.join}/;'  file

Beispieleingabe:

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Beispielausgabe:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Raku ist eine Programmiersprache aus der Perl-Familie mit einer Reihe leistungsstarker Regex-Funktionen. Einen allgemeinen Überblick über diese Antwort finden Sie unter der folgenden URL:

https://unix.stackexchange.com/a/722570/227738

Im obigen Code werden Ziffern erkannt und eingebettete Kommas entfernt. Der reguläre Ausdruck nutzt die Tatsache aus, dassverschachtelte Strukturenkann mit Rakus neuer ~ Tilde-Notation (verschachtelt) gekennzeichnet werden, was \" ~ \" [\d+]„eine oder mehrere Ziffern umgeben von Anführungszeichen“ bedeutet.

Zusätzlich,sich wiederholende Strukturenkann mit Rakus neuem %modifizierten Quantifizierer für wiederholte Strukturen bezeichnet werden. Die Notation [\d+] ** 2..* % "," bedeutet „eine oder mehrere durch ,Kommas getrennte Ziffern, wobei dieses Muster ** 2..*zwei oder mehr Mal wiederholt wird. [Wenn es zufällig ein nachfolgendes Trennzeichen (z. B. Komma) gibt, verwenden Sie in der Syntax a %%statt of ].%

Dies ist nur ein Anfang. CSV-Dateien mit alternativen Trennzeichen, eingebetteten Zeilenumbrüchen, eingebetteten Kommas, möglicherweise leeren Feldern usw. müssen wirklich von einem echten CSV-Parser wie dem Raku-Modul verarbeitet werden Text::CSV. Weitere Informationen finden Sie unter den folgenden Links.

https://docs.raku.org/language/regexes
https://raku.land/github:Tux/Text::CSV
https://raku.org

verwandte Informationen