根据模式将一个文件拆分为多个文件(剪切可以在行内进行)

Los*_*ion 4 python perl awk split gnu

存在很多解决方案,但这里的特殊性是我需要能够在一条线内分割,切割应该在模式之前发生.例如:

INFILE:

<?xml 1><blabla1>
<blabla><blabla2><blabla>
<blabla><blabla>
<blabla><blabla3><blabla><blabla>
<blabla><blabla><blabla><?xml 4>
<blabla>
<blabla><blabla><blabla>
<blabla><?xml 2><blabla><blabla>
Run Code Online (Sandbox Code Playgroud)

应该成为模式 <?xml

Outfile1:

<?xml 1><blabla1>
<blabla><blabla2><blabla>
<blabla><blabla>
<blabla><blabla3><blabla><blabla>
<blabla><blabla><blabla>
Run Code Online (Sandbox Code Playgroud)

Outfile2:

<?xml 4>
<blabla>
<blabla><blabla><blabla>
<blabla>
Run Code Online (Sandbox Code Playgroud)

Outfile3:

<?xml 2><blabla><blabla>
Run Code Online (Sandbox Code Playgroud)

实际上,perl在验证答案脚本在这里工作正常,我的小例子.但它会为我更大(约6GB)的实际文件生成错误.错误是:

panic: sv_setpvn called with negative strlen at /home/.../split.pl line 7, <> chunk 1.
Run Code Online (Sandbox Code Playgroud)

我没有评论的权限,这就是我开始新帖子的原因.最后,Python我更了解解决方案,因为我更了解它.

geo*_*org 10

这将执行拆分而不将所有内容读入RAM:

def files():
    n = 0
    while True:
        n += 1
        yield open('/output/dir/%d.part' % n, 'w')


pat = '<?xml'
fs = files()
outfile = next(fs) 

with open(filename) as infile:
    for line in infile:
        if pat not in line:
            outfile.write(line)
        else:
            items = line.split(pat)
            outfile.write(items[0])
            for item in items[1:]:
                outfile = next(fs)
                outfile.write(pat + item)
Run Code Online (Sandbox Code Playgroud)

警告:如果您的模式跨越多行(即包含"\n"),则不起作用.如果是这种情况,请考虑mmap解决方案.


Cof*_*ter 6

Perl可以逐行解析大文件,而不是将整个文件压入内存.这是一个简短的脚本(附带说明):

perl -n -E 'if (/(.*)(<\?xml.*)/ ) {
   print $fh $1 if $1;
   open $fh, ">output." . ++$i;
   print $fh $2;
} else { print $fh $_ }'  in.txt
Run Code Online (Sandbox Code Playgroud)

perl -n:-n标志将逐行循环遍历文件(将内容设置为$ _)

-E :执行以下文本(Perl默认需要文件名)

if (/(.*)(<\?xml.*) )如果一行匹配<?xml将该行(使用正则表达式匹配)拆分为$ 1和$ 2.

print $fh $1 if $1 将行的开头打印到旧文件.

open $fh, ">output.". ++$i; 创建一个新的文件句柄进行写作.

print $fh $2 将剩余的行打印到新文件.

} else { print $fn $_ }如果该行不匹配<?xml,则将其打印到当前文件句柄.

注意:此脚本假定您的输入文件以<?xml.


Joh*_*ard 5

对于那个大小的文件,您可能希望使用该mmap模块,因此您不必自己处理文件分块.从那里的文档:

内存映射文件对象的行为类似于字符串和文件对象.然而,与普通的字符串对象不同,这些是可变的.您可以在大多数需要字符串的地方使用mmap对象; 例如,您可以使用re模块搜索内存映射文件.由于它们是可变的,您可以通过执行更改单个字符obj[index] = 'a',或通过分配切片来更改子字符串: obj[i1:i2] = '...'.您还可以从当前文件位置开始读取和写入数据,并seek()通过文件读取和写入不同的位置.

这是一个快速示例,向您展示如何查找<?xml #>文件中的每个匹配项.您可以随时将块写入新文件,但我没有写过这部分内容.

import mmap
import re

# a regex to match the "xml" nodes
r = re.compile(r'\<\?xml\s\d+\>')

with open('so.txt','r+b') as f:
    mp = mmap.mmap(f.fileno(),0)
    for m in r.finditer(mp):
        # here you can start collecting the starting positions and 
        # writing chunks to new files 
        print m.start()
Run Code Online (Sandbox Code Playgroud)