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

时间:2012-10-03 21:38:50

标签: 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>

应该使用模式<?xml

Outfile1:

<?xml 1><blabla1>
<blabla><blabla2><blabla>
<blabla><blabla>
<blabla><blabla3><blabla><blabla>
<blabla><blabla><blabla>

Outfile2:

<?xml 4>
<blabla>
<blabla><blabla><blabla>
<blabla>

Outfile3:

<?xml 2><blabla><blabla>

实际上,经验证的答案here中的perl脚本适用于我的小例子。但它会为我更大(约6GB)的实际文件生成错误。错误是:

panic: sv_setpvn called with negative strlen at /home/.../split.pl line 7, <> chunk 1.

我没有评论的权限,这就是我开始发布新帖子的原因。 最后,我更了解Python解决方案,因为我理解得更好。

4 个答案:

答案 0 :(得分:9)

执行拆分而不将所有内容读入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)

警告:如果您的模式跨越多行(即包含“\ n”),则不起作用。如果是这种情况,请考虑mmap solution

答案 1 :(得分:6)

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

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

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开头。

答案 2 :(得分: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()

答案 3 :(得分:0)

只需对搜索字词进行拆分

for i,part in enumerate(my_xml_Text_string.split("<?xml")):
    if not part.strip():continue # make sure its not empty
    with open("file%d.xml"%i,"w") as f: #open a file to write to
         f.write("<?xml"+part) #write the content putting your search term back in