큰 텍스트 파일에서 여러 번 나타나는 "키"를 해당 대체 "값"으로 바꿉니다.

큰 텍스트 파일에서 여러 번 나타나는 "키"를 해당 대체 "값"으로 바꿉니다.

큰 텍스트 파일에서 여러 단어("키"라고 함)를 다른 대체 텍스트("값"이라고 함)로 바꿔야 합니다. 현재 나는 sed이 목적으로 다음과 같은 것을 사용합니다.

sed -i -e 's/\bkey\b/value/' file

파일이 커서 프로세스에 몇 분이 걸립니다. 1,000개가 넘는 키-값 쌍이 있으며 현재 sed각 키-값 쌍에 대해 프로세스를 반복하고 있습니다. 분명히 이것은 오랜 시간이 걸립니다.

한 번에(또는 더 빠르게) 교체를 수행할 수 있도록 "키-값"(패턴 교체) 쌍 세트를 sed/ 또는 유사한 유틸리티 에 입력하는 방법이 있는지 궁금합니다 . awk키-값 쌍은 어떤 형식으로든 구성될 수 있습니다.

예를 들어 이름을 약어(예: TSV 형식)로 바꾸는 것입니다.

Key                                               Value
United Nations                                    UN
United States Environmental Protection Agency     EPA
International Atomic Energy Agency                IAEA
World Health Organization                         WHO

입력 텍스트는 다음과 같습니다.

이는 UN과 세계보건기구(WHO)에서 보고한 내용입니다. 이것이 IAEA의 주요 영역이다. 미국 환경 보호국은 이 문제를 감독하는 연방 기관입니다.

답변1

여기서는 -i둘 다 및 의 \b일부 sed구현 입니다 perl. 먼저 다음을 사용하는 것이 좋습니다 perl.

perl -i -pe '
  BEGIN {
    %map = (
      "key1"  => "value1",
      "key 2" => "value2"
    );
    $re = join "|", map {qr{\Q$_\E}} keys %map;
  }
  s/\b(?:$re)\b/$map{$&}/g' your-file

키 => 값 매핑은 다음과 같이 표현할 수도 있습니다.

%map = qw(
   key1 value1
   key2 value2
);

또는 해당 perl 모듈( Text::CSV, )을 사용하여 JSONCSV 또는 기타 구조화된 형식에서 읽습니다 perl. 텍스트 조작에 적합한 범용 프로그래밍 언어이므로 여기서는 확실한 선택이며 수행할 수 있는 작업에는 제한이 없습니다. .

간단한 TSV의 경우 다음과 같습니다.

<map.tsv perl -i -pe '
  BEGIN {
    <STDIN>; # skip header
    while (<STDIN>) {
      chomp;
      my ($k, $v) = split /\t/;
      $map{$k} = $v;
    }

    $re = join "|", map {qr{\Q$_\E}} keys %map;
  }
  s/\b(?:$re)\b/$map{$&}/g' your-file

다음 작업을 수행하는 경우 참고하세요.

sed -i -e 's/\bK1\b/V1/g' file
sed -i -e 's/\bK2\b/V2/g' file

다음과 같이 단순화할 수 있습니다.

sed -i '
  s/\bK1\b/V1/g
  s/\bK2\b/V2/g' file

또는 TSV의 경우:

<map.tsv awk -F'\t' '
   NR > 1 {
     # escape regexp operators in keys to emulate perl \Q \E:
     gsub(/[][\/\\*.^$]/, "\\\\&", $1)
     # escape /, \ and & in replacement:
     gsub(/[\\/&]/, "\\\\&", $2)
     print "s/\\b"$1"\\b/"$2"/g"
   }' | sed -i -f - your-file

파일을 한 번만 읽고 씁니다.

그러나 두 경우 모두 일부 경우가치그 중에도열쇠. 예를 들어 s/\bA\b/B/g힐을 사용 하면 s 대신 s가 s/\bB\b/C/g표시됩니다 . 위 방법은 ubtitute 연산자 만 실행하기 때문에 문제가 없습니다 .ACBperls

또한 perl정규 표현식에서는 왼쪽에서 오른쪽으로의 교대를 처리하므로 입력 s/\b(?:foo|foo bar)\b/$map{$&}/g에 , 가 있는 경우 대신 foo bar이를 대체합니다 .foofoo bar

연관 배열은 무작위 순서로 탐색된다는 점을 기억하세요.

