큰 기록/단락 처리

큰 기록/단락 처리

구분 기호가 있는 레코드가 포함된 큰 텍스트 파일(300MB)이 있습니다 \n\n. 각 줄은 필드이며 숫자(필드 태그/이름)로 시작하고 그 뒤에 TAB 문자와 필드 내용/값이 옵니다.

110    something from record 1, field 110
149    something else
111    any field could be repeatable
111    any number of times
120    another field

107    something from record 2, field 107
149    fields could be repeatable
149    a lot of times
149    I mean a LOT!
130    another field

107    something from record 3
149    something else

각 레코드는 100KB를 초과할 수 없습니다.

문제가 있는 기록(한도보다 큼)을 찾을 수 있는 방법은 다음과 같습니다.이 레코드/"단락"에서 줄 끝 제거그리고길이를 얻는 중:

cat records.txt | awk ' /^$/ { print; } /./ { printf("%s ", $0); } ' | awk '{print length+1}' | sort -rn | grep -P "^\d{6,}$"

다음 중 하나를 통해 잘못된 기록을 처리하는 방법을 찾으려고 합니다.

  • 제한보다 큰 레코드를 제거합니다.
  • 알려진 특정 문제 태그(위 예에서는 149)의 모든 발생을 제거합니다. 149 필드로 시작하는 모든 줄이 제거되면 제한을 초과하는 레코드가 없다는 가설은 허용됩니다.

아마도 한도에 맞게 특정 필드/태그의 발생을 충분히 제거하면 전체 스크립트가 필요할 수 있습니다. 마지막 항목을 먼저 제거하는 것이 더 좋습니다.

이는 고대 사서 파일 형식과 관련이 있습니다.ISO 2709.

답변1

문제가 있는 기록을 건너뛰고 싶다면 다음을 수행하세요.

awk 'BEGIN { ORS=RS="\n\n" } length <= 100*1000' file

이렇게 하면 100,000자 이하의 각 레코드가 인쇄됩니다.

레코드가 너무 큰 경우 특정 양의 정수로 시작하는 필드를 삭제하려면:

awk -v number=149 'BEGIN { ORS=RS="\n\n"; OFS=FS="\n" }
    length <= 100*1000 { print; next }
    {
        # This is a too long record.
        # Re-create it without any fields whose first tab-delimited
        # sub-field is the number in the variable number.

        # Split the record into an array of fields, a.
        nf = split($0,a)

        # Empty the record.
        $0 = ""

        # Go through the fields and add back the ones that we
        # want to the output record.
        for (i = 1; i <= nf; ++i) {
            split(a[i],b,"\t")
            if (b[1] != number) $(NF+1) = a[i]
        }

        # Print the output record.
        print
    }' file

이는 이전과 마찬가지로 짧은 레코드를 인쇄합니다. 더 긴 레코드는 탭으로 구분된 첫 번째 하위 필드가 숫자 number(여기서는 명령줄에 149로 지정됨)인 모든 필드에서 제거됩니다.

대규모 레코드의 경우 원하지 않는 필드 없이 레코드가 다시 생성됩니다. 내부 루프는 탭의 필드를 분할하고 탭으로 구분된 첫 번째 하위 필드가 다음이 아닌 필드를 추가하여 출력 레코드를 다시 생성합니다 number.

for (i = 1; i <= nf; ++i) {
    split(a[i],b,"\t")
    if (b[1] != number) $(NF+1) = a[i]
}

awk지정되지 않은 다중 문자 값이 있을 때 발생하는 현상 에 대한 POSIX 사양이 있기 때문에 (대부분의 구현에서는 이를 정규 표현식으로 처리함) 엄격하게 준수하는 구현을 사용할 때보 다는 RS사용할 수 있습니다 . 이렇게 하면 데이터의 여러 빈 줄이 더 이상 빈 레코드를 구분하지 않습니다.RS=""; ORS="\n\n"ORS=RS="\n\n"awk

답변2

또 다른 awk접근법:

awk -v lim=99999 'BEGIN{RS=""; ORS="\n\n"}\
 {while (length()>=lim) {if (!sub(/\n149\t[^\n]*/,"")) break;}} length()<lim' file

149레코드 길이가 변수에 지정된 제한을 초과하는 경우 lim제한이 유지되거나 더 이상 감소가 불가능할 때까지(실제 대체 횟수로 표시됨) "nothing"으로 대체하여 시작하는 줄을 점차적으로 제거합니다. 0)입니다. 그러면 최종 길이가 제한보다 작은 레코드만 인쇄됩니다.

