문자열 대체를 위해 AWK와 함께 정규식을 사용하는 방법은 무엇입니까?


13

파일에 텍스트가 있다고 가정하십시오.

(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 23" "#2")
("Exercises 31" "#30")
("Notes and References 42" "#34"))
)

각 번호에 11을 추가 "하고 각 줄에 하나 를 추가하고 싶습니다.

(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 34" "#13")
("Exercises 42" "#41")
("Notes and References 53" "#45"))
)

다음은 GNU AWK와 정규식을 사용하는 솔루션입니다.

awk -F'#' 'NF>1{gsub(/"(\d+)\""/, "\1+11\"")}'

즉, 나는 대체 할 (\d+)\"함께 \1+10\"여기서 \1그룹이 표현됩니다 (\d+). 그러나 작동하지 않습니다. 어떻게 작동시킬 수 있습니까?

gawk가 최선의 해결책이 아닌 경우 다른 무엇을 사용할 수 있습니까?


복제에 대해 죄송합니다. 그러나 처음에 stackoverflow를 요청했지만 만족스러운 답변을 얻지 못했기 때문에 마이그레이션 플래그를 지정했습니다. 그러나 그것은 한동안 일어나지 않았기 때문에 나는 그것이 일어날 것이라고 기대하지 않았고 Unix.SE에 물었습니다.
Tim

답변:


12

이것을 시도하십시오 (개크가 필요합니다).

awk '{a=gensub(/.*#([0-9]+)(\").*/,"\\1","g",$0);if(a~/[0-9]+/) {gsub(/[0-9]+\"/,a+11"\"",$0);}print $0}' YourFile

예를 들어 테스트 하십시오.

kent$  echo '(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 2" "#2")
("Exercises 30" "#30")
("Notes and References 34" "#34"))
)
'|awk '{a=gensub(/.*#([0-9]+)(\").*/,"\\1","g",$0);if(a~/[0-9]+/) {gsub(/[0-9]+\"/,a+11"\"",$0);}print $0}'   
(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 13" "#13")
("Exercises 41" "#41")
("Notes and References 45" "#45"))
)

두 숫자 (예 : 1 "과"# 1 ")가 다르거 나이 패턴과 같은 줄에 더 많은 숫자가있는 경우 (예 : 23"... 32 "..."#)이 명령은 작동하지 않습니다. 한 줄에 123 ").


최신 정보

@Tim (OP)은 "같은 줄에 나오는 숫자 가 다를 수 있다고 말했기 때문에 이전 솔루션에서 일부 변경을 수행하여 새 예제에서 작동하게했습니다.

BTW, 예에서 나는 그것이 컨텐츠 구조의 테이블 일 수 있다고 생각하므로 두 숫자가 어떻게 다른지 알 수 없습니다. 첫 번째는 인쇄 된 페이지 번호이고, 두 번째는 페이지 색인입니다. 내가 맞아?

어쨌든, 당신은 당신의 요구 사항을 가장 잘 압니다. 이제 gawk를 사용하는 새로운 솔루션 (읽기 쉽도록 명령을 줄로 나눕니다) :

awk 'BEGIN{FS=OFS="\" \"#"}{if(NF<2){print;next;}
        a=gensub(/.* ([0-9]+)$/,"\\1","g",$1);
        b=gensub(/([0-9]+)\"/,"\\1","g",$2); 
        gsub(/[0-9]+$/,a+11,$1);
        gsub(/^[0-9]+/,b+11,$2);
        print $1,$2
}' yourFile

새로운 예제로 테스트 하십시오 .

kent$  echo '(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 23" "#2")
("Exercises 31" "#30")
("Notes and References 42" "#34"))
)
'|awk 'BEGIN{FS=OFS="\" \"#"}{if(NF<2){print;next;}
        a=gensub(/.* ([0-9]+)$/,"\\1","g",$1);
        b=gensub(/([0-9]+)\"/,"\\1","g",$2); 
        gsub(/[0-9]+$/,a+11,$1);
        gsub(/^[0-9]+/,b+11,$2);
        print $1,$2
}'                        
(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 34" "#13")
("Exercises 42" "#41")
("Notes and References 53" "#45"))
)


