문자열 대체를위한 비선형 도구?


13

최근에 다른 특정 문자 뒤에 나오는 줄 바꿈 문자를 제거하는 방법에 대한 질문을 했습니다.

유닉스 텍스트 프로세싱 툴은 매우 강력하지만 거의 모든 텍스트 라인을 처리하는데, 이는 입력이 사용 가능한 메모리에 맞을 때 가장 좋습니다.

그러나 줄 바꿈이 포함되지 않은 거대한 파일에서 텍스트 시퀀스를 바꾸려면 어떻게해야합니까?

예를 들어 교체 <foobar>\n<foobar>입력 라인 별을 읽지 않고? (한 줄만 있고 길이는 2.5G이므로).


1
또는를 사용 perl하고 python있습니까?
iruvar

펄은 괜찮아 방금 시도 할 것 gsar( home.online.no/~tjaberg )을 찾았습니다 .
MattBianco

답변:


12

이 유형의 문제에 직면했을 때 가장 먼저 발생하는 것은 레코드 구분 기호를 변경하는 것입니다. 대부분의 도구에서 \n기본적으로 설정되어 있지만 변경할 수 있습니다. 예를 들면 다음과 같습니다.

  1. perl -0x3E -pe 's/<foobar>/\n$&/' file
    

    설명

    • -0: 입력 레코드 구분 기호를 16 진수 값이 지정된 문자로 설정합니다 . 이 경우 >16 진수 값 을 로 설정합니다 3E. 일반적인 형식은 -0xHEX_VALUE입니다. 이것은 선을 관리 가능한 덩어리로 나누는 트릭입니다.
    • -pe:에서 제공 한 스크립트를 적용한 후 각 입력 행을 인쇄하십시오 -e.
    • s/<foobar>/\n$&/: 간단한 대체. 는 $&이 경우, 일치 된 어떤이다 <foobar>.
  2. 어 wk

    awk '{gsub(/foobar>/,"\n<foobar>");printf "%s",$0};' RS="<" file
    

    설명

    • RS="<": 입력 레코드 구분 기호를로 설정하십시오 >.
    • gsub(/foobar>/,"\n<foobar>"): 모든 경우 대체 foobar>와를 \n<foobar>. 그 때문에 참고 RS로 설정되어있는 <모든 <(의는 어떻게 입력 파일에서 제거 awk작품) 우리가 일치 할 필요가 있도록foobar> (A없이 <)와 교체 \n<foobar>.
    • printf "%s",$0: 대체 후 현재 "행"을 인쇄합니다. $0에있는 현재 레코드 awk이므로 이전의 모든 것을 보유합니다 <.

다음 명령으로 만든 2.3GB 단일 행 파일에서이를 테스트했습니다.

for i in {1..900000}; do printf "blah blah <foobar>blah blah"; done > file
for i in {1..100}; do cat file >> file1; done
mv file1 file

모두 awkperl메모리의 사용 무시할 수있는 양.


혹시 봤어 Tie::File perldoc.perl.org/Tie/File.html을 . Perl큰 파일을 다룰 때 가장 좋은 기능이라고 생각 합니다.
cuonglm 2016 년

@Gnouc 나는 조금 가지고 놀았습니다. 그러나 i) OP는 이미 다른 질문에 Perl을 싫어한다고 고백했기 때문에 간단하게 유지하고 싶었습니다 .ii) 절대적으로 필요한 경우가 아니라면 외부 모듈을 사용하지 않는 경향이 있습니다 .iii) Tie :: File 모듈을 사용하면 구문이 상당히 줄어 듭니다. 명확한.
terdon

동의하다. 그 Tie::File이후로 핵심 모듈 인 작은 메모입니다 v5.7.3.
cuonglm

9

gsar (일반 검색 및 바꾸기) 는이 목적을 위해 매우 유용한 도구입니다.

이 질문에 대한 대부분의 답변은 레코드 기반 도구와 다양한 트릭을 사용하여 기본 레코드 구분 문자를 입력에서 충분히 자주 발생하는 것으로 가정하여 각 레코드를 너무 커서 처리 할 수없는 것으로 전환하는 등의 문제에 적응시킵니다.

많은 경우에 이것은 매우 훌륭하고 읽기 쉽습니다. 나는 효율적 등 어디든지 사용할 수있는 도구와 해결을 쉽게 할 수있는 문제 / 좋아해요 awk, tr, sed및 Bourne 쉘.

