문자열 대체를 위해 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기능을 ,하지만 함께 ~또는 sub나 gsub.

또한 \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.