@Tim 의 의견에 따른 EDIT2

(1) FS = OFS = "\"\ "#"는 입력 및 출력에서 ​​필드 구분 기호가 큰 따옴표, 공백, 큰 따옴표 및 #을 의미합니까? 큰 따옴표를 두 번 지정하는 이유는 무엇입니까?

입력부와 출력 부에서 분리대를 사용할 수 있습니다. 구분 기호를 다음과 같이 정의했습니다.

" "#

두 개의 큰 따옴표가 있습니다. 예를 들어 입력 예제에 따라 원하는 두 숫자를 쉽게 잡을 수 있기 때문입니다.

(2) /.* ([0-9] +) $ /에서 $는 문자열의 끝을 의미합니까?

바로 그거죠!

(3) gensub ()의 세 번째 인수에서 "g"와 "G"의 차이점은 무엇입니까? G와 g 사이에는 차이가 없습니다. 이것 좀 봐:

gensub(regexp, replacement, how [, target]) #
    Search the target string target for matches of the regular expression regexp. 
    If "how" is a string beginning with g or G (short for global”), then 
        replace all matches of regexp with replacement.

http://www.gnu.org/s/gawk/manual/html_node/String-Functions.html 에서 가져온 것 입니다. gensub의 자세한 사용법을 읽으려면 읽을 수 있습니다.


감사! 예를 들어 1 "과"# 1 "의 두 숫자가 다른 경우 어떻게 작동하는지 궁금합니다.
Tim

이 답변은 현재 reqirement / 예제에 적용됩니다. 요구 사항이 변경되면 질문을 편집하고 더 나은 예를 제시 할 수 있습니다. 그리고 당신의 코드에서 awk -F'#', 당신은 '#'다음 부분에서만 변경을 원하는 것 같습니다.
Kent

제안 해 주셔서 감사합니다. 두 숫자가 동일하지 않도록 예제를 수정했습니다.
Tim

@Tim은 새로운 예를 위해 업데이트 된 답변을 참조하십시오.
Kent

감사! 몇 가지 질문 : (1) FS=OFS="\" \"#"입력 및 출력 모두에서 필드 구분 기호가 큰 따옴표, 공백, 큰 따옴표 및 #입니까? 큰 따옴표를 두 번 지정하는 이유는 무엇입니까? (2)에서는 /.* ([0-9]+)$/, 않습니다 $문자열의 끝을 의미? (3) gensub ()의 세 번째 인수에서 "g"와 의 차이점은 무엇 "G"입니까?
Tim

7

정규 표현식 대체를 제공하는 거의 모든 도구와 달리 awk는 \1대체 텍스트 와 같은 역 참조를 허용하지 않습니다 . 당신이 사용하는 경우 GNU awk는 일치하는 그룹에 대한 액세스를 제공 match기능을 ,하지만 함께 ~또는 subgsub.

또한 \1지원 되더라도 스 니펫은 +11숫자 계산을 수행하지 않고 문자열을 추가합니다 . 또한 정규 표현식이 옳지 "42""않습니다 "#42".

다음은 awk 솔루션입니다 (경고, 테스트되지 않음). 라인 당 단일 교체 만 수행합니다.

awk '
  match($0, /"#[0-9]+"/) {
    n = substr($0, RSTART+2, RLENGTH-3) + 11;
    $0 = substr($0, 1, RSTART+1) n substr($0, RSTART+RLENGTH-1)
  }
  1 {print}'

Perl에서는 더 간단합니다.

perl -pe 's/(?<="#)[0-9]+(?=")/$1+11/e'

답의 첫 번째 문장은 내가 찾던 것입니다. 그러나 "... 대체 텍스트에서"라고 말한 사실은 후속 질문을 제기합니다. awk는 정규식 패턴 자체에서 역 참조를 허용합니까?
와일드 카드

1
@Wildcard 아니요, awk는 그룹을 추적하지 않습니다 (내가 언급 한 GNU 확장명 제외).
Gilles 'SO- 악마 그만

5

awk그것을 할 수는 있지만 역 참조를 사용하더라도 직접적이지는 않습니다.
GNU awk 에는 gensub 형식의 (부분) 역 참조가 있습니다 .