이진 검색을 수행하고 임의의 거대한 파일에서 임의의 내용으로 바꾸는 것은 이러한 표준 유닉스 도구에 적합하지 않습니다.

여러분 중 일부는 이것이 속임수라고 생각할 수도 있지만 작업에 적합한 도구를 사용하는 것이 어떻게 잘못 될 수 있는지 모르겠습니다. 이 경우 GPL v2에gsar 따라 라이센스가 부여 된 C 프로그램 이므로 gentoo , redhat 또는 ubuntu 에이 매우 유용한 도구에 대한 패키지가 없다는 것이 놀랍습니다 .

gsarBoyer-Moore 문자열 검색 알고리즘 의 이진 변형을 사용합니다 .

사용법은 간단합니다.

gsar -F '-s<foobar>' '-r:x0A<foobar>'

여기서 -F"필터"모드, 즉 읽기 stdin쓰기를 의미 stdout합니다. 파일에서도 작동하는 방법이 있습니다. -s검색 문자열을 지정하고-r 교체를 . 콜론 표기법을 사용하여 임의의 바이트 값을 지정할 수 있습니다.

대소 문자를 구분하지 않는 모드는 지원 -i되지만 ( ), 알고리즘은 검색 문자열의 길이를 사용하여 검색을 최적화하므로 정규식은 지원되지 않습니다.

이 도구는 약간의 검색에도 사용할 수 있습니다 grep. gsar -b일치하는 검색 문자열의 바이트 오프셋을 출력하고 gsar -l파일 이름 및 일치하는 수 (있는 경우)와 결합하여 비트 grep -l를 출력 wc합니다.

이 도구는 Tormod Tjaberg (초기)와 Hans Peter Verne (개선)에 의해 작성되었습니다 .


그것이 GPL이라면 배포판을 위해 그것을 포장하는 것을 고려할 것입니다 :)
Rqomey

1
사실 나는 젠투이 빌드를 만드는 것에 대해 진지하게 생각하고 있습니다. 아마도 rpm도 가능합니다. 그러나 전에는 .deb 패키지를 빌드 한 적이 없으므로 누군가 시간이 걸리기 때문에 누군가 나를 이길 수 있기를 바랍니다.
MattBianco

나는 이것이 위안이 될지 의심하지만 OS X의 홈브류에는 공식이 gsar있습니다.
crazysim

5

대상 문자열과 교체 문자열의 길이가 같은 좁은 경우에는 메모리 매핑 이 복구 될 수 있습니다. 교체가 제자리에서 수행되어야하는 경우에 특히 유용합니다. 기본적으로 파일을 프로세스의 가상 메모리에 매핑하고 있으며 64 비트 주소 지정을위한 주소 공간이 큽니다. 파일이 반드시 한 번에 실제 메모리에 모두 맵핑 될 필요는 없으므로 기계에서 사용 가능한 실제 메모리 크기의 몇 배인 파일을 처리 할 수 ​​있습니다.

다음은 파이썬 예제는 대체합니다의 foobarXXXXXX

#! /usr/bin/python
import mmap
import contextlib   
with open('test.file', 'r+') as f:
 with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)) as m:
   pos = 0
   pos = m.find('foobar', pos)
   while pos > 0:
    m[pos: pos+len('XXXXXX')] = 'XXXXXX'
    pos = m.find('foobar', pos)

4

이를위한 많은 도구가 있습니다 :

dd파일을 차단하지 않으려는 경우 사용하려는 것입니다. 특정 횟수만큼만 특정 바이트 수만 읽으십시오. 파일 스트림 차단 및 차단 해제를 이식 가능하게 처리합니다.

tr -dc '[:graph:]' </dev/urandom | dd bs=32 count=1 cbs=8 conv=unblock,sync 2>/dev/null

###OUTPUT###

UI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)N

또한 trASCII 바이트를 다른 바이트로 변환 (또는이 경우 공백이 아닌 인쇄 가능한 문자가 아닌 ASCII 바이트 삭제)을 처리 할 수 있으므로 위 를 사용 합니다. 오늘 아침에 다른 질문 에 대한 답 으로 실제로 사용한 것은 다음 과 같습니다.

tr '>\n' '\n>' | sed 's/^>*//' | tr '\n>' '>\n' 

비슷한 것이 많이 있습니다 . 이 목록은 가장 친숙한 공통 분모 하위 집합을 제공해야합니다.