불리:첫 번째 줄부터 시작하는 줄이 제거되므로 해당 149줄이 인접한 텍스트의 개별 요소를 구성하는 경우 해당 텍스트는 다소 이해하기 어려워집니다.

메모:RS=""명시적인 대신 지정 RS="\n\n"하는 것이가지고 다닐 수 있는awk다중 문자의 동작이 RSPOSIX 사양에 의해 정의되지 않았기 때문에 "단락 모드"에서 사용하는 방법입니다 . 그러나 있을 수 있는 경우비어 있는파일에 있는 레코드는 무시되고 awk결과적으로 출력에 표시되지 않습니다. 이것이 원하는 것이 아니라면 명시적 RS="\n\n"표기법을 대신 사용해야 할 수도 있습니다 . 대부분의 awk구현에서는 이를 정규식으로 처리하고 "순진하게" 기대하는 대로 수행합니다.

답변3

\n\n레코드 구분 기호가 있을 때마다 Perl 및 단락 모드(from man perlrun)를 생각해 보십시오.

-0[octal/hexadecimal]
        specifies the input record separator ($/) as an octal or hexadecimal number.  
   [...]
        The special value 00 will cause Perl to slurp files in paragraph mode. 
        

이를 사용하여 다음을 수행할 수 있습니다.

  1. 100,000보다 긴 모든 레코드를 제거합니다.문자(파일 인코딩에 따라 바이트열과 동일하지 않을 수 있습니다):

     perl -00 -ne 'print unless length()>100000' file
    
  2. 처음 100,000자 이후의 모든 문자를 제거하여 100,000자보다 긴 레코드를 잘라냅니다.

     perl -00 -lne 'print substr($_,0,100000)' file
    
  3. 다음 으로 시작하는 줄을 제거하세요 149.

     perl -00 -pe 's/(^|\n)149\s+[^\n]+//g;' file
    
  4. 다음으로 시작하는 줄을 제거하세요. 149단, 이 레코드가 100000자를 초과하는 경우에만 해당됩니다.

     perl -00 -pe 's/(^|\n)149\s+[^\n]+//g if length()>100000; ' file
    
  5. 레코드가 100000자를 초과하는 경우 149레코드가 100000자 미만이 되거나 149가 있는 줄이 더 이상 없을 때까지 다음과 같이 시작하는 줄을 제거합니다.

     perl -00 -pe 'while(length()>100000 && /(^|\n)149\s/){s/(^|\n)149\s+[^\n]+//}' file
    
  6. 레코드가 100000자를 초과하는 경우 149레코드가 100000자 미만이 되거나 149가 있는 줄이 더 이상 없을 때까지 시작하는 줄을 제거합니다.아직100000자를 초과하는 경우 처음 100000만 인쇄합니다.

     perl -00 -lne 'while(length()>100000 && /(^|\n)149\s/){
                         s/(^|\n)149\s+[^\n]+//
                    }
                    print substr($_,0,100000)' file
    
  7. 마지막으로 위와 동일하지만 레코드가 잘리지 않도록 올바른 크기를 얻을 때까지 문자뿐만 아니라 전체 줄을 제거합니다.

     perl -00 -ne 'while(length()>100000 && /(^|\n)149\s/){
                     s/(^|\n)149\s+[^\n]+//
                   }
                   map{
                     $out.="$_\n" if length($out . "\n$_")<=100000
                   }split(/\n/); 
                   print "$out\n"; $out="";' file
    

답변4

아마도 더 우아할 수도 있지만 해결책은 다음과 같습니다.

cat records.txt | awk -v RS='' '{if (length>99999) {gsub(/\n149\t[^\n]*\n/,"\n");print $0"\n"} else {print $0"\n"} }'

나는 고양이의 쓸모없는 사용을 알고 있다고 믿습니다.왼쪽에서 오른쪽으로의 흐름이 더 명확해졌습니다..

여기서 99999는 임계값 크기이고 149는 이 경우 제거할 줄(필드 이름)의 시작입니다.

나는 탐욕스럽지 않은 \n149\t[^\n]*\n/것을 사용하여 ^149\t.*$.

gsub패턴을 지정된 문자열로 바꾸고 대체/교체 횟수를 반환합니다.

에서 영감을 얻었습니다이 답변.

관련 정보