![하위 문자열을 해당 하위 문자열로 인덱싱된 사전에서 얻은 값으로 바꾸는 방법](https://linux55.com/image/154206/%ED%95%98%EC%9C%84%20%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84%20%ED%95%B4%EB%8B%B9%20%ED%95%98%EC%9C%84%20%EB%AC%B8%EC%9E%90%EC%97%B4%EB%A1%9C%20%EC%9D%B8%EB%8D%B1%EC%8B%B1%EB%90%9C%20%EC%82%AC%EC%A0%84%EC%97%90%EC%84%9C%20%EC%96%BB%EC%9D%80%20%EA%B0%92%EC%9C%BC%EB%A1%9C%20%EB%B0%94%EA%BE%B8%EB%8A%94%20%EB%B0%A9%EB%B2%95.png)
정규식과 일치하는 가능한 문자열의 일부인 가능한 하위 문자열을 인덱스가 관련 하위 문자열인 배열에서 가져온 값으로 대체하여 대용량 파일을 구문 분석해야 합니다.
파일은 일반 텍스트 파일입니다. 즉, 줄 바꿈으로 구분된 줄이며 각 줄에는 ASCII 32에서 ASCII 126 사이의 모든 문자, 기본적으로 C 로케일의 제어 문자를 제외한 모든 인쇄 가능한 문자가 포함될 수 있습니다.
관심 있는 문자열과 정확히 일치하는 확장 정규 표현식은 이고 \<prefix-[[:alnum:]]{2,}\>
, 문제의 하위 문자열은 대시 뒤의 모든 문자열입니다.
샘플(합성) 입력을 사용하세요. 예를 들면 다음과 같습니다.
# arbitrary number of comment lines of any length
:prefix-foo ; arbitrary strings
# arbitrary number of comment lines of any length foo -prefix-foo-
-bar -foo-xx arbitrary string -yet-more strings prefix-foo-bar MORE strings
YET more --STRINGS prefix-bar -prefix-foo-STRingS--
even MORE strings ; prefix -foo -yy--more-and-prefix-bar-and-more
다음과 같은 샘플 사전이 있습니다.
dictionary["foo"] = 2
dictionary["bar"] = 15
원하는 출력은 다음과 같습니다.
# arbitrary number of comment lines of any length
:prefix-2 ; arbitrary strings
# arbitrary number of comment lines of any length foo -prefix-2-
-bar -foo-xx arbitrary string -yet-more strings prefix-2-bar MORE strings
YET more --STRINGS prefix-15 -prefix-2-STRingS--
even MORE strings ; prefix -foo -yy--more-and-prefix-15-and-more
나는 이것이 최고의 도구라고 생각합니다. 특히 awk
단일 필드를 대체하여 전체 레코드를 다시 작성할 수 있는 기능이 본질적으로 있기 때문입니다. 그래서 나는 다음 스크립트를 생각해 냈습니다.$0
$1...$n
#!/usr/bin/gawk -f
BEGIN {
# first fill in dictionary
while ("cmd-providing-dictionary" | getline) {
dictionary[$1] = $2
}
close("cmd-providing-dictionary")
# pattern that matches interesting fields
field_regex = "\\<prefix-[[:alnum:]]{2,}\\>"
# I don't care default splitting of line
FS = OFS = ""
}
{
# split line in fields as per regex
if (patsplit($0, fields, field_regex, seps)) {
FS = OFS = "-"
# for each field, split it on dash character,
# modify its substring as per dictionary,
# and finally rebuild it
for (fn in fields) {
$0 = fields[fn]
if ($2 in dictionary) {
$2 = dictionary[$2]
fields[fn] = $0
}
}
FS = OFS = ""
# clear whole record and rebuild it with
# fields computed above + original separators
$0 = ""
for (fn in fields)
$fn = seps[fn - 1] fields[fn]
$(fn+1) = seps[fn]
}
print
}
비록 제가 awk를 잘 다루지 못하더라도 위의 코드는 충분히 빠르게 올바른 작업을 수행하는 것처럼 보이지만 약간 투박해 보이고 부 awk
자연스러운 방식으로 일이 일어나도록 강요하는 것처럼 느껴집니다. 동일한 결과를 얻는 더 좋은 방법이 있는지 궁금합니다. 아니면 더 나은 도구도 있습니다.
gsub()
내 첫 번째 생각은 or 를 사용하여 간단한 정규식 대체를 수행하는 것이었지만 gensub()
정규식의 하위 표현식(이 경우 \<prefix-([[:alnum:]]{2,})\>
)을 조회 배열로 사용하고 이를 대체 문자열에서 사용하는 (깨끗한) 방법을 찾지 못했습니다. 값. 반면에 모든 사전 키를 반복하여 all gsub
을 항상 적용하는 것은 실제로 실현 가능하지 않습니다. 사전이 매우 크고 따라서 매우 비효율적이기 때문입니다.
답변1
비교를 위해 대체 항목에서 함수를 호출할 수 있어 많은 이점을 얻을 수 있는 비전문가 버전의 Perl을 소개합니다. 마치 당신이 말할 수 있는 것처럼
gsub(regexp, call_function(matched_part), variable_to_change)
이 함수는 대체 문자열을 반환합니다.
#!/usr/bin/perl
use strict;
my %d;
sub fix{
my ($prefix,$str) = @_;
$str = $d{$str} if defined $d{$str};
return "$prefix$str";
}
open(D,"dictionary") or die;
while(<D>){
$d{$1} = $2 if $_ =~ m/^([^ ]+) ([^ \n]+)/;
}
close(D);
while(<>){
$_ =~ s/\b(prefix-)([[:alnum:]]{2,})\b/fix($1,$2)/ge;
print;
}
여기서 바꾸기 명령은 $_ =~ s/regex/fix($1,$2)/ge
현재 줄(g)을 전역적으로 변경하고 (e) 정규식 캡처 그룹 (내부) 에서 및 가 있는 $_
대체 문자열을 수행합니다 .fix()
$1
$2
()