AWK에서 정규 표현식의 욕심을 줄이는 방법은 무엇입니까?


14

에서 욕심없는 패턴 (정규 표현식) 일치를 원합니다 awk. 예를 들면 다음과 같습니다.

echo "@article{gjn, Author =   {Grzegorz J. Nalepa}, " | awk '{ sub(/@.*,/,""); print }'

더 짧은 문자열을 선택하는 정규식을 작성할 수 있습니까?

@article{gjn,

이 긴 문자열 대신에? :

@article{gjn, Author =   {Grzegorz J. Nalepa},

이 결과를 얻고 싶습니다 :

 Author =   {Grzegorz J. Nalepa},



다른 예가 있습니다.

echo " , 기사 {gjn, 저자 = {Grzegorz J. Nalepa},"| awk '{sub (/ , [^,] *, /, ""); 인쇄} '
      ↑ ^^^^^^^

내가 변경된 것을 참고 @쉼표 (문자를 ,입력 문자열과 정규 표현식 모두의 첫 번째 위치에) 문자 (및 변경 .*[^,]*). 더 짧은 문자열을 선택하는 정규식을 작성할 수 있습니까?

, Author =   {Grzegorz J. Nalepa},

더 긴 문자열 대신에? :

,article{gjn, Author =   {Grzegorz J. Nalepa},

이 결과를 얻고 싶습니다 :

,article{gjn

4
정규 표현식이 강력한 HTML 구문 분석에 적합하지 않은 것처럼, 이런 종류의 상황에 맞는 문법 구문 분석을 수행 할 수 없을 것입니다. 그러나 입력 집합이 상당히 제한되어 있고 제대로 구성되어 있으면 제한 사항을 선언하는 한 정규 표현식을 사용할 수 있습니다. 예를 들어 당신이 볼 수 있었다 Author공백 뒤에 쉼표와 공백을, 다음은 다음에 =다음에 공백 다음에 {어떤 비 다음에 }다음에 }당신이 중첩 할 수 없습니다이 (다른 것들 사이)이 필요하지만, {}안쪽 = { ... }부분.
jw013

@ jw013, 설명해 주셔서 감사합니다. 그러나 다른 사용자의 제안을 기다릴 것입니다.
nowy1

답변:


18

그 후 @첫 번째 를 선택하려면 다음 과 ,같이 지정해야합니다.@[^,]*,

@뒤에는 *쉼표 ( [^,]) 가 아닌 임의의 수 ( )가 옵니다 ,.

그 접근 방식은와 동등한 것으로 작동 @.*?,하지만와 같은 것은 아닙니다 @.*?string. 그 이후의 것이 단일 문자 이상입니다. 문자를 부정하는 것은 쉽지만 정규 표현식에서 문자열을 부정하는 것은 훨씬 어렵습니다 .

다른 접근 방식은 입력에서 사전 처리하여 입력에서 string발생하지 않는 문자 를 대체하거나 앞에 추가하는 것 입니다.

gsub(/string/, "\1&") # pre-process
gsub(/@[^\1]*\1string/, "")
gsub(/\1/, "") # revert the pre-processing

입력에 대체 문자 ( \1위)가 포함되지 않는다고 보장 할 수없는 경우 이스케이프 메커니즘을 사용하는 방법이 있습니다.

gsub(/\1/, "\1\3") # use \1 as the escape character and escape itself as \1\3
                   # in case it's present in the input
gsub(/\2/, "\1\4") # use \2 as our maker character and escape it
                   # as \1\4 in case it's present in the input
gsub(/string/, "\2&") # mark the "string" occurrences

gsub(/@[^\2]*\2string/, "")

# then roll back the marking and escaping
gsub(/\2/, "")
gsub(/\1\4/, "\2")
gsub(/\1\3/, "\1")

그것은 fixed에서는 작동 string하지만와 동등한 임의의 정규 표현식에는 작동 하지 않습니다 @.*?foo.bar.


좋은 답변 감사합니다. 편집 할 때 또 다른 예를 물었습니다 (편집 참조).
nowy1

6

awk욕심없는 일치를 수행 할 수없는 해결 방법을 제공하는 몇 가지 좋은 답변이 이미 있으므로 Perl Compatible Regular Expressions (PCRE)를 사용하여 다른 방법으로 정보를 제공하고 있습니다. 명령 행 옵션 awkperl사용하여 가장 간단한 "일치 및 인쇄" 스크립트를 쉽게 다시 구현할 -n수 있으며 a2p Awk를 Perl 변환기로 더 복잡한 스크립트를 변환 할 수 있습니다 .

Perl 에는 욕심없는 연산자가 있으며 Perl 스크립트 및 PCRE를 사용하는 모든 것에 사용할 수 있습니다. 예를 들어 GNU grep의 -P옵션 에서도 구현됩니다 .

PCRE는 Perl의 정규식 과 동일하지 않지만 매우 가깝습니다. 매우 빠르며 확장 정규 표현식에 대한 Perl 향상 기능이 매우 유용하기 때문에 많은 프로그램에서 일반 표현식 라이브러리로 널리 사용됩니다.

로부터 perlre (1) 매뉴얼 페이지

   By default, a quantified subpattern is "greedy", that is, it will match
   as many times as possible (given a particular starting location) while
   still allowing the rest of the pattern to match.  If you want it to
   match the minimum number of times possible, follow the quantifier with
   a "?".  Note that the meanings don't change, just the "greediness":

       *?        Match 0 or more times, not greedily
       +?        Match 1 or more times, not greedily
       ??        Match 0 or 1 time, not greedily
       {n}?      Match exactly n times, not greedily (redundant)
       {n,}?     Match at least n times, not greedily
       {n,m}?    Match at least n but not more than m times, not greedily

3

이 게시물은 오래된 게시물이지만 다음 정보는 다른 사람에게 유용 할 수 있습니다.

awk에서 욕심없는 RE 매칭을 수행하는 방법은 명백하게 조잡합니다. 기본 아이디어는 match (string, RE) 함수를 사용하고 일치하지 않을 때까지 (예상치 않은) 문자열 크기를 점진적으로 줄이는 것입니다.

if (match(string, RE)) {
    rstart = RSTART
    for (i=RLENGTH; i>=1; i--)
        if (!(match(substr(string,1,rstart+i-1), RE))) break;
    # At this point, the non-greedy match will start at rstart
    #  for a length of i+1
}

2

일반적인 표현의 경우 이것은 욕심없는 일치로 사용할 수 있습니다.

function smatch(s, r) {
    if (match(s, r)) {
        m = RSTART
        do {
            n = RLENGTH
        } while (match(substr(s, m, n - 1), r))
        RSTART = m
        RLENGTH = n
        return RSTART
    } else return 0
}

@JimMellander의 답변을 기반으로 이것을 사용하고 있습니다. smatch다음과 같이 동작합니다 match.

s 정규식이 r발생 하는 위치 , 그렇지 않은 경우는 0 변수 RSTART와는 RLENGTH일치하는 문자열의 위치 및 길이로 설정된다.


1

욕심없는 매칭을하는 방법은 없습니다. 그래도 원하는 출력을 얻을 수 있습니다. sch의 제안은 해당 라인에서 작동합니다. 쉼표를 사용할 수 없지만 "저자"가 항상 원하는 것의 시작 인 경우 다음을 수행 할 수 있습니다.

awk '{ sub(/@.*Author/,"Author"); print }'

Author 앞에 오는 문자 수가 항상 같은 경우 다음을 수행 할 수 있습니다.

awk '{ sub(/@.{21}/,""); print }'

전체 세트에서 데이터가 어떻게 보이는지 알아야합니다.


0

방법은 항상있다. 쉼표를 구분 기호로 사용하면 주어진 문제를 상당히 쉽게 해결할 수 있습니다.

echo "@article{gjn2010jucs, Author =   {Grzegorz J. Nalepa}, " |
awk -F, '{sub(/^[ \t]/, "", $2); print $2}'

필드의 수가 다양 할 때 일반적으로 약간 더 나은 것이 필요합니다. 이러한 경우, 중지 단어를 찾는 것은 종종 그 단어를 사용하여 줄에서 무엇이든 잘라낼 수 있기 때문에 돈을 지불합니다. 예제와 관련하여 여기에 정지 단어가 의미하는 바가 있습니다.

echo "@article{gjn2010jucs, Author =   {Grzegorz J. Nalepa}, " |
awk  '{sub(/.*Author/, "Author", $0); sub(/},.*/, "}", $0); print $0}'

0

나는 이것이 오래된 게시물이라는 것을 알고있다. 그러나 요청에 따라 awk를 OP로 사용하는 것이 있습니다.
A = @ article {gjn2010jucs, Author = {Grzegorz J. Nalepa},
echo $ A | awk 'sub (/ @ [^,] * /, "")'

출력 :
, 저자 = {Grzegorz J. Nalepa},


1
그 대답은 약 5 가지 이유로 잘못되었습니다.
Scott

3
무엇이 잘못되었는지 이해하도록 도와 주실 수 있습니까? 출력은 요청한 것과 일치하는 것 같습니다. 왜 대답이 옳고 그른지 이해하려고 노력합니다.
VINAY NAIR
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.