解決方案在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
,b
和c
中。然後內部@(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
,c
和x
。在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)
沒有明確的邏輯來使用分隔逗號:它們被隱式跳過。
謝謝你的徹底和教育的答案。我不知道TXR,這聽起來很有趣。雖然@ karakfa的答案不是解釋性的,但它並沒有偏離這個問題,我成功實施了它。因此,它將成爲我所接受的那一點是有道理的。 我會研究你所建議的工具。 – pfff