2014-02-08 111 views
3

裏面我有以下模式sed和正則表達式替換「」除了字符串

10,0,'string1_string2,_string3','',8,0,0,0.59,'20140101205216','20140128074836',584266915,5934 

的輸入,我想用SED選項卡全部更換逗號‘,’字。約束條件是不要替換文本字符串中的「,」(即不應使用製表符替換'string1_string2,_string3'中的逗號)。一個正則表達式是,(?!,_)。

但是下面的sed不起作用。我也嘗試過所有的轉義排列。

sed s/",\(\?\!,_\)"/"\t"/g 

有沒有辦法做到這一點?

+0

我會用一個Perl程序來讀取行,根據打破它CSV字段,修改有問題的字段,然後重建該行。以非結構化方式處理結構化數據是一個長期的噩夢。 –

+0

感謝Johanthan,以及下面的其他人。是的,你說得很對。我做過很多惡夢。按照您的建議我有一個解決方案。 – Peyman

回答

2

在Mac OS X 10.9.1,你可以使用:

sed -E -e "s/('[^']*'|[^,]*),/\1X/g" 

除了你有一個實際的標籤取代X。對於你的輸入行,這將產生:

10X0X'string1_string2,_string3'X''X8X0X0X0.59X'20140101205216'X'20140128074836'X584266915X5934 

其中有你想要標籤的X。使用GNU sed,您可以使用-r代替-E(儘管它也可以識別-E)。 Mac sed不會將\t擴展爲選項卡; GNU sed會。使用bash,您可以使用ANSI-C引用機制有殼嵌入傳遞給sed字符串中的一個標籤:

sed -E -e "s/('[^']*'|[^,]*),/\1"$'\t'"/g" 

沒有擴展正則表達式(由-r-E激活),它不是值得在sed嘗試;改爲使用awk

正則表達式查找要麼單引號後跟零個或多個非引號和單引號零個或多個非逗號,後跟一個逗號,以怎樣被人記住的替換它/或字符串和'選項卡'(使用X來表示選項卡,因爲它更可見)。


devnullpoints out,答案上面一行的末尾替換逗號中的字符串。有應該是一個解決辦法:

sed -E -e "s/('[^']*'|[^,]*)(,|$)/\1"$'\t'"/g; s/"$'\t'"$//" 

s///g分號之前添加到每一行的末尾一個標籤;分號後的s///刪除剛剛添加的選項卡。

+0

+1;在OSX上,拼接'$'\ t''來創建一個tab字符。 (與其他控制字符類似,例如'$'\ n'')。它看起來很尷尬(沒有awk雙關語意思),但它起作用:'sed -E -e's /('[^'] *'| [^,] *),/ \ 1「$'\ t'」/ g「' – mklement0

+1

是的,這肯定會起作用,使用Bash和[ANSI-C引用](http://www.gnu.org/software/bash/manual/bash.html#ANSI_002dC-Quoting)機制,但它是處理'\ t'而不是'sed'的shell。 (我知道你知道,我確信後面的人也會知道。)使用control-V control-I(或tab)也可以。使用GNU'sed','sed'本身處理'\ t'標籤轉換。 –

+1

如果字符串以帶引號的文本結尾,恐怕這可能會中斷。 'a,'b,c'' – devnull

0

這似乎如果我正確地理解你的問題的工作:

sed -E 's/,([^_])/\t\1/g' 

輸出:

10 0 'string1_string2,_string3' '' 8 0 0 0.59 '20140101205216' '20140128074836' 584266915 5934 
+1

這對給定數據有效,因爲字符串中的逗號後面跟着下劃線,而其他逗號後面沒有下劃線。它沒有處理變量,比如''string1,string2,string3',_ abc_'都很好。 –

+0

@JonathanLeffler我知道,但沒有必要處理問題中的所有變體。 Peyman建議''sed s /「,\(\?\!,_ \)」/「\ t」/ g',它是關於替換',_' - >'\ t'。我有點困惑,他真的在問什麼。如果它適用於所有其他變體,那麼我的答案當然是無用的。 –

1

我會建議採取若可用,因爲lookarounds可用性Perl的幫助:

s="10,0,'string1_string2,_string3','',8,0,0,0.59,'20140101205216','20140128074836',584266915,5934" 

perl -pe "s/,(?=(([^']*'){2})*[^']*$)/\t/g" <<< "$s" 

10\t0\t'string1_string2,_string3'\t''\t8\t0\t0\t0.59\t'20140101205216'\t'20140128074836'\t584266915\t5934 

PS:Showing \t僅用於可讀性目的。

+0

+1;它可以工作,但是我的大腦仍然因試圖理解而受到傷害: - 由於前瞻斷言'(?= ...)',匹配是通過* every *行的* end *來執行的,'found 。 - 圓括號中的整個表達式只有在前面的','不在單引號字符串內時才匹配的超前表達式。 - 它通過尋找*對*引號來實現 - 其含義是,如果該行上的剩餘引號(如果有的話)不配對,那麼'''手頭必須*在引號字符串中。 - 淨效果:只有','字符。在引用的字符串之外匹配並替換。 – mklement0

+0

是的,它看起來有點棘手,但它的作用是確保逗號後總是有偶數個單引號(0,2,4,6 ...)。向前看就是這樣做的(參見'{2}'部分)。 – anubhava

1

你可以使用Text::ParseWords

perl -MText::ParseWords -n -l -e 'print join("\t", parse_line(",", 1, $_));' filename 

您的輸入,它會導致:

10  0  'string1_string2,_string3'  ''  8  0  0  0.59 '20140101205216'  '20140128074836'  584266915  5934