2011-02-10 23 views
11

Xin chào các bạn Tôi hy vọng chủ đề này đủ rõ ràng, tôi chưa tìm thấy bất cứ điều gì cụ thể về điều này trong thùng được yêu cầu trước đó. Tôi đã thử thực hiện điều này trong Perl hoặc Python, nhưng tôi nghĩ rằng tôi có thể đang cố gắng quá khó.Tách tệp txt lớn thành 200 tệp txt nhỏ hơn trên regex bằng cách sử dụng tập lệnh shell trong BASH

Có một lệnh/đường dẫn shell đơn giản sẽ tách tệp .txt 4MB của tôi thành các tệp .txt riêng biệt, dựa trên regex bắt đầu và kết thúc không?

Tôi cung cấp một mẫu ngắn của tệp bên dưới .. để bạn có thể thấy rằng mọi "câu chuyện" đều bắt đầu bằng cụm từ "X của XXX DOCUMENTS", có thể được sử dụng để chia nhỏ tệp.

Tôi nghĩ rằng điều này phải dễ dàng và tôi sẽ ngạc nhiên nếu bash không thể làm điều đó - nhanh hơn Perl/Py.

Ở đây là:

      1 of 999 DOCUMENTS 


       Copyright 2011 Virginian-Pilot Companies LLC 
          All Rights Reserved 
        The Virginian-Pilot(Norfolk, VA.) 

... 



          3 of 999 DOCUMENTS 


        Copyright 2011 Canwest News Service 
          All Rights Reserved 
          Canwest News Service 

... 

Cảm ơn trước cho tất cả sự giúp đỡ của bạn.

Ross

+1

là nhiều văn bản mẫu cần thiết? – jakev

+1

Vui lòng chỉnh sửa và xóa khoảng 95% văn bản trong câu hỏi của bạn. –

+0

có thể trùng lặp của [Chia một tệp thành nhiều tệp dựa trên dấu phân tách] (http://stackoverflow.com/questions/11313852/split-one-file-into-multiple-files-based-on-delimiter) – tripleee

Trả lời

22
awk '/[0-9]+ of [0-9]+ DOCUMENTS/{g++} { print $0 > g".txt"}' file 

người dùng OSX sẽ cần gawk, như BUILTIN awk sẽ tạo ra một lỗi như awk: illegal statement at source line 1

của Ruby (1.9+)

#!/usr/bin/env ruby 
g=1 
f=File.open(g.to_s + ".txt","w") 
open("file").each do |line| 
    if line[/\d+ of \d+ DOCUMENTS/] 
    f.close 
    g+=1 
    f=File.open(g.to_s + ".txt","w") 
    end 
    f.print line 
end 
+0

OH và chúng tôi có một người chiến thắng .... tốc độ * VÀ * sang trọng tôi đã dành một mùa hè thực sự ướt vào năm 1997 với cuốn sách sed/awk O'Reilly. Ước gì tôi có thể nhớ lại tất cả những gì bây giờ. Tôi * sẽ * đi và lấy nó tmrw. ** CẢM ƠN BẠN ** – rosser

+1

Giải pháp này đặt dòng khớp trong tệp mới, trả lời câu hỏi. Nhưng nếu, giống như tôi, bạn muốn đặt dòng khớp trong tệp cũ trước khi bắt đầu tệp mới, bạn sẽ làm điều này: 'awk '{print $ 0> n" .txt "}/text để khớp/{n ++} ' – indiv

+1

Lưu ý: trên Mac OS X, bạn cần' gawk' từ ví dụ MacPorts cho việc này để làm việc –

0

regex để phù hợp với "X VĂN XXX" là
\ d {1,3} của \ d {1,3) VĂN

dòng đọc theo dòng và bắt đầu viết mới tập tin khi trận đấu regex sẽ ổn.

-1

chưa được kiểm tra:

base=outputfile 
start=1 
pattern='^[[:blank:]]*[[:digit:]]+ OF [[:digit:]]+ DOCUMENTS[[:blank:]]*$ 

while read -r line 
do 
    if [[ $line =~ $pattern ]] 
    then 
     ((start++)) 
     printf -v filecount '%4d' $start 
     >"$base$filecount" # create an empty file named like foo0001 
    fi 
    echo "$line" >> "$base$filecount" 
done 
+0

Nhân tiện , ở trên là tinh khiết Bash. Ngoài ra, tôi chắc chắn rằng Python hoặc Perl sẽ nhanh hơn nhiều. –

+1

Bạn có thể làm điều đó với csplit không? csplit -k -z --digits = 3 --suffix = '% d.TXT' --prefix = TẬP_TIN *.TXT/'SPLITONTHIS' – rosser

+0

@rosser - đây là một ứng cử viên để chia nhỏ, không biết csplit mặc dù – sln

1

Làm thế nào cứng bạn đã thử trong Perl?

Chỉnh sửa Đây là phương pháp nhanh hơn. Nó chia nhỏ tập tin rồi in các tập tin.

use strict; 
use warnings; 

my $count = 1; 

open (my $file, '<', 'source.txt') or die "Can't open source.txt: $!"; 

for (split /(?=^.*\d+[^\S\n]*of[^\S\n]*\d+[^\S\n]*DOCUMENTS)/m, join('',<$file>)) 
{ 
    if (s/^.*(\d+)\s*of\s*\d+\s*DOCUMENTS.*(\n|$)//m) 
    { 
     open (my $part, '>', "Part$1_$count.txt") 
      or die "Can't open Part$1_$count for output: $!"; 
     print $part $_; 
     close ($part); 
     $count++; 
    } 
} 
close ($file); 

Đây là dòng bằng phương pháp dòng:

use strict; 
use warnings; 

open (my $masterfile, '<', 'yourfilename.txt') or die "Can't open yourfilename.txt: $!"; 

my $count = 1; 
my $fh; 

while (<$masterfile>) { 
    if (/(?<!\d)(\d+)\s*of\s*\d+\s*DOCUMENTS/) { 
     defined $fh and close ($fh); 
     open ($fh, '>', "Part$1_$count.txt") or die "Can't open Part$1_$count for output: $!"; 
     $count++; 
     next; 
    } 
    defined $fh and print $fh $_; 
} 
defined $fh and close ($fh); 
close ($masterfile); 
+0

'$ count' là không xác định. Tôi nghi ngờ bạn có nghĩa là '$ cnt'. Ngoài ra, lần đầu tiên bạn chạy qua vòng lặp '$ fh' là không xác định, vì vậy bạn sẽ nhận được giá trị' Không thể sử dụng giá trị không xác định là tham chiếu/lỗi tham chiếu biểu tượng khi bạn cố gắng đóng '$ fh'. – CanSpice

+0

@CanSpice, bây giờ bạn có thích không? – sln

+1

Có vẻ tốt hơn bây giờ! – CanSpice

9

Như đã đề cập trong các giải pháp khác, bạn có thể sử dụng csplit cho rằng:

csplit csplit.test '/^\.\.\./' '{*}' && sed -i '/^\.\.\./d' xx* 

tôi đã không tìm thấy một cách tốt hơn để thoát khỏi các dấu phân cách gợi nhớ trong các tập tin chia.

+0

Tôi không thể thử ngay bây giờ vì trên cửa sổ, nhưng trang người dùng csplit dường như đề xuất sử dụng% REGEX% thay vì/REGEX/cho rằng: /REGEXP/[OFFSET] sao chép lên nhưng không bao gồm một dòng phù hợp % REGEXP% [OFFSET] bỏ qua, nhưng không bao gồm một dòng phù hợp – Spikolynn

Các vấn đề liên quan