2016-06-10 27 views
0

我有一個CSV,我需要重新格式化一個列的內容。 問題是每個單元格的格式都有完全不同的長度。Bash/Awk:重新格式化帶有多個分隔符的不平衡列

當前列樣子(這是單列的兩行):

Foo*foo*foo*1970,1980+Bar*bar*bar*1970 
Foobar*Foobar*foobarbar*1970,1975,1980 

結果應該像(還是兩行一列)

Foo*foo*foo*1970+Foo*foo*foo*1980+Bar*bar*bar*1970 
Foobar*Foobar*foobarbar*1970+Foobar*Foobar*foobarbar*1975+Foobar*Foobar*foobarbar*1980 

這就是我想要要做

#!/bin/bash 

cat foocol | \ 
    awk -F'+' \ 
    '{for i in NF print $i}' \ 
     | awk -F'*' \ 
     '{$Foo=$1"*"$2"*"$3"*" print $4}' \ 
\ 
     | awk -v Foo=$Foo -F',' \ 
     '{for j in NF do \ 
      print Foo""$j"+" }' \ 
> newcol 

這個想法是迭代多個'+'分隔的數據,而前三個'*' '分隔的值將被分組爲每個','分隔的年份,它們之間有一個'+'

但是我只是隨處可見語法錯誤。

感謝

回答

1
$ awk --re-interval -F, -v OFS=+ '{match($1,/([^*]*\*){3}/); 
       prefix=substr($0,RSTART,RLENGTH); 
       for(i=2;i<=NF;i++) $i=prefix $i }1' file 

Foo*foo*foo*1970+Foo*foo*foo*1980+Bar*bar*bar*1970 
Foobar*Foobar*foobarbar*1970+Foobar*Foobar*foobarbar*1975+Foobar*Foobar*foobarbar*1980 

也許添加驗證if(match(...

1

解決方案在TXR

 
$ txr reformat.txr data 
Foo*foo*foo*1970+Foo*foo*foo*1980+Bar*bar*bar*1970 
Foobar*Foobar*foobarbar*1970+Foobar*Foobar*foobarbar*1975+Foobar*Foobar*foobarbar*1980 

守則reformat.txr

@(repeat) 
@ (coll)@/\+?/@a*@b*@c*@(coll)@{x /[^,+]+/}@(until)[email protected](end)@(end) 
@ (output :into items) 
@ (repeat) 
@  (repeat) 
@a*@b*@c*@x 
@  (end) 
@ (end) 
@ (end) 
@ (output) 
@ {items "+"} 
@ (end) 
@(end) 

該解決方案基於數據具有嵌套語法:記錄組由新行分隔。組內的記錄由+分隔,並且在記錄內有由*分隔的四個字段。最後一個字段包含逗號分隔的項目。數據將通過擴展記錄的副本進行標準化,以便逗號分隔的項目分佈在副本中。

外部@(repeat)處理行走。外部@(coll)重複記錄,將前三個字段收集到變量a,bc中。然後內部@(coll)將每個逗號分隔的項目獲取到變量x。內部@(coll)x -s收集到列表中,並且外部@(coll)也將所有變量收集到列表中,因此a,b,c成爲字符串列表,並且x是字符串列表的列表。

output中的:into items關鍵字參數導致通常會轉到標準輸出設備的行被收集到一個字符串列表中,並綁定到一個變量。例如:

@(output :into lines) 
a 
b 
cd 
@(end) 

建立了一個變量lines其中包含列表("a" "b" "cd")

所以在這裏,我們將雙線嵌套repeat的輸出看作一束線,每條線代表一條記錄,存儲在一個名爲items的變量中。然後我們output這些使用@{items "+"},這是一個語法,輸出列表變量的內容與給定的分隔符。

雙嵌套repeat處理記錄從第四個字段中的每個逗號分隔項目的擴展。外部repeat隱含地迭代列表a,b,cx。在repeat內部,這些變量表示它們各自列表的項目。變量x是列表的列表,因此內部repeat對此進行了迭代。在外部repeat內部,變量a,b,c已經是標量,並保持在內部repeat的範圍內:只有x變化,這正是我們想要的。

跨越每一行數據收集,也有一些細微之處:

@ (coll)@/\+?/@a*@b*@c*@(coll)@{x /[^,+]+/}@(until)[email protected](end)@(end) 

首先,我們匹配一個可選的前導加配/\+?/正則表達式,從而消耗它。如果沒有這個,除第一個記錄外,每個記錄的a字段將包括分隔+,我們將在最終輸出中得到雙倍+ -s。 a,b,c變量被簡單地匹配。 TXR對於分離物質是非貪婪的:@a*表示將某些字符匹配到最近的*,並將它們綁定到變量a。收集x列表更爲棘手。這裏使用了正匹配正則表達式匹配變量:@{x /[^,+]+/}來提取子字段。每個x是一個或多個字符的序列,它們不是加號或逗號,而是正面提取而不考慮後面的任何內容,就像分詞器提取令牌一樣。此內部收集在遇到+時終止,這是@(until)+子句確保的內容。它也將隱含地終止,如果它擊中行的末尾; @(until)匹配不是必需的(默認情況下)。終止+停留在輸入流中,這就是爲什麼我們必須識別它並放棄在@a之前。

應該指出的是,默認情況下,@(coll)掃描匹配並跳過不匹配的文本區域,就像它的堂兄@(collect)對線條所做的那樣。例如,如果我們有@(coll)@{foo /[a-z]+/}@(end),它將小寫字母序列收集到foo中,則將foo轉換爲此類字符串的列表,並且如果輸入爲1234abcd-efgh.... ijk,則foo以列表("abcd" "efgh" "ijk")結尾。這就是爲什麼內部@(coll)沒有明確的邏輯來使用分隔逗號:它們被隱式跳過。

+0

謝謝你的徹底和教育的答案。我不知道TXR,這聽起來很有趣。雖然@ karakfa的答案不是解釋性的,但它並沒有偏離這個問題,我成功實施了它。因此,它將成爲我所接受的那一點是有道理的。 我會研究你所建議的工具。 – pfff

相關問題