使用模式作为分隔符拆分文件



我有一个5000行文件,由行块组成,块之间有一个END字符串,如下所示

ATOM 1
ATOM 3
ATOM 25
END 
ATOM 2
ATOM 36
ATOM 22
ATOM 12 
END 
ATOM 1
ATOM 87
END 

我想找到一种方法将文件分成几个文件,每个文件在END字符串之前包含一个单独的行块。第一个文件应该如下所示:

ATOM 1
ATOM 3
ATOM 25
第二个文件应该包含
ATOM 2
ATOM 36
ATOM 22
ATOM 12 

以此类推。我想过使用awk '/END/{flag=1; next} /END/{flag=0} flag' file之类的东西来获取END字符串之间的块。然而,这对我的第一个块不起作用,因为END字符串只在块之后,最重要的是,它不能考虑到它找到字符串END的次数,以将每个块分离到其单独的文件中。是否有一种方法,我可以使用字符串END拆分我的文件成几个,每个包含一个块,以字符串END结束?

关闭。增加每个块的标志。并输出到一个文件。在awk:

awk 'BEGIN{flag=0} /END/{flag++} {print $0 > flag ".txt"}' file

在Bash:

flag=0
while IFS= read -r line; do
if [[ "$line" = "END" ]]; then
flag=$((flag + 1))
else
printf "%sn" "$line" >> "$flag.txt"
fi
done <inputfile

等在任何其他编程语言

awkrecord分隔符(RS)可以重置为读取以"&;end &;"分隔的块,并且每个块可以打印到文件名以数字递增的文件中,如下所示:

awk 'BEGIN{RS="END";ORS="";i=1;} {print > "part"i".file"; i++}' file.txt

输出记录分隔符ORS已设置为空字符串,以防止在文件末尾出现额外的新行。第一部分之后的文件仍然有一个前导空行,如果必要的话可以删除。它还创建了一个额外的空文件,可以忽略这个"快速而肮脏"的解决方案。

递增计数器i用于形成顺序文件名。

从上述过程检查的输出与输入的文件副本一起运行:

> ls part*
part1.file  part2.file  part3.file  part4.file
> cat part1.file
ATOM 1
ATOM 3
ATOM 25
>cat part2.file

ATOM 2
ATOM 36
ATOM 22
ATOM 12 

(部件。文件为空)

可能的问题:awk的某些版本显然不喜欢直接打印重定向的文件名连接。如果这里出现错误,文件名可以以稍微长一点的版本进行:

awk 'BEGIN{RS="END";ORS="";i=1;} {flname="part"i".file"; print > flname; i++}' file.txt

使用任意awk:

$ awk -v cnt=1 '
/END/ { cnt++; next }
cnt != prev { close(out); out="foo" cnt ".txt"; prev=cnt }
{ print > out }
' file

$ head foo*.txt
==> foo1.txt <==
ATOM 1
ATOM 3
ATOM 25
==> foo2.txt <==
ATOM 2
ATOM 36
ATOM 22
ATOM 12
==> foo3.txt <==
ATOM 1
ATOM 87

$ awk '/END/{c++; next} {print > ("file."(c+1)".txt")}' file

==> file.1.txt <==
ATOM 1
ATOM 3
ATOM 25
==> file.2.txt <==
ATOM 2
ATOM 36
ATOM 22
ATOM 12
==> file.3.txt <==
ATOM 1
ATOM 87

如果你有太多的节最终可能会遇到太多的文件打开的问题。因此,最好在完成后关闭文件。

$ awk 'BEGIN {f="file."(++c)".txt"} 
/END/ {close(f); f="file"(++c)".txt"; next} 
{print > f}' file

这可能适合您(GNU csplit):

csplit -qz -f file -b '%04d.txt' --suppress-matched file '/END/' '{*}'

保持安静,删除任何空文件。

file作为输出文件的前缀,以四位数字加.txt作为后缀。

禁止匹配行,例如END.

重复,直到文件结束。


如果您不介意文件默认为xxn,请使用:

csplit -qz --sup file '/END/' '{*}'

其他几种方法

Perl:

perl -0777 -lnE 'while (/([sS]*?)^ENDs*/gm) {
$cnt++;
open(FH, ">file_${cnt}.txt");
print FH $1;
close (FH);
}' file 

Ruby:

ruby -e 'cnt=1; s=$<.read.scan(/([sS]*?)^ENDs*/m) { |b|
File.write("file_#{cnt}.txt", b.join(""))
cnt+=1
}' file 

任何awk:

awk 'BEGIN { i=1; fn=sprintf("file_%s.txt", i) }
$1=="END" { close(fn); fn=sprintf("file_%s.txt", ++i); next }
{print > fn }
' file 

或者,您可以使用sed并使用Bash处理替换(注意——这仅在文件以最后的新行正确结束时才有效)

while IFS= read -r -d $'3' block; do
(( i++ ))
printf "%s" "$block" > "file_${i}.txt"
done < <(sed '/^END[[:space:]]*$/N; s/^END[[:space:]]*/x3/' file)

以上任何一种情况都会导致:

head file_*.txt
==> file_1.txt <==
ATOM 1
ATOM 3
ATOM 25
==> file_2.txt <==
ATOM 2
ATOM 36
ATOM 22
ATOM 12 
==> file_3.txt <==
ATOM 1
ATOM 87
# ^ Note final file has proper n termination

相关内容

  • 没有找到相关文章

最新更新