정규 표현식: 가장 깊은 목록 수준만 일치

정규 표현식: 가장 깊은 목록 수준만 일치

게임에 필요한 재료 목록을 가장 상위부터 가장 원시적인 재료까지 정리했습니다. 그러나 이제는 숫자를 집계하는 빠른 방법을 찾고 있습니다.

21 reinforced alloy
    21 damascus steel
        21 steel
            21 iron dust
            21 carbon
            21 iron
        21 iron dust
        21 carbon
        21 iron
    21 hardened metal
        21 damascus steel
            21 steel
                21 iron dust
                21 carbon
                21 iron
            21 iron dust
            21 carbon
            21 iron
        21 duralmin
            21 aluminum dust
            21 copper dust
            21 aluminum
                21 aluminum dust
        21 compressed carbon
            84 carbon
        21 aluminum bronze
            21 aluminum dust
            21 bronze
                21 copper dust
                21 tin dust
                21 copper
            21 aluminum
                21 aluminum dust
    21 corinthian bronze
        21 silver dust
        21 gold dust
        21 copper dust
        21 bronze
            21 copper dust
            21 tin dust
            21 copper
    21 solder
        21 lead dust
        21 tin dust
        21 lead
            21 lead dust
    21 billon
        21 silver dust
        21 copper dust
        21 silver
            21 silver dust
    21 gold 24 carat

수집해야 할 원자재를 찾는 것이기 때문에 최상위 레벨은 중요하지 않습니다. 예를 들어, 21 hardened metal및 는 21 damascus steel합계를 찾고 있기 때문에 중요하지 않으며 , , 및 (이 예에서는 목록의 나머지 부분을 계산하지 않음) 원시 합계를 42 damascus steel찾고 있기 때문에 중요하지 않습니다 .42 iron dust42 carbon42 iron

지금까지 나는정규식 테스트 웹사이트grep, 하지만 결국에는 계산을 위해 웹사이트를 열 필요가 없도록 사용할 수 있게 되기를 바랍니다 . 나는 "탄소가 5번 나타나고 이것이 일치하는 선입니다"와 같은 것을 얻고 싶습니다. 탄소가 5번 나타나고 그 중 4번이 나타나고 21 carbon1번이 이라는 것을 알면 84 carbon이제 총 필요량을 쉽게 계산할 수 있기 때문에 더 쉽게 계산할 수 있습니다 21*4 + 84 = 168 carbon.

나는 다른 행이 없고 그 뒤에 많은 수의 탭이 있는 행의 수를 세려고 합니다. 만약 있다면 그것은 원시가 아닐 것이기 때문입니다.

/(\t+)\d+ aluminum\n(?!\1)/g("알루미늄"을 내가 찾으려는 원자재로 대체)

그러나 이것은 아무것도 밝혀지지 않았습니다. 정규식을 사용하여 달성하려는 목표를 달성할 수 있는 방법이 있습니까? 그렇다면 어떻게 해야 할까요?

시간 내 주셔서 감사합니다.


이것을 SO에 둘지 SE에 둘지는 잘 모르겠지만, 결국에는 사용할 수 있게 되기를 바란다는 점을 고려하면 grep그곳이 더 적절한 곳이 아닐까 싶습니다.

답변1

Perl과 유사한 정규식을 사용하려면 실제 정규식을 사용하는 것이 좋습니다.

<your-file perl -l -0777 -ne '
  while (m{^(\s*+)(\d+) (.*)$(?!\n\1\s)}mg) {
    $count{$3} += $2
  }
  END {
    printf "%4d %s\n", $count{$_}, $_ for sort keys %count
  }'

이것은 만든다:

  84 aluminum dust
 168 carbon
  42 copper
 105 copper dust
  21 gold 24 carat
  21 gold dust
  84 iron
  84 iron dust
  42 lead dust
  63 silver dust
  63 tin dust

-0777 -n전체 입력이 흡수된다는 의미입니다 $_. 연산자 m의 최종 플래그는 의 시작과 끝 뿐만 아니라 각 줄의 시작과 끝에서도 m{...}^일치를 발생시킵니다 . 플래그가 없으면 개행과 일치하는 항목이 없지만 입력에 빈 줄이 있으면 여기서 문제가 발생할 수 있다는 점에 유의하세요.$$_$_s.\s

