중복되는 괄호에서 식별자와 해당 괄호 추출

중복되는 괄호에서 식별자와 해당 괄호 추출

다음과 같은 식별자가 포함된 파일이 있습니다.

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로 시작하는 식별자를 별도의 파일로 자동 추출하고 싶습니다. 각 파일에는 동일한 클러스터의 식별자만 포함되어 있습니다.

동일한 괄호 안의 식별자는 동일한 그룹에 속합니다. 예를 들어,((B#198094,B#176280),B#273119)

B#198094와 B#196280은 동일한 내부 그룹에 속해 있으며, B#273119와 함께 그 중 3개가 더 큰 그룹에 속해 있습니다. 즉, 식별자 추출 프로세스에서는 괄호가 중요합니다.

기본적으로 내가 알고리즘적으로 상상할 수 있는 것은 괄호 안의 모든 식별자가 동일한 클러스터(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}'

나는 그 과정에서 약간 길을 잃었고 가능한 가장 간단하고 간단한 방법으로 이것을 강조하려고 노력했습니다. 누락된 부분이나 명확하지 않은 부분이 있으면 알려주세요.

답변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)또는 를 삽입하여 (?!)첫 번째 캡처 그룹과 일치하는 ABE 문자가 두 개 이상의 동일한 문자의 시퀀스가 ​​아닌 경우 이번에는 일치하는 항목이 없음을 정규식 엔진에 알립니다.

perldoc perlre자세히보다.

그런 다음 이러한 일치 항목에서 문자를 추출하고 일치 항목을 해당 출력 파일에 씁니다.

익숙하지 않은 사람들을 위해 perl:

  • perl -ln는 input 의 각 줄에 대해 sed코드(여기에 전달됨)를 실행하는 패턴 입니다. 여기서 는 의 패턴 공간과 같습니다.-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]//grs대체( g전역) 를 적용 하고 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}} $_;
  }'

답변2

한 가지 방법은 입력 파일 구조에 대한 구문을 작성하는 것입니다.

perl -M5.010 -Mautodie -lne 'my $code = 
sub($) {
  qr{
    ((?&list))
    (?(DEFINE)
      (?<element> [$_[0]][#]\d+)
      (?<value> (?:(?&element)|(?&list)))
      (?<list> \((?&value)(?:,(?&value))*\))
    ) #DEFINE
  }x; #qr
}; #sub

for my $v ( qw(A B E) ) {
  my $re = $code->(quotemeta $v);
  open my $fh, ">", "cluster_$v.txt";
  select $fh;
  print for grep(/\S/,/$re/g);
  close $fh;
}' file

노트:-

  • 입력 파일을 늘려서 보면 다음과 같습니다.
sample of file1:
(
  (
    (
      (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
   )
 )
)

  • 따라서 우리는 이것이 본질적으로 목록의 모음 또는 목록의 목록이라는 것을 알 수 있습니다.
  • 우리의 문법은 이러한 관찰에 기초하고 있습니다.

출력:- cluster_[ABE].txt아래와 같이 결합된 출력으로 파일을 생성합니다.

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

관련 정보