sed-E-r( BRE를 사용하거나 BRE에서 확장 정규식을 지원하는 구현 의 경우 \|) 대신 가장 긴 일치 항목을 찾으려고 시도합니다.

perl를 결합하기 전에 키를 길이별로 정렬하여 동일한 동작을 얻을 수 있습니다 (예: 로 |대체 ) .keys %mapsort {length$b <=> length$a} keys %map

마지막 참고 사항: perl기본적으로 입력은 바이트 단위로 처리되며, 단어 문자( \b단어와 비단어 문자 사이의 경계와 일치)는 ASCII 문자, 숫자 및 밑줄로 제한되며 sed구현에서는 일반적으로 로케일의 문자 집합에 따라 이를 디코딩합니다. 입력 또는 키/값에 ASCII가 아닌 문자가 포함된 경우 추가하여 로캘의 문자 집합에 따라 디코딩할 수 있습니다 -Mopen=locale. 또는 UTF-8(현재 가장 일반적인 로캘 인코딩)인 경우 해당 -C옵션을 추가하면 됩니다.

답변2

제공된 예와 같이 Sunny 매핑만 처리하면 된다고 가정하면(예: 정규식 또는 역참조 메타 문자 없음, 대소문자 변경 없음, 하위 문자열 없음, 루프 매핑 없음 등) awk를 사용하세요.

$ awk -F'\t+' '
    NR==FNR { if (NR>1) map[$1]=$2; next }
    { for (key in map) gsub(key,map[key]); print }
' map_file input_file
This has been covered by both the UN and WHO. This is the main domain of the IAEA. EPA is a federal agency supervising this matter.

이것이 필요한 전부가 아니라면 질문을 편집하여 보다 대표적인 입력/출력 예제를 제공하십시오.

답변3

사용행복하다(이전 Perl_6)

~$ raku -pe 'BEGIN my %h = ("United Nations" => "UN",  \
             "United States Environmental Protection Agency" => "EPA",  \
             "International Atomic Energy Agency" => "IAEA",  \
             "World Health Organization" => "WHO");  \
             s:g/@(%h.keys)/%h{$/}/;'   file

또는:

~$ raku -ne 'BEGIN my %h = ("United Nations" => "UN",  \
             "United States Environmental Protection Agency" => "EPA",  \
             "International Atomic Energy Agency" => "IAEA",  \
             "World Health Organization" => "WHO");  \
             put S:g/@(%h.keys)/%h{$/}/ given $_;'   file

입력 예:

This has been covered by both the United Nations and World Health Organization. This is the main domain of the International Atomic Energy Agency. United States Environmental Protection Agency is a federal agency supervising this matter.

예제 출력:

This has been covered by both the UN and WHO. This is the main domain of the IAEA. EPA is a federal agency supervising this matter.

Raku는 Perl 프로그래밍 언어 계열의 프로그래밍 언어이며, 이 답변은 기본적으로 @Stéphane_Chazelas가 게시한 훌륭한 Perl 답변을 번역한 것입니다. Raku의 가장 좋은 "사용 사례"는 아마도 유니코드 대체를 일관되게 처리해야 하는 경우일 것입니다. Raku는 내장 유니코드에 대한 고급 지원을 제공하기 때문입니다.

즉, 관심 있는 / 쌍을 사용하여 해시를 생성합니다 %h. 참고 - Raku 정규식에서 해시를 직접 사용하려고 하면 다음 경고가 표시됩니다.keyvalue정규식에 해시 변수를 계속 사용하세요.대신 %h.keys해시 값을 keys먼저 가져와서 @(…)일치자 절반의 배열로 캐스팅합니다(정규식 일치자의 -sigiled 또는 -sigiled 변수는 $Raku에게 문자 그대로 문자열화된 콘텐츠를 삽입하도록 지시합니다). 대체 반에서 일치 변수는 / 쌍으로 디코딩된 해당 값입니다.@$/valuekeyvalue