의 인스턴스는 123"일시에 싸여 \x01\x02수정되지 않은 등 (그들을 표시하는 sub(). 공동

또는 루프 변경 후보를 진행할 때 역 참조 및 "브래킷"이 필요하지 않습니다. 그러나 문자 색인을 추적해야합니다.

awk '{$0=gensub(/([0-9]+)\"/, "\x01\\1\"\x02", "g", $0 )
      while ( match($0, /\x01[0-9]+\"\x02/) ) {
        temp=substr( $0, RSTART, RLENGTH )
        numb=substr( temp, 2, RLENGTH-3 ) + 11
        sub( /\x01[0-9]+\"\x02/, numb "\"" ) 
      } print }'

여기서 사용하는 또 다른 방법 gensub및 배열 split\x01(위한 필드 분리 분할 ) ... \ X02 표시는 산술 가산 후보로 배열 요소.

awk 'BEGIN{ ORS="" } {
     $0=gensub(/([0-9]+)\"/, "\x01\x02\\1\x01\"", "g", $0 )
     split( $0, a, "\x01" )
     for (i=0; i<length(a); i++) { 
       if( substr(a[i],1,1)=="\x02" ) { a[i]=substr(a[i],2) + 11 }
       print a[i]
     } print "\n" }'

감사! 첫 번째 코드에서 (1) 무엇을 "\x01\\1\"\x02"의미합니까? 난 아직도 이해가 안 \x01\x02. (2) 반환 방법 다른 $0의하여 gensub$0마지막 인수로는 gensub?
Tim

@ 팀. 16 진수 값 \x01이며 \x02대체 마커로 사용됩니다. 이러한 값은 일반 텍스트 파일 에있을 가능성 거의 없으므로 사용하기에 "똑같이"안전합니다 (예 : 기존 파일과 충돌하지 않음). 이는 임시 레이블 일뿐입니다. Re .. 참조 link String-Manipulation Functions , 요약하자면 : (gensub) 함수의 결과로 수정 된 문자열을 반환하며 원래 대상 문자열은 변경되지 않습니다. ... 단순히 원래 대상을 수정합니다.$0=gensub(... $0)$0=
Peter.O

3

(g) awk의 솔루션은 상당히 복잡해 보이므로 Perl에 다른 솔루션을 추가하고 싶었습니다.

perl -wpe 's/\d+(?=")/$&+11/eg' < in.txt > out.txt

설명:

  • 옵션 -w은 경고를 활성화합니다 (원치 않는 영향에 대해 경고합니다).
  • 옵션 -p은 sed 또는 awk와 유사하게 작동하는 코드 주위의 루프를 암시하여 각 입력 행을 기본 변수 인 자동으로 저장합니다 $_.
  • 옵션 -e은 프로그램 코드가 스크립트 파일이 아니라 명령 행을 따르고 있음을 perl에 알려줍니다.
  • 코드는 정규식 대체 ( s/.../.../)입니다 $_. 여기서 일련의 숫자 뒤에 a가 오는 경우 순서에 의해 "대치되며 숫자에 더하여 숫자 11을 더한 숫자로 해석됩니다.
  • 0 인 포지티브 룩 어설 션 (?=pattern) 을 찾습니다 "우리는 교체에 그것을 반복하지 않아도, 경기에 복용하지 않고. $&대체 의 MATCH 변수 는 숫자 만 포함합니다.
  • 그만큼 /e정규 표현식 수정자는 perl대체를 문자열로 사용하는 대신 코드로 "실행"하도록 지시 합니다.
  • 그만큼 /g수정 라인의 모든 경기에 반복, "글로벌"교체한다.

$&불행히도 MATCH 변수 는 5.20 이전의 Perl 버전에서 코드 성능을 저하시킵니다. 더 빠르고 (훨씬 복잡하지 않은) 솔루션은 그룹화와 역 참조를 $1대신 사용합니다.

perl -wpe 's/(\d+)?="/$1+11/eg' < in.txt > out.txt

미리보기 어설 션이 너무 혼란스러워지면 따옴표를 명시 적으로 바꿀 수도 있습니다.

perl -wpe 's/(\d+)"/$1+11 . q{"}/eg' < in.txt > out.txt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.