큰 문자열의 일치 항목 수를 빠르게 계산

큰 문자열의 일치 항목 수를 빠르게 계산

한 줄에 공백도 없고 다른 줄도 없는 텍스트 데이터가 많이 있습니다. 실제로 흐름은 0.2Gb/s입니다.여기, 그러나 이 작업에서는 발생 횟수를 계산하는 것이 빈 줄을 계산하는 것보다 더 어렵습니다. 게임은

585e0000fe5a1eda480000000d00030007000000cd010000

예제 데이터 하위 집합은 다음과 같습니다.여기라고2015.6.30_data.txt완전한 바이너리 데이터여기라고0002.원시. 경기는 1회 발생했습니다.2015.6.30_data.txt하지만 전체 데이터에서는 10배입니다.0002.원시한 줄에. 를 통해 txt 데이터를 준비했습니다 xxd -ps 0002.raw > /tmp/1 && fold -w2 /tmp/1 > /tmp/2 && gsed ':a;N;$!ba;s/\n//g' /tmp/2 > /tmp/3. 구현은 빠를수록 좋습니다. 열에 큰 문자열을 준비하려면 이것을 사용할 수 있습니다 xxd -ps 0002.raw > /tmp/1 && fold -w2 /tmp/1 > /tmp/2. 현재 속도는 게임당 0.0012초입니다. 이는 전체 데이터 파일에서 10게임당 0.012초이므로 느립니다.

Grep은 이 작업을 행별로 수행하므로 셀 수 없습니다. Vim에서는 %s/veryLongThing//gn작업을 완료하는 것만으로는 충분하지 않습니다. 이 명령은 wc문자, 바이트, 줄만 제공하므로 적합한 도구는 아니지만 다른 것과 결합하는 것은 가능합니다. 아마도 GNU Find와 Sed의 조합일 것입니다. 하지만 모든 구현이 너무 복잡해 보입니다.

Mikeserv의 답변 결과

$ cat 1.7.2015.sh 
time \
    ( export ggrep="$(printf '^ \376Z\36\332H \r \3 \a \315\1')" \
             gtr='\1\3\a\r\36HZ^\315\332\376'
             LC_ALL=C
      gtr -cs "$gtr" ' [\n*]' |
      gcut -sd\  -f1-6       |
      ggrep -xFc "$ggrep"
    ) <0002.raw

$ sh 1.7.2015.sh 
1

real    0m0.009s
user    0m0.006s
sys 0m0.007s

-----------

$ cat 1.7.2015.sh 
time \
    (  set      x58 x5e x20 x20 xfe x5a x1e xda \
                x48 x20 x20 x20 x0d x20 x03 x20 \
                x07 x20 x20 x20 xcd x01 x20 x20
        export  ggrep="$(shift;IFS=\\;printf "\\$*")"    \
                gtr='\0\1\3\a\r\36HXZ^\315\332\376'      \
                LC_ALL=C i=0
        while [ "$((i+=1))" -lt 1000 ]
        do    gcat 0002.raw; done            |
        gtr -cd "$gtr" |gtr 'X\0' '\n '      |
        gcut -c-23    |ggrep -xFc "$ggrep"
    ) 

$ sh 1.7.2015.sh 
9990

real    0m4.371s
user    0m1.548s
sys 0m2.167s

모든 도구는 GNU coreutils이며 코드에 제공하는 모든 옵션이 있습니다. 그러나 GNU devtools와는 다를 수 있습니다. Mikeserv는 코드를 990번 실행하고 10개의 이벤트를 가지므로 총 9990개의 이벤트가 정확합니다.

슈퍼 문자열의 일치 항목 수를 효율적으로 계산하는 방법은 무엇입니까?

답변1

GNU 구현 grep(최신 버전은 완전한(대부분 호환) 재작성이지만 대부분의 최신 BSD에도 있음)은 -o출력 옵션을 지원합니다.모두일치하는 부품.

LC_ALL=C grep -ao CDA | wc -l

그러면 모든 발생 횟수가 계산됩니다.

LC_ALL=C grep -abo CDA

바이트 오프셋으로 찾으십시오.

LC_ALL=Cgrep비용이 많이 드는 UTF-8 구문 분석을 시도하지 않도록 하십시오 (여기서 고정된 ASCII 문자열 검색을 사용하면 grep자체적으로 UTF-8 구문 분석을 최적화할 수 있어야 하지만). 바이너리에 대해 생각하라고 -a말하는 것은 또 다른 GNUism입니다 .grep