그러나 2.5GB의 이진 파일에서 텍스트 처리를 수행하려는 경우로 시작할 수 있습니다 od. 그것은 당신에게 octal dump또는 다른 여러 형식을 줄 수 있습니다. 모든 종류의 옵션을 지정할 수 있지만 \C이스케이프 된 형식으로 한 줄에 한 바이트 만 수행 합니다.

아래에서 볼 수 od있듯이 데이터 는 사용자가 지정한 간격마다 규칙적입니다. 그러나 먼저 다음은 귀하의 질문에 대한 답변입니다.

printf 'first\nnewline\ttab spacefoobar\0null' |
od -A n -t c -v -w1 |
sed 's/^ \{1,3\}//;s/\\$/&&/;/ /bd
     /\\[0nt]/!{H;$!d};{:d
    x;s/\n//g}'

위의 작은 부분은 \newline, \0nulls, \tabs 및 구분 기호 <spaces>\C이스케이프 된 문자열을 유지하면서 구분됩니다. 사용 된 Hx기능에 유의하십시오 sed. 구분 기호가 나타날 때마다 메모리 버퍼의 내용을 교체합니다. 이러한 방식으로 sed파일을 안정적으로 분리하고 오버런을 버퍼링하지 않기 위해 필요한만큼의 정보 만 보유합니다. 즉, 실제로 분리 문자를 만나는 한 그렇지 않습니다. 그렇게하는 동안 sed입력을 계속 처리 od하고을 만나기 전까지 계속 입력 합니다 EOF.

그대로 출력은 다음과 같습니다.

first
\nnewline
\ttab
 spacefoobar
\0null

그래서 내가 원한다면 foobar:

printf ... | od ... | sed ... | 
sed 's/foobar/\
&\
/g'

###OUTPUT###

first
\nnewline
\ttab
 space
foobar

\0null

이제 당신의 메이크업에 사용하려는 경우 C꽤 쉽게 탈출 - 때문에 sed이미 두 번이 \\때문에 자사의 단일 입력 백 슬래시의 모든 탈출 백 슬래시, printf에서 execed xargs귀하의 사양에 출력을 생성 아무 문제가 없게됩니다. 그러나 xargs 쉘 따옴표를 먹으 므로 다시 큰 따옴표를 사용해야합니다.

printf 'nl\ntab\tspace foobarfoobar\0null' |
PIPELINE |
sed 's/./\\&/g' | 
xargs printf %b | 
cat -A

###OUTPUT###

nl$
tab^Ispace $
foobar$
$
foobar$
^@null%

쉘 변수에 쉽게 저장하고 나중에 동일한 방식으로 출력 할 수 있습니다. 마지막 sed삽입\ 은 입력의 모든 문자 앞에 백 슬래시를 합니다. 그게 전부입니다.

그리고 여기 sed에 그것을 잡기 전에 모든 것이 보이는 것처럼 보입니다 .

printf 'nl\ntab\tspace foobarfoobar\0null' |
od -A n -t c -v -w1

   n
   l
  \n
   t
   a
   b
  \t
   s
   p
   a
   c
   e

   f
   o
   o
   b
   a
   r
   f
   o
   o
   b
   a
   r
  \0
   n
   u
   l
   l

2

Awk는 연속 레코드에서 작동합니다. 모든 문자를 레코드 구분 기호로 사용할 수 있습니다 (많은 구현에서 널 바이트 제외). 일부 구현에서는 레코드 구분 기호로 임의의 정규 표현식 (빈 문자열과 일치하지 않음)을 지원하지만 레코드 구분 기호를 각 레코드의 끝에서 잘 리기 전에 잘리지 않기 때문에 다루기 어려울 수 있습니다 $0(GNU awk는 변수 RT를 레코드 구분 기호로 설정합니다) 현재 레코드의 끝에서 제거되었습니다). 기본적으로 줄 바꾸기 인 print출력 레코드 구분 기호 ORS를 사용하여 출력 을 종료하고 입력 레코드 구분 기호와 독립적으로 설정 RS됩니다.

awk -v RS=, 'NR==1 {printf "input up to the first comma: %s\n", $0}'

당신은 효과적으로 (다른 도구에 대한 레코드 구분 기호로 다른 문자를 선택할 수 있습니다 sort, sed와 그 문자와 줄 바꿈을 교환함으로써, ...)을 tr.

tr '\n,' ',\n' |
sed 's/foo/bar/' |
sort |
tr '\n,' ',\n'

많은 GNU 텍스트 유틸리티는 줄 바꿈 대신 널 바이트 사용을 분리 자로 지원합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.