テキストファイルに複数のレコードがあります。各レコードにはカンマで区切られた複数の列があり、一部の列には中括弧セットが1つ、他の列に複数の中括弧があります。
以下を行う必要があります。
1つ以上の中括弧セットの外側にコンマがある場合は、コンマを垂直バーに置き換える必要があります。
1つ以上の中括弧セット内にカンマがある場合は、そのカンマを保持する必要があります。
THING1,{THING2,{THING3,}},THING4
したがって、与えられた出力はTHING1|{THING2,{THING3,}}|THING4
。
レコードサンプル:
(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})
結果は次のとおりです。
(**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})
答え1
Perl
+組み合わせで簡単にregex
できます。
perl -pe 's/(\{(?:[^{}]|(?1))*\})(*SKIP)(*F)|,/|/g' file
例:
$ 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})
説明する:
正規表現を2つの部分に分けて説明します。
(\{(?:[^{}]|(?1))*\})
(*SKIP)(*F)|,
パート1
(\{(?:[^{}]|(?1))*\})
- このトリックは、中かっこが正しくペアになっている場合にのみ機能します。
()
キャラクタをキャプチャするために使用されるキャプチャグループです。\{
開く中かっこに一致します。(?:[^{}]|(?1))
(?:...)
非キャプチャグループと呼ばれます。[^{}]
すべての文字に一致するが一致し{
ない}
|
論理OR演算子。(?1)
最初のキャプチャグループに再帰します。
(?:[^{}]|(?1))*
以前のトークンと0回以上一致します。\}
終了}
記号。
次の例とその中に入れ子にされた括弧に一致するパターンを考えてみましょう。
ひも:
h{foo{bar}foobar}
パターン:
h(\{(?:[^{}]|(?1))*\})
- まず、正規表現エンジンは次のように一致しようとします
h
(このパターンにあります。) 入力文字列の場合。したがって、最初の文字がh
一致します。 - バランスカッコを探すパターンは、キャプチャグループに入力されます。
- エンジンは
\{
パターンの2番目の文字(たとえば)を取得し、入力文字列と比較しようとします。だから最初の人{
が得た。キャプチャされます。\{
キャプチャグループ内にあるため、「一致」ではなく「キャプチャ」という単語を使用してください。 (?:[^{}]|(?1))*
これは、正規表現エンジンがゼロ回以上の文字を{
除くすべての文字と一致するように指示します。または、文字}
が見つかった場合は、最初のキャプチャグループに戻ります。これで文字列がキャプチャされました。次の文字があるため、最初のキャプチャグループに再帰されます。これで、正規表現エンジンの再帰レベルが1段階低下しました。最初のキャプチャグループの最初のパターンは何ですか({
}
foo
{
正規表現を見る)?はい\{
、これは{
文字列の後ろの記号と一致しますfoo
。- エンジンの再帰深度はまだ1レベルで、パターンは
(?:[^{}]|(?1))*
文字列と再一致しますbar
。今後bar
の文字は、}
文字列を一致させた後にbar
正規表現エンジンが介入しないため、非(?1)
キャプチャグループを繰り返す理由です。若いまたはそれ以上。次のモード(以降のモデル(?:[^{}]|(?1))*
)は正規表現からです\}
。したがって、これは直後の中括弧\}
と一致します。正規表現エンジンは1つのレベルの再帰から外れ、パターンは次の文字列と一致します。最後は最後の支柱と一致します。}
bar
[^{}]*
foobar
\}
- 今最初のキャプチャグループに
{foo{bar}foobar}
。
第二部
(*SKIP)(*F)
一致またはキャプチャ失敗を引き起こす文字です。したがって、この例では、キャプチャされたすべてのバランス括弧をスキップします。つまり、正規表現エンジンが文字列の残りの文字と一致するように強制します。構文または形式
(*SKIP)(*F)
part1(*SKIP)(*F)|part2 | | |---- -----> Match this Don't match this
したがって、それに続くパターンは
|
残りの文字列(入れ子にされた中かっこを除く文字列)。私たちの場合、次のパターン
|
はです,
。したがって、入れ子になった中括弧の外側のすべてのコンマが一致します。
読むこれ理解するRegular Expression Recursion
。
メモ:
(?R)
完全なサブパターン、つまり完全一致を繰り返します。私たちも(?R)
使えます。(?0)
(?1)
最初のサブパターンを繰り返します(つまり、最初のキャプチャグループ内のパターン)。
答え2
変える
,{
そして
|{
そして
},
そして
}|
echo "THING1,{THING2,{THING3,}},THING4" | sed -re "s/,\{/|{/gi" | sed -re "s/},/}|/gi"
明らかにする
THING1|{THING2|{THING3,}}|THING4
答え3
これが難しいsed
ステートメントであることを恐れないでください。しかし、カスケードを尊重する必要があります。次の行は次のとおりです。
sed -e 's/,/|/g;:a;s/{\([^{}]*\)|\([^{}]*\)}/{\1,\2}/g;ta;s/{\([^{}]*\)}/<\1>/g;ta;:b;s/<\([^<>]*\)>/{\1}/g;tb' file
注釈付きバージョンは次のとおりです。
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
これにより、目的の結果が得られます。
答え4
私はこれを行うためのいくつかの方法を考えましたが、sed
ほとんどは特別なケースでは失敗します。しかし、そうでないことがあります:
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
データ行にわたって区切り記号を使用すると、区切り記号(改行文字)が行に見つからないことを確認できます。左から右に線をたどり、2つの関心点のうち次の点である文字で停止します}{
。 aで停止すると{
区切り記号に改行文字を追加し、aで停止すると}
2つがある場合は1を減算します。
行に改行文字のみが見つかり、aの前の区切り記号の後にカンマがある点で停止した場合は、{}
それをパイプに置き換えて再帰的に戻って同じ代替テストを再試行します。
これは、必要に応じて不均衡な中括弧グループも保護する必要がありますが、引用した中括弧を処理するために何もしません。見どころそうなのですが、その事実を知ることになって、それほど嬉しくありません。
出力例:
(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)