[두 번째 예에서는 -neRaku의 "big-S" 표기법과 함께 명령줄 플래그를 사용하여 S///결과 문자열을 반환합니다].

물론, 주어진 다른 답변을 더 완벽하게 복제하려면 Raku의 너비가 0인 단어 경계 앵커인 <|w>또는 를 사용할 수 있습니다. 이는 다른 언어의 앵커와 동일합니다. 따라서 위의 마지막 줄은 다음과 같습니다.<?wb>\b

s:g/ <?wb> @(%h.keys) <?wb> /%h{$/}/;

<<Raku의 왼쪽 및 오른쪽 경계를 사용할 수도 있습니다 >>(유니코드 기호도 작동 «»).

s:g/ << @(%h.keys) >> /%h{$/}/;


TSV 파일로 시작합니다.

위의 인라인이 아닌 2열 TSV 파일에서 키/값 쌍을 가져오면 코드가 훨씬 간단해집니다. Text::CSV다음과 같이 명령줄에서 Raku 모듈을 사용하세요(참고: .skip(1)TSV 파일에 헤더가 없으면 이 호출을 제거하세요). .[*;*]Raku는 각 행을 해시에 추가된 두 요소(키와 값)로 처리하므로 색인 대괄호 코드를 포함하는 것을 잊지 않는 것이 중요합니다 %h.

~$ raku -MText::CSV -pe 'BEGIN my %h = csv(in => "/path/to/kv_pairs.tsv", sep => "\t").skip(1).[*;*];   
                         s:g/ << @(%h.keys) >> /%h{$/}/;'   file
This has been covered by both the UN and WHO. This is the main domain of the IAEA. EPA is a federal agency supervising this matter.

또는:

~$ raku -MText::CSV -ne 'BEGIN my %h = csv(in => "/path/to/kv_pairs.tsv", sep => "\t").skip(1).[*;*];   
                         put S:g/ << @(%h.keys) >> /%h{$/}/ given $_;'   file
This has been covered by both the UN and WHO. This is the main domain of the IAEA. EPA is a federal agency supervising this matter.

https://docs.raku.org/언어/regexes
https://docs.raku.org
https://raku.org

답변4

우리가 말하면

  • 대안가치키-값 맵 파일 자체에는 다음을 포함할 수 없습니다.열쇠이를 위해서는 대체 항목(자체 관련 키 포함!)이 필요합니다.
  • 매핑 파일은 탭으로 구분됩니다.

다음 awk절차가 작동합니다.

awk -F'\t' 'NR==FNR{repl[$1]=$2;klen[$1]=length($1);next}
            {for (key in repl) {
               while (i=index($0,key)) {
                 $0=substr($0,1,i-1) repl[key] substr($0,i+klen[key])
               }
             }
            }1' mapfile.txt input.txt

그러면 먼저 입력 필드 구분 기호가 TAB으로 설정되고 매핑 파일이 먼저 처리된 다음 실제 입력 파일이 처리됩니다.

  • 첫 번째 파일( 글로벌 라인 카운터 FNR와 동일한 파일별 라인 카운터로 표시됨)을 처리할 때 수행할 대체 항목으로 배열을 채우고 별도의 배열에서 "키" 길이를 추적합니다 . 그런 다음 처리를 위해 다음 줄로 이동합니다.NRreplklen
  • 두 번째 파일을 처리할 때 조건이 NR==FNR더 이상 충족되지 않으므로 건너뜁니다. repl각 입력 줄에 대한 모든 대체 키(예: 배열의 모든 인덱스)를 반복하고 이 index()함수를 사용하여 입력 줄에 나타나는지 확인합니다.
  • key그렇다면 하위 문자열에서 입력 행을 다시 조립하여 발생 항목을 바꿉니다.앞으로, 교체 key후 다음 하위 문자열 key.
  • 특정 입력 라인이 두 번 이상 나타나는 while경우 모든 항목이 대체되도록 루프에서 이 작업을 수행합니다 .key
  • 정규식 기반이 아닌 이 "수동" 접근 방식을 사용하는 이유는 이 방법을 사용하면 gsub()값이 표시되는 방식에 대한 제한이 없기 때문입니다. 정규식 관련 문자와 함께 를 사용하면 예기치 않은 동작이 발생할 수 있습니다 key.gsub()key

입력 예의 경우 출력은 다음과 같습니다.

이는 UN과 세계보건기구(WHO)에서 보고한 내용입니다. 이것이 IAEA의 주요 영역이다. EPA는 이 문제를 감독하는 연방 기관입니다.

노트모든 awk버전과 구현이 내부 편집을 수행할 수 있는 것은 아닙니다( flags 와 동일 -i). 상당히 새로운 GNU Awk(>4.1.0)가 있는 경우 -i inplace이 기능의 확장을 사용할 수 있습니다.

또한 현재 형식에서 이 프로그램은 대체에 대한 "단어 경계" 제약 조건을 구현하지 않습니다.

관련 정보