Text-Processing

在重疊的括號中提取標識符和對應的括號

  • May 28, 2022

我有一些文件包含如下標識符:

B#205918
A#273075
E#554065

例如。文件1樣本:

((((A#273075,A#273116),((A#224325,A#192952),A#243232)),(((E#7955,E#7165),E#6239),E#4530)),(((((E#3075,E#3702),B#251221),E#35128),B#243275),((B#198094,B#176280),B#273119)))

在這個文件中,標識符僅從三個字母(群)開始;A/B/E。我想自動將以 A/B/E 開頭的標識符提取到單獨的文件中,其中每個文件僅包含同一集群中的標識符。

同一括號內的標識符屬於同一組。例如,((B#198094,B#176280),B#273119)

B#198094 和 B#196280 在同一個內組中,與 B#273119 一起,三個在更大的組內。也就是說,括號在標識符的提取過程中確實很重要。

基本上,我可以在算法上描繪的是,當括號內的所有標識符都以同一群中的標識符(A/B/E)開頭時,提取標識符和包含它們的所有匹配的左括號和右括號。

預期的輸出文件:

集群 A:

((A#273075,A#273116),((A#224325,A#192952),A#243232))

集群 B:

((B#198094,B#176280),B#273119)

集群 E*:

(((E#7955,E#7165),E#6239),E#4530)
(E#3075,E#3702)

*在提取輸出文件中可以多於一行,因為同一個集群的標識符有可能沒有放在同一個組中(離群值)——從範例文件中可以看出,這兩個組集群 E 文件中的標識符沒有被任何公共括號括起來,除了括住所有標識符的括號。

到目前為止,這是我對集群 A 提取的結果:

grep -o "(*(A#.*)*" file1 | sed 's/,*E#.*//g'

但這不適用於在文件的不同部分多次出現的群,即本例中的群 E。此外,它實際上並沒有關注被提取的括號數量,這將導致輸出文件出錯(左括號和右括號的數量不同)。

sed並且perl命令對我不起作用。我嘗試在每個逗號處拆分文件並提取以 E 開頭的每個後續行(以提取 E 群)。

sed 's/,/,\n/g' file1 | sed -n '/*E.*,\n(E/p'
sed -n ':begin;$!N;/*(E#.*\n*(E/p' file1
sed 's/,/,\n/g' file1 | perl -ane 'if(/.*E#.,\n*E#./ ... /^}/){$counter++ if /\(E#/; print if $counter==1}'

我在這個過程中有點迷失,並努力以最簡單和最短的方式強調這一點。如果有任何遺漏或不太清楚的部分,請告訴我。

可能是這樣的:

<file1 perl -lne '
 for (m{(\((?:[^()]++|(?1))*\))(?(?{($1 =~ s/[^ABE]//gr) !~ /^(.)\1+$/})(*FAIL))}g) {
   ($cluster) = /([ABE])/;
   open($out{$cluster}, ">", "cluster $cluster.txt") unless $out{$cluster};
   print {$out{$cluster}} $_;
 }'

這裡使用了一些 perl 的高級正則表達式運算符:

  • (?1)用於遞歸匹配,因此我們可以說匹配(...)包含 0 個或多個非()s 序列的一對或包含這些對中的另一個(...)…等等。
  • (?:...)只是(...). 僅用於分組。
  • ++``+是(一個或多個,但不回溯)的非回溯版本。
  • (?(?{code})pattern)如果成功,則動態插入pattern正則表達式。code在這裡,我們插入(*FAIL)aka (*F)or(?!)告訴正則表達式引擎,如果第一個擷取組匹配的 ABE 字母不是兩個或多個相同字母的序列,則此時不匹配。

詳情請參閱perldoc perlre

然後只需從這些匹配項中提取字母並將匹配項寫入相應的輸出文件即可。

對於不熟悉的人perl

  • perl -ln是為輸入的每一行執行sed程式碼(此處傳遞給 )的模式,其中等效於’s 模式空間。-e``$_``sed
  • m{regex}g是 . 的替代語法/regex/g。在列表上下文中,它將所有擷取組匹配的內容作為單獨的元素(如果有)返回,否則返回所有匹配項(這裡沒有任何區別,因為只有一個擷取組並且它包括整個匹配項)。$_如果未指定主題(帶有subject =~ m{...}g) ,則適用於。
  • for (list) {code}for $var (list) {code}遍歷列表的元素,但沒有指定變數,所以它預設為$_.
  • /(ABE)/,與 相同m{(ABE)},在列表內容(此處為列表的分配)中,此處沒有g,返回擷取組匹配的內容(第一次出現 A、B 或 E 字母)。如果沒有擷取組,它只會返回一個布爾值。
  • $1 =~ s/[^ABE]//gr應用subtitution ( globally) 並r返回結果。所以這裡返回擷取組的內容,除了 ABE 字母被刪除。

x您可以通過使用允許插入空格和註釋並命名擷取組的標誌使其更易讀:

<file1 perl -lne '
 for (
   m{
     (?<paren> [(] (?: [^()] ++ | (?&paren) ) * [)])
     (?(?{ ($+{paren} =~ s/[^ABE]//gr) !~ /^(.)\1+$/ })(*FAIL))
   }xg
 ) {
   ($cluster) = /([ABE])/;
   open($out{$cluster}, ">", "cluster $cluster.txt") unless $out{$cluster};
   print {$out{$cluster}} $_;
 }'

引用自:https://unix.stackexchange.com/questions/703609