
在文字檔中我有多筆記錄。每筆記錄都有多個以逗號分隔的列,某些列有一組大括號,而其他列有多個大括號。
我需要:
如果在一組或多組大括號之外發現逗號,則應將逗號替換為豎線。
如果在一組或多組花括號內發現逗號,則應保留該逗號。所以給出的
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})
解釋:
我將正規表示式分成兩部分來解釋。
(\{(?:[^{}]|(?1))*\})
(*SKIP)(*F)|,
第 1 部分
(\{(?:[^{}]|(?1))*\})
- 只有當花括號正確配對時,此技巧才有效。
()
這些是捕獲組,用於捕獲字元。\{
匹配左大括號。(?:[^{}]|(?1))
(?:...)
稱為非捕獲組。[^{}]
它會匹配任何字符,但不匹配{
或}
|
邏輯或運算符。(?1)
遞歸第一個捕獲組。
(?:[^{}]|(?1))*
匹配前一個標記零次或多次。\}
結束}
符號。
考慮下面的範例以及與其中的嵌套括號相符的模式。
細繩:
h{foo{bar}foobar}
圖案:
h(\{(?:[^{}]|(?1))*\})
- 首先,正規表示式引擎嘗試匹配
h
(這是圖案中的) 針對輸入字串。所以第一個字母h
是匹配的。 - 尋找平衡括號的模式被輸入到捕獲組。
- 現在,引擎採用
\{
模式中的第二個字元(即 )並嘗試與輸入字串進行比對。所以第一個{
得到了被捕獲。我使用“捕獲”一詞而不是“匹配”,因為\{
它位於捕獲組內。 (?:[^{}]|(?1))*
這告訴正規表示式引擎符合除{
或之外的任何字元}
零次或多次。如果您找到任何{
或}
字符,則再次遞歸到第一個捕獲組。所以現在字串foo
被捕獲了。接下來的字元是{
,因此它遞歸到第一個捕獲組。現在,正規表示式引擎的遞歸等級下降了一級。我們的第一個捕獲組中的第一個模式是什麼(看正規表示式)?是的\{
,現在它{
與字串後面的符號相符foo
。- 引擎的遞歸深度仍然是一層,模式再次
(?:[^{}]|(?1))*
與字串相符bar
。現在bar
is之後的字符}
,因此在匹配字串後bar
,正則表達式引擎將不會進入,(?1)
這就是我們使非捕獲組重複的原因零或更多次。下一個模式(後的模式(?:[^{}]|(?1))*
) 在正規表示式中是\}
.所以這\}
將匹配}
緊接在 to 之後的大括號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
使用定界符跨越一行數據,您可以確定在一行中找不到定界符 - 換行符。它從左到右沿線行走,停在兩個興趣點中的下一個——角色}{
。當它停在 a 處時,{
它會在其分隔符號中添加一個換行符;當在 a 時,}
如果有兩個,則減一。
當它停止在某一點時,該行上只能找到一個換行符,並且在任一 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)