텍스트 파일을 여러 텍스트 파일로 분할하려면 어떻게해야합니까?


16

entry.txt다음을 포함 하는 텍스트 파일 이 있습니다.

[ entry1 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3633 3634 3636 3690 3691 3693 3766
3767 3769 4526 4527 4529 4583 4584 4586 4773 4774
4776 5153 5154 5156 5628 5629 5631
[ entry2 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3690 3691 3693 3766 3767 3769 4526
4527 4529 4583 4584 4586 4773 4774 4776 5153 5154
5156 5628 5629 5631
[ entry3 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3690 3691 3693 3766 3767 3769 4241
4242 4244 4526 4527 4529 4583 4584 4586 4773 4774
4776 5153 5154 5156 5495 5496 5498 5628 5629 5631

entry1.txt,, entry2.txt라는 세 개의 텍스트 파일로 나누고 싶습니다 entry3.txt. 내용은 다음과 같습니다.

entry1.txt :

[ entry1 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3633 3634 3636 3690 3691 3693 3766
3767 3769 4526 4527 4529 4583 4584 4586 4773 4774
4776 5153 5154 5156 5628 5629 5631

entry2.txt :

[ entry2 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3690 3691 3693 3766 3767 3769 4526
4527 4529 4583 4584 4586 4773 4774 4776 5153 5154
5156 5628 5629 5631

entry3.txt :

[ entry3 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3690 3691 3693 3766 3767 3769 4241
4242 4244 4526 4527 4529 4583 4584 4586 4773 4774
4776 5153 5154 5156 5495 5496 5498 5628 5629 5631

즉, [문자는 새 파일이 시작되어야 함을 나타냅니다. 항목 ( [ entry*], 여기서 *정수)은 항상 숫자 순서이며 1부터 N까지 시작하는 연속 정수입니다 (실제 입력 파일에서 N = 200001).

bash에서 자동 텍스트 파일 분할을 수행 할 수있는 방법이 있습니까? 내 실제 입력 entry.txt에는 실제로 200,001 개의 항목이 있습니다.

답변:


11

그리고 여기에 간단하고 멋진 멋진 한 줄짜리가 있습니다.

$ gawk '/^\[/{match($0, /^\[ (.+?) \]/, k)} {print >k[1]".txt" }' entry.txt

이것은 각 항목 헤더가처럼 보이는 한, 각 항목의 줄 수에 관계없이 모든 파일 크기에서 작동합니다 [ blahblah blah blah ]. 오프닝 직후 [와 클로징 직전 의 공간에 주목하십시오 ].


설명:

awkgawk라인으로 입력 파일 라인을 읽어 보시기 바랍니다. 각 줄을 읽을 때 내용은 $0변수에 저장됩니다 . 여기서는 gawk대괄호 안에있는 모든 것을 일치시키고 해당 일치를 배열에 저장합니다 k.

따라서 정규식이 일치 할 때마다, 즉 파일의 모든 헤더에 대해 k [1]은 일치하는 행 영역을 갖습니다. 즉, "entry1", "entry2"또는 "entry3"또는 "entryN"입니다.

마지막으로 각 행을 <whatever value k currently has>.txtentry1.txt, entry2.txt ... entryN.txt 라는 파일에 인쇄합니다 .

이 방법은 큰 파일의 경우 perl보다 훨씬 빠릅니다.


+1 좋아요 match항목을 입력 할 필요가 없습니다 /^\[/ { name=$2 }. 충분해야합니다.
Thor

감사합니다 @. 설명 된 경우에 대한 제안은 정확하지만 항목 이름에 공백이없는 것으로 가정합니다. 그렇기 때문에 내가 [ blahblah blah blah ]대답에 예제 를 사용했습니다 .
terdon

아 나는 공백으로 구분 된 항목에 대해 조금보고 싶었습니다. 당신은 또한에 그 수용 할 수있는 FS예를 -F '\\[ | \\]'.
Thor

@terdon 나는이 짧은 솔루션을 정말로 좋아하지만 불행히도 나는 일반적으로 내 요구에 맞게 일반화하지 못합니다. 손 좀 주 시겠어요? 내 파일에는 다음으로 시작하는 줄이 있습니다.#S x . 여기서 x는 1, 2 또는 3 자리 숫자입니다. x.dat에 저장하면 충분합니다. 나는 그것을 시도 gawk '/^#S/{match($0, / [0-9]* /, k)} {print >k[1]".dat" }' myFile.txt했다.
mikuszefski

가있어 gawk '/^#S/{match($0, /^#S (\s+?)([0-9]+)(\s+?)/, k)} {print >k[2]".txt" }' test.txt트릭을했다. 2그래도 배열 번호를 잘 모르십시오.
mikuszefski

17

GNU coreutils (포함되지 않은 Linux, Cygwin)의 csplit 사용 :

csplit -f entry -b '%d.txt' entry.txt '/^\[ .* \]$/' '{*}'

빈 파일 entry0.txt(첫 번째 헤더 앞 부분 포함)이 추가됩니다.

표준 csplit 에는 {*}무한 리피터와 -b접미사 형식을 지정 하는 옵션이 없으므로 다른 시스템에서는 먼저 섹션 수를 계산하고 나중에 출력 파일의 이름을 바꿔야합니다.

csplit -f entry -n 9 entry.txt '/^\[ .* \]$/' "{$(egrep -c '^'\[ .* \]$' <entry.txt)}"
for x in entry?????????; do
  y=$((1$x - 1000000000))
  mv "entry$x" "entry$y.txt"
done

csplit은 때때로 조금 기발한 것이지만, 이런 종류의 일을하고 싶을 때 매우 유용합니다.
ixtmixilix

10

펄에서는 훨씬 간단하게 할 수 있습니다 :

perl -ne 'open(F, ">", ($1).".txt") if /\[ (entry\d+) \]/; print F;' file

9

다음은 짧은 awk one-liner입니다.

awk '/^\[/ {ofn=$2 ".txt"} ofn {print > ofn}' input.txt

어떻게 작동합니까?

  • /^\[/ 왼쪽 대괄호로 시작하는 줄과 일치하고
  • {ofn=$2 ".txt"}출력 파일 이름으로 두 번째 공백으로 구분 된 단어에 변수를 설정합니다. 그때,
  • ofn 변수가 설정되면 true로 평가되는 조건입니다 (따라서 첫 번째 헤더 앞의 행은 무시됩니다)
  • {print > ofn} 현재 줄을 지정된 파일로 리디렉션합니다.

참고 모든 컴팩트 당신을 행복하게 만드는 경우이 awk 스크립트의 공간이 제거 될 수있다.

또한 위의 스크립트에는 섹션 헤더가 공백이 아닌 섹션 헤더가 필요합니다. [foo]and와 같은 섹션 헤더를 처리 [ this that ]하려면 약간 더 많은 코드가 필요합니다.

awk '/^\[/ {sub(/^\[ */,""); sub(/ *\] *$/,""); ofn=$0 ".txt"} ofn {print > ofn}' input.txt

이것은 awk의 sub()기능을 사용 하여 선행 및 후행 대괄호 플러스 공백을 제거합니다. 표준 awk 동작에 따라 공백 (필드 구분 기호)이 단일 공백으로 축소됩니다 (즉, [ this that ]에 저장 됨 "this that.txt"). 출력 파일 이름에 원래 공백을 유지해야하는 경우 FS를 설정하여 실험 할 수 있습니다.


2

파이썬의 명령 줄에서 다음과 같이 수행 할 수 있습니다.

paddy$ python3 -c 'out=0
> with open("entry.txt") as f: 
>   for line in f:
>     if line[0] == "[":
>       if out: out.close()
>       out = open(line.split()[1] + ".txt", "w")
>     else: out.write(line)'

2

이것은 다소 조잡하지만 이해하기 쉬운 방법 grep -l '[ entry ]' FILENAME입니다. [entry]에서 줄 번호를 나누기 위해 사용 하십시오. 머리와 꼬리를 조합하여 올바른 조각을 얻으십시오.

내가 말했듯이; 예쁘지는 않지만 이해하기 쉽습니다.


2

[레코드 구분 기호로 awk를 사용 하고 필드 구분 기호로 공백 을 사용하는 것은 어떻습니까? 이렇게하면 $0제거 된 행간 [과 파일 이름을로 되돌려 야하는 위치에 파일에 넣을 데이터를 쉽게 얻을 수 있습니다 $1. 그런 다음 비어있는 첫 번째 레코드의 특수한 경우 만 처리하면됩니다. 이것은 우리에게 :

awk -v "RS=[" -F " " 'NF != 0 {print "[" $0 > $1}' entry.txt

2

terdon의 답변이 저에게 효과적이지만 awk가 아닌 gawk를 사용해야했습니다. 둔한 설명서 ( '(경기') 검색이 경기에서 배열 인수 ()가 둔한 확장이라고 설명한다. 아마 설치 리눅스에 의존하고 AWK / nawk / 둔한 버전하지만 내 우분투 머신에만 둔한의 만났 terdon의 우수에 대답:

$ gawk '{if(match($0, /^\[ (.+?) \]/, k)){name=k[1]}} {print >name".txt" }' entry.txt

1

여기 펄 솔루션이 있습니다. 이 스크립트는 [ entryN ]행을 감지하고 그에 따라 출력 파일을 변경하지만 각 섹션의 데이터를 확인, 구문 분석 또는 처리하지 않고 입력 행을 출력 파일로 인쇄합니다.

#! /usr/bin/perl 

# default output file is /dev/null - i.e. dump any input before
# the first [ entryN ] line.

$outfile='/dev/null';
open(OUTFILE,">",$outfile) || die "couldn't open $outfile: $!";

while(<>) {
  # uncomment next two lines to optionally remove comments (starting with
  # '#') and skip blank lines.  Also removes leading and trailing
  # whitespace from each line.
  # s/#.*|^\s*|\s*$//g;
  # next if (/^$/)

  # if line begins with '[', extract the filename
  if (m/^\[/) {
    (undef,$outfile,undef) = split ;
    close(OUTFILE);
    open(OUTFILE,">","$outfile.txt") || die "couldn't open $outfile.txt: $!";
  } else {
    print OUTFILE;
  }
}
close(OUTFILE);

1

안녕하세요, 루비를 사용 하여이 간단한 스크립트를 작성하여 문제를 해결했습니다.

#!ruby
# File Name: split.rb

fout = nil

while STDIN.gets
  line = $_
  if line.start_with? '['
    fout.close if fout
    fname = line.split(' ')[1] + '.txt'
    fout = File.new fname,'w'
  end
  fout.write line if fout
end

fout.close if fout

이 방법으로 사용할 수 있습니다 :

ruby split.rb < entry.txt

나는 그것을 테스트했으며 잘 작동합니다 ..


1

csplit옵션을 선호 하지만 대안으로 GNU awk 솔루션이 있습니다.

구문 분석

BEGIN { 
  RS="\\[ entry[0-9]+ \\]\n"  # Record separator
  ORS=""                      # Reduce whitespace on output
}
NR == 1 { f=RT }              # Entries are of-by-one relative to matched RS
NR  > 1 {
  split(f, a, " ")            # Assuming entries do not have spaces 
  print f  > a[2] ".txt"      # a[2] now holds the bare entry name
  print   >> a[2] ".txt"
  f = RT                      # Remember next entry name
}

다음과 같이 실행하십시오.

gawk -f parse.awk entry.txt

1
FWIW에서 RT변수는 각기 다른 것으로 보입니다. 이 솔루션은 FreeBSD의 awk를 사용하면 작동하지 않습니다.
ghoti

@ghoti : 그렇습니다. 나는 지금 그 대답에 그것을 포함시켰다. 감사.
Thor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.