답변2

그래서 16진수 문자열을 바이트로 인쇄했지만 NUL을 <spaces>로 바꿨습니다.(주로 스키마에서 NUL을 얻는 방법을 모르기 때문입니다 grep):

time \
    (  set      x58 x5e x20 x20 xfe x5a x1e xda \
                x48 x20 x20 x20 x0d x20 x03 x20 \
                x07 x20 x20 x20 xcd x01 x20 x20
        export  grep="$(shift;IFS=\\;printf "\\$*")"    \
                tr='\0\1\3\a\r\36HXZ^\315\332\376'      \
                LC_ALL=C i=0
        while [ "$((i+=1))" -lt 1000 ]
        do    cat 0002.raw; done     |
        tr -cd "$tr" |tr 'X\0' '\n ' |
        cut -c-23    |grep -xFc "$grep"
    )

거기에 있는 변수는 16진수 문자열의 바이트 값에 대한 8진수 이스케이프/ASCII 문자로 구성됩니다. 왜냐하면 그 보수를 제거 tr하고 싶기 때문입니다 . 그런 다음 일치시키려고 시도할 수 있는 가장 긴 줄이 가 있는 바이트 인지 확인 하고 X 문자를 ewline으로 변환하는 동시에 NUL을 <spaces>로 대체하여 문자열이 항상 줄의 제목이 되도록 했습니다 .tr-dgrep-c-23cuttr\n

cat여기서는 파이프라인에서 원본 바이너리를 999번 실행했습니다 . 파일에 10개의 일치 항목이 있으므로 결과는 다음과 같습니다.

9990
1.06s user 0.94s system 65% cpu 3.054 total

이제 나도 테스트해봤는데..

time \
    (  set      x58 x5e x20 x20 xfe x5a x1e xda \
                x48 x20 x20 x20 x0d x20 x03 x20 \
                x07 x20 x20 x20 xcd x01 x20 x20
        export  LC_ALL=C i=0 grep="$(IFS=\\;printf "\\$*")"
        while [ "$((i+=1))" -lt 1000 ]
        do    cat 0002.raw;  done    |
        tr '\0 ' ' \0'   |
        grep -aFo "$grep"| wc -l
    )

나는 거기를 사용하고 있지만 테스트에서 완전히 사용하고 제거해도 wc -l실행 시간에 영향을 미치지 않는 것 같습니다. 어쨌든 개수는 동일합니다. 결과는 다음과 같습니다.-caFowc

9990
1.56s user 1.46s system 82% cpu 3.648 total

이제 이 두 명령 세트는 동일하지 않습니다. 원하지 않는 바이트를 먼저 짜내면 더 빠르게 수행되는 것처럼 보이지만 이는 개수를 얻을 수 있지만 tr두 번째 예에서 스위치를 추가하여-b 오프셋을 얻을 수 없다는 것을 의미합니다.....grep

time \
   (    set     x58 x5e x20 x20 xfe x5a x1e xda \
                x48 x20 x20 x20 x0d x20 x03 x20 \
                x07 x20 x20 x20 xcd x01 x20 x20
        export  LC_ALL=C i=0 grep="$(IFS=\\;printf "\\$*")"
        while [ "$((i+=1))" -lt 1000 ]
        do    cat 0002.raw;  done    |
        tr '\0 ' ' \0'     |
        grep -baFo "$grep" | sed -n l
   )

...

241133568:X^  \376Z\036\332H   \r \003 \a   \315\001  $
241157720:X^  \376Z\036\332H   \r \003 \a   \315\001  $
241181872:X^  \376Z\036\332H   \r \003 \a   \315\001  $
241206024:X^  \376Z\036\332H   \r \003 \a   \315\001  $
241230176:X^  \376Z\036\332H   \r \003 \a   \315\001  $
241254328:X^  \376Z\036\332H   \r \003 \a   \315\001  $

1.59s user 1.41s system 85% cpu 3.496 total

그래서 어떤 것을 선택하든 당신이 원하는 것이 무엇인지에 따라 달라질 것이라고 생각합니다. 수학적으로만 계산하면 tr -cd더 나을 것입니다. 매번 다른 방법보다 0.5초 더 빠릅니다. 하지만 그렇게 일반적이지는 않으므로 기꺼이 grep지원한다면 grep -baFo필요할 수도 있습니다.

관련 정보