다음과 같은 식별자가 포함된 파일이 있습니다.
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]//gr
s
대체(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)