
In der Textdatei habe ich mehrere Datensätze. Jeder Datensatz hat mehrere durch Kommas getrennte Spalten, einige Spalten haben einen Satz geschweifter Klammern und andere haben mehr als eine geschweifte Klammer.
Ich brauche:
Wenn ein Komma außerhalb einer oder mehrerer geschweifter Klammern steht, sollte das Komma durch ein Pipe-Zeichen ersetzt werden.
Wenn ein Komma in einer oder mehreren geschweiften Klammern steht, sollte das Komma unverändert bleiben.
THING1,{THING2,{THING3,}},THING4
Die Ausgabe sollte also lautenTHING1|{THING2,{THING3,}}|THING4
:
Beispieldatensatz:
(999969,2500,"777777888",0,"45265","65522",NULL,10001,2014-09-15 10:27:07.287,2014-09-15 10:28:49.085,2014-09-15 06:28:50.000,0,0,NULL,"text","401c4133091977",{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL},NULL,{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]},{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1},NULL,NULL,NULL,NULL,NULL,{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})
das Ergebnis sollte sein:
(**999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|**{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}**|NULL|**{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}**|**{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}**|NULL|NULL|NULL|NULL|NULL|**{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})
Antwort1
Dies kann einfach durch die Kombination Perl
„+“ erfolgen regex
.
perl -pe 's/(\{(?:[^{}]|(?1))*\})(*SKIP)(*F)|,/|/g' file
Beispiel:
$ perl -pe 's/(\{(?:[^{}]|(?1))*\})(*SKIP)(*F)|,/|/g' file
(999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}|NULL|{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}|{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}|NULL|NULL|NULL|NULL|NULL|{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL})
Erläuterung:
Zur Erklärung habe ich den regulären Ausdruck in zwei Teile aufgeteilt.
(\{(?:[^{}]|(?1))*\})
(*SKIP)(*F)|,
1. Teil
(\{(?:[^{}]|(?1))*\})
- Dieser Trick funktioniert nur, wenn die geschweiften Klammern richtig gepaart sind.
()
Dies sind Erfassungsgruppen, die zum Erfassen von Zeichen verwendet werden.\{
entspricht einer öffnenden geschweiften Klammer.(?:[^{}]|(?1))
(?:...)
Wird als nicht erfassende Gruppe bezeichnet.[^{}]
Es würde mit jedem Zeichen übereinstimmen, jedoch nicht mit{
oder}
|
Logischer ODER-Operator.(?1)
Rekursiert die erste Erfassungsgruppe.
(?:[^{}]|(?1))*
Passt null oder mehrmals zum vorherigen Token.\}
Schließsymbol}
.
Betrachten Sie das folgende Beispiel und das Muster, das den verschachtelten Klammern darin entspricht.
Zeichenfolge:
h{foo{bar}foobar}
Muster:
h(\{(?:[^{}]|(?1))*\})
- Zuerst versucht die Regex-Engine, das
h
(das war im Muster) mit der Eingabezeichenfolge verglichen. Der erste Buchstabeh
wurde also abgeglichen. - Muster zum Finden der ausgeglichenen Klammern werden in eine Erfassungsgruppe eingespeist.
- Nun nimmt die Engine das zweite Zeichen (also
\{
) im Muster und versucht, es mit der Eingabezeichenfolge abzugleichen. Das erste{
wurde alsogefangen. Ich habe das Wort „erfasst“ anstelle von „übereinstimmend“ verwendet, da\{
es sich innerhalb einer Erfassungsgruppe befindet. (?:[^{}]|(?1))*
{
Dies weist die Regex-Engine an, jedes Zeichen außer oder}
null oder mehrmals abzugleichen . Wenn Sie ein beliebiges{
oder}
Zeichen gefunden haben, gehen Sie noch einmal zur ersten Erfassungsgruppe zurück. Jetztfoo
wurde die Zeichenfolge also erfasst. Das folgende Zeichen ist{
, also geht es zur ersten Erfassungsgruppe zurück. Jetzt ist die Regex-Engine eine Ebene tiefer in der Rekursion. Was ist das erste Muster in unserer ersten Erfassungsgruppe (siehe den regulären Ausdruck)? Es ist\{
, jetzt entspricht es dem{
Symbol, das direkt nach der Zeichenfolge kamfoo
.- Die Engine ist immer noch eine Ebene tief in der Rekursion, wieder
(?:[^{}]|(?1))*
stimmt das Muster mit der Zeichenfolge übereinbar
. Jetzt ist das Zeichen nach dembar
,}
also kommt die Regex-Engine nach dem Abgleichen der Zeichenfolgebar
nicht hinein,(?1)
deshalb haben wir die nicht erfassende Gruppe so erstellt, dass sie wiederholt wirdnulloder mehrmals. Nächstes Muster (Muster nach zu(?:[^{}]|(?1))*
) im regulären Ausdruck ist\}
. Dies\}
würde also mit der Klammer übereinstimmen}
, die direkt nach to standbar
. Jetzt verlässt die Regex-Engine die Rekursion eine Ebene tiefer und das Muster[^{}]*
würde mit der folgenden Zeichenfolge übereinstimmenfoobar
. Last\}
würde mit der letzten geschweiften Klammer übereinstimmen. - Jetzt enthält unsere erste Erfassungsgruppe
{foo{bar}foobar}
.
2. Teil
(*SKIP)(*F)
Bewirkt, dass die Zeichen, die abgeglichen oder erfasst werden, fehlschlagen. In unserem Fall wurden also alle erfassten geschweiften Klammern übersprungen. Das heißt, es zwingt die Regex-Engine, die Zeichen aus der verbleibenden Zeichenfolge abzugleichen.Syntax oder Format von
(*SKIP)(*F)
part1(*SKIP)(*F)|part2 | | |---- -----> Match this Don't match this
Das Muster, das direkt danach kam,
|
versucht also, die Zeichen aus der verbleibenden Zeichenfolge abzugleichen (Zeichenfolge außer den verschachtelten Klammern).|
In unserem Fall ist das Muster nach dem,
. Daher werden alle Kommas außerhalb der verschachtelten Klammern abgeglichen.
LesenDasum das zu verstehen Regular Expression Recursion
.
Notiz:
(?R)
rekursiert das gesamte Untermuster, d. h. die gesamte Übereinstimmung. Wir könnten es auch(?R)
so schreiben:(?0)
(?1)
rekursiert das erste Untermuster (also das Muster innerhalb der ersten Erfassungsgruppe)
Antwort2
ersetzen
,{
mit
|{
Und
},
mit
}|
echo "THING1,{THING2,{THING3,}},THING4" | sed -re "s/,\{/|{/gi" | sed -re "s/},/}|/gi"
führt zu
THING1|{THING2|{THING3,}}|THING4
Antwort3
Keine Angst, das ist eine harte sed
Aussage. Aber sie sollte die Kaskadierung berücksichtigen. Hier ist sie in einer Zeile:
sed -e 's/,/|/g;:a;s/{\([^{}]*\)|\([^{}]*\)}/{\1,\2}/g;ta;s/{\([^{}]*\)}/<\1>/g;ta;:b;s/<\([^<>]*\)>/{\1}/g;tb' file
Hier ist die kommentierte Version:
sed -e '
s/,/|/g; #replaces all commas (,) with pipes (|)
:a; #sets a label called a
s/{\([^{}]*\)|\([^{}]*\)}/{\1,\2}/g; #replaces {a|b|c} with {a,b|c}
ta; #go back to the label `a` and repeat the
#prevous part until there is nothing more
#to replace: when {a|b|c} became {a,b,c}
s/{\([^{}]*\)}/<\1>/g; #replace {...} with <...>
ta; #go back to label a again until all {} are
#replaces by <>
:b; #create a new label called b
s/<\([^<>]*\)>/{\1}/g; #replace <...> back to {...}
tb; #and back to label b to repeat the previous
#part
' file
Damit habe ich die gewünschte Ausgabe erhalten.
Antwort4
Mir sind ein paar Möglichkeiten eingefallen, dies in zu tun sed
, aber die meisten scheiterten in Sonderfällen. Eine funktioniert jedoch nicht:
sed 's/^/\n/;:b
/\n\n/!s/\(\n[^,{}]*\),/\1|/;tb
s/\(\n\n*\)\([^{}]*[{}]\)/\2\1/
s/{\(\n\)/&\1/;s/\(}\n\)\n/\1/;tb
s/\n//g' <<\DATA
(999969,2500,"777777888",0,"45265","65522",NULL,10001,2014-09-15 10:27:07.287,2014-09-15 10:28:49.085,2014-09-15 06:28:50.000,0,0,NULL,"text","401c4133091977",{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL},NULL,{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]},{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1},NULL,NULL,NULL,NULL,NULL,{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL},poopoer,sciioooper)
DATA
Dabei wird eine Datenzeile mithilfe eines Trennzeichens durchlaufen, das Sie mit Sicherheit nicht in einer Zeile finden werden - ein Zeilenumbruchzeichen. Es durchläuft die Zeile von links nach rechts und hält am nächsten von zwei interessanten Punkten an - den Zeichen }{
. Wenn es bei a anhält, {
fügt es seinem Trennzeichen ein Zeilenumbruchzeichen hinzu; wenn es bei a anhält, }
zieht es eins ab, wenn zwei vorhanden sind.
Wenn es an einem Punkt angehalten wird, an dem nur ein Zeilenumbruchzeichen in der Zeile zu finden ist und auf das Trennzeichen vor einem Komma folgt, {}
wird es durch ein Pipe-Zeichen ersetzt und es wird rekursiv zurückgegangen, um denselben Ersetzungstest erneut zu versuchen.
Dies sollte bei Bedarf auch unausgeglichene Klammergruppen schützen, obwohl es keine Methode zur Behandlung einer Klammer in Anführungszeichen verwendet, was durch Hinzufügen vonSehenswürdigkeitenIch schätze schon, aber ich bin nicht besonders begeistert, es herauszufinden.
Die Ausgabe Ihres Beispiels:
(999969|2500|"777777888"|0|"45265"|"65522"|NULL|10001|2014-09-15 10:27:07.287|2014-09-15 10:28:49.085|2014-09-15 06:28:50.000|0|0|NULL|"text"|"401c4133091977"|{F,F,"711592473,"00967711580001,F,NULL,NULL,"421010617759466","'401c4133091977H'",NULL,NULL,NULL,NULL,NULL,NULL,1,1,10,1,0,0,0,"a30200000000276f",NULL}|NULL|{gggg{-1, 0, -1, 1410762530000, 87, 0, 0}, rrrr[{"foot", 24000, 976000, 3999-12-31 23:59:59, 0}], rrrr[{1000003, 1410762443000, 120, 87, 0, 0, 2, 1, 24000, 0, 0}]}|{dd=0, ff=0, gg=0, hh=1, ctr="live", dddd="52265", eni=55, cuc=1}|NULL|NULL|NULL|NULL|NULL|{NULL,NULL,NULL,0,"wwww","eeee",2014-10-10 10:45:59.000,2015-03-09 23:59:59.000,2015-06-07 23:59:59.000,2015-08-06 23:59:59.000,NULL}|poopoer|sciioooper)