\s*+예, 소급 적용되지 않은 버전입니다 \s*. \d+( ) 뒤의 내용은 공백과 일치할 수 없기 때문에 반드시 필요한 것은 아닙니다 .

Standard는 grep사용 중인 것과 같은 perl과 유사한 정규식과 \dperl RE 연산자를 지원하지 않지만 여러 줄 모드도 지원하는 이를 사용할 수 있습니다.(?!\1)pcregrep-o-M

<your-file pcregrep -Mo '^(\s*+)\K.*$(?!\n\1\s)'

perl합계 계산 과 같은 다른 작업을 위해서는 여전히 파이프가 필요하므로 모든 작업에 파이프를 사용하는 것보다 이점 awkperl거의 없습니다.

들여쓰기가 탭 및 공백과 혼합될 수 있는 경우, 입력을 이들 중 하나를 통해 전달하거나 expand먼저 unexpand공백 또는 탭으로 병합할 수 있습니다. 기본적으로 그들은 탭 정지를 대부분의 터미널이나 브라우저처럼 8열로 간주하지만(Stackexchange를 제외하면 귀찮게도 4열로 떨어져 있음) -t이를 변경하는 옵션을 참조하세요.

답변2

라인의 레벨 <= 다음 요소의 레벨인 경우 라인은 "프리미"입니다. 이는 다음과 같습니다.

이전 행은 해당 레벨 <= 현재 레벨인 경우(또는 마지막 행인 경우) 초기 행입니다.

NF마지막 필드로 필드 구분 기호 "\t", level 및 구성 요소와 함께 awk를 사용하십시오 $NF.

awk -F '\t' 'prevlev>=NF  {print primi}; 
                          {prevlev = NF; primi=$NF } 
             END          {print $NF}'

요약하자면 다음과 같은 내용을 실행할 수 있습니다.

... | sed 's/ /\t/' | datamash -g 2 -s sum 1

답변3

Lookbehind와 Lookahead를 사용해야 합니다. 또한 한 줄씩 처리하는 대신 전체 입력을 함께 처리해야 합니다. 다음 명령은 원하는 작업을 수행해야 합니다.

grep -Pzo '(?<=\n)(\s+)(\S[^\n]*)(?!\n\1\s)' input_file
  • -PPerl 구문을 활성화합니다.

  • -z개행 문자 대신 널 종결자를 사용하십시오.

  • -o일치하는 항목만 출력됩니다.

  • (?<=\n)줄바꿈을 찾아보세요. 대신 ^일반적으로 각 줄의 시작 부분과 일치합니다. 후속 부정적인 견해의 경우 를 사용하십시오 (?<!...). 아마도 항상 더 깊은 수준이 있기 때문에 첫 번째 줄을 무시합니다. 그렇지 않은 경우 로 보내기 전에 입력 시작 부분에 새 줄을 추가할 수 있습니다 grep. 이 작업을 수행하는 더 좋은 방법이 있을 수 있지만 다음은 하나입니다.

    ( echo ; cat input_file ) | grep ...
    
  • (\s+)들여쓰기 수준을 캡처합니다. 이것은 나중에 호출됩니다 \1. \s공백과 일치합니다. 이에 대한 한 가지 잠재적인 문제는 개행 문자가 들여쓰기의 일부로 간주될 수 있다는 것입니다. 예를 들어, 이중 줄 바꿈은 단락 구분 기호로 자주 사용됩니다. \s들여쓰기에 사용하려는 특정 공백( )으로 바꿀 수 있습니다 [\ \t].

  • (\S[^\n]*)관심 있는 텍스트를 캡처하세요. \S공백이 아닌 것과 일치합니다. [^\n]개행 문자가 아닌 모든 문자와 일치합니다.

  • (?!\n\1\s)부정적인 미리보기는 다음 줄이 현재 줄보다 깊게 들여쓰기되지 않도록 보장합니다. 긍정적인 전망을 원하시면 를 사용하세요 (?=...).

관련 정보