我有一个巨大的文件(6Gb),其格式为74.000篇文章:
<text id="1">
bla bla bla bla.........
</text>
<text id="2">
bla bla bla bla.........
</text>
<text id="3">
bla bla bla bla.........
</text>
<text id="............ and so on untill 74.000
然后我有另一个文件,其标题对应于每个id,如下所示:
1 title1
2 title2
3 title3
...
74000 title74000
我必须在第一个文件中为每个id添加相应的标题,所以我将第二个文件转换为这个脚本:
sed -i "s/<text id="1">/<text id="1" title="title1">/" file1
sed -i "s/<text id="2">/<text id="2" title="title2">/" file1
sed -i "s/<text id="3">/<text id="3" title="title3">/" file1
...
sed -i "s/<text id="74000">/<text id="74000" title="title74000">/" file1
注意我没有将g放在sed命令的末尾,因为它不是全局serch,这意味着在第一次匹配时它会更改字符串并转到下一个搜索。该脚本可以工作,但是由于文件的大小每次更改需要12分钟,这让我有两年的时间来完成所有更改,而我需要它们尽快,所以我的问题是如果有人知道如何执行此更改以更快的方式,也许与其他一些实用程序,python,perls或任何其他...
答案 0 :(得分:4)
在Gnu Awk第4版中,您可以尝试:
gawk4 -f a.awk file2 RS="^$" file1
其中a.awk
是:
NR==FNR {
b["<text id=\""$1"\">"]=$2
next
}
{
n=split($0,a,/<text id=[^>]*>/,s)
printf "%s%s",s[0],a[1]
for (i=1; i<n; i++) {
ind=index(s[i],">")
printf "%s%s", substr(s[i],1,ind-1) " title=\""b[s[i]]"\">", a[i+1]
}
printf "%s",s[n]
}
输出:
<text id="1" title="title1">
bla bla bla bla.........
</text>
<text id="2" title="title2">
bla bla bla bla.........
</text>
<text id="3" title="title3">
bla bla bla bla.........
</text>
<强>更新强>
为了好玩,我在3.9Mb xml文件(80000个标题)和1.3Mb信息文件(也是80000个标题)上测试了一些解决方案
(生成输入文件的脚本可以在这里找到:http://pastebin.com/PpTPt0gk)
更新2
为了获得更可靠的计时结果,我平均花费了20多次:
答案 1 :(得分:3)
我建议你使用这样的东西。
每次遇到XML文件中的<text>
标记时,它都会从titles文件中读取一行,并将title
属性插入到标记中。
它还检查两个文件中的ID是否匹配,并每500 <text>
个元素打印一个日志输出,以便您可以看到它的进度。
输出发送到单独的文件。您不应该覆盖输入文件,就好像出现问题一样,您丢失了原始数据。
这应该只比复制XML文件慢一点。
use strict;
use warnings;
use IO::Handle;
STDOUT->autoflush;
open my $in_xml, '<', 'input.xml' or die "Failed to open XML file: $!";
open my $in_titles, '<', 'titles.txt' or die "Failed to open titles file: $!";
open my $out_xml, '>', 'output.xml' or die "Failed to open output file: $!";
while (my $xml_line = <$in_xml>) {
if ( $xml_line =~ /<text/ ) {
my ($id1) = $xml_line =~ /id="(\d+)"/;
unless (defined $id1) {
chomp;
die sprintf qq{Error in input XML file at line %d: %s\n-}, $in_xml->input_line_number, $_;
}
printf "Processing ID %d\n", $id1 unless $id1 % 500;
my $title_line = <$in_titles>;
my ($id2, $title) = $title_line =~ /^(\d+)\s+(.+)/;
unless (defined $id2) {
chomp $title_line;
die sprintf qq{Error in input titles file at line %d: %s\n-}, $in_titles->input_line_number, $title_line;
}
unless ($id1 == $id2) {
die sprintf "ID mismatch %d <=> %d\nXML file line %d\ntitles file line %d\n-",
$id1, $id2, $in_xml->input_line_number, $in_titles->input_line_number
}
$xml_line =~ s/>/ title="$title">/;
}
print $out_xml $xml_line;
}
close $out_xml or die "Failed to close output file: $!";
<强>输出强>
<text id="1" title="title1">
bla bla bla bla.........
</text>
<text id="2" title="title2">
bla bla bla bla.........
</text>
<text id="3" title="title3">
bla bla bla bla.........
</text>
答案 2 :(得分:2)
awk '
NR==FNR {
id = $1
sub(/^[^[:space:]]+[[:space:]]+/,"")
map["<text id=\"" id "\">"] = "<text id=\"" id "\" title=\"" $0 "\">"
next
}
$0 in map { $0 = map[$0] }
1
' file2 file1
如果file2是制表符分隔的,那么它会更简单,我希望更快:
awk -F'\t' '
NR==FNR {
map["<text id=\"" $1 "\">"] = "<text id=\"" $1 "\" title=\"" $2 "\">"
next
}
$0 in map { $0 = map[$0] }
1
' file2 file1
答案 3 :(得分:1)
这是GNU awk的另一种方法
gawk '
NR == FNR { title[NR] = $0; next }
match($0, /<text id="([[:digit:]]+)">/, m) {
sub(/>/, " title=\"" title[m[1]] "\">")
}
{print}
' titles articles
awk保留2个计数器:FNR
是当前正在处理的文件中的记录号; NR
是到目前为止处理的所有记录的记录号。对于第一个文件中的所有记录,条件NR == FNR
都为真。
你需要GNU awk来扩展他的match()函数,第三个参数是一个存储正则表达式匹配部分的数组。
答案 4 :(得分:1)
这可能适合你(GNU sed):
sed -r 's|^([0-9]+)\s*(.*)|/(<text id="\1")(>)/s//\\1 title="\2"\\2/|;t;d' file2 |
sed -rf - file1
针对包含标题的文件运行sed脚本,以生成对源文件运行的sed脚本。
小心标题中的元字符!
答案 5 :(得分:1)
这是另一个Perl版本。它首先将所有标题读入哈希,然后将原始文件中的每一行复制到新文件中,必要时替换。
use strict;
use warnings;
open (my $title_file, '<', 'titles.txt') or die "Could not open titles.txt, $!";
my %titles;
while (<$title_file>) {
chomp;
my ($id,$title) = split(m/\s+/,$_,2);
$titles{$id} = $title;
}
close $title_file;
open (my $in_file, '<', 'in.txt') or die "Could not open in.txt, $!";
open (my $out_file, '>', 'out.txt') or die "Could not open out.txt, $!";
while (<$in_file>) {
if (m/<text id=/) {
s/<text id="(\d+)">/<text id="$1" title="$titles{$1}">/;
}
print $out_file $_;
}
close $in_file;
close $out_file;
答案 6 :(得分:1)
从具有同伴ID - 标题
的文件中为您的sed指令创建第一个文件sed 's|\([0-9]\{1,\}\)[[:blank:]]*\([^[:blank:]].*\)|/<text id="\1"/ {s/>/ title="\2">/\
b\
}|' ID_Title_File > /tmp/ID_Chg.sed
2加速器(与您的版本相比)
从此操作列表中处理您的大文件
sed -unbuffer -f /tmp/ID_Chg.sed file1 > Output
对于GNU sed,您可能需要-posix选项(在KSH / AIX上进行测试)
仅供测试目的:
Increment=$((80000 / 128));echo "" > /tmp/ID_Chg.sed;Iter=0;while [ $Iter -lt 80000 ]; do echo "/id=\"$Iter\""/ b r$Iter >> /tmp/ID_Chg.sed; let Iter+=Increment; done
sed 's|\([0-9]\{1,\}\)[[:blank:]]*\([^[:blank:]].*\)|:r\1\
/ ^ / title =“\ 2”&gt; / \ b \ } |” ID_Title.lst&gt;&gt; /tmp/ID_Chg.sed
其中80000是ID的数量和128个子部分(“加速器”)想要的数量