2014-01-14 30 views
2

我有像數據的文本文件:使用awk排序數據或Perl

# Pri: Rest, DTG: 20140109183524.013578591, CID: 1004592976 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 

# Pri: Rest, DTG: 20140109183524.013580787, CID: 1004592977 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 
Option1:34 
Option5:98 

一個數據單元從「#」直到下一個「#」開始。就像上面的示例一樣,數據單位是:

# Pri: Rest, DTG: 20140109183524.013580787, CID: 1004592977 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 

現在我必須在DTG的基礎上對這些數據進行排序。 (例如在第二數據單元中給出的20140109183524.013580787)。

輸出文件將具有相同的數據,但基於DTG值(實際上是日期和時間)進行排序。

其他數據單元就數據排序而言並不重要。

實際的輸入文件將包含數千個這樣的條目。 什麼是實現排序數據的最快方法?速度對於達到要求的結果非常重要。

+1

請發佈您試過的代碼;有沒有錯誤,或者你需要簡單的優化? –

+0

你想如何完成基於DTG的排序?例如,對於'20140109183524.013580787':如果該整數沒有點可以使用,比如'20140109183524013580787'然後做此值的數字升序排序? –

回答

0

嘗試類似的東西(我加了用於測試的一些記錄):

use strict; 
use warnings; 
use Data::Dumper; 
use v5.16; 
my (%data,$DTG); 
while(<DATA>) { 
    if(/^#.*?DTG:\s(.*?),/) { 
    $DTG = $1; 
     } 
    if($DTG) { 
     $data{$DTG} .= $_; 
    } 
    } 
for my $d (sort keys %data) { 
    print $data{$d}; 
    } 


__DATA__ 
# Pri: Rest, DTG: 20140109183524.013578591, CID: 1004592976 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 

# Pri: Rest, DTG: 20140109183524.013580787, CID: 1004592977 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 
Option1:34 
Option5:98 

# Pri: Rest, DTG: 20140109183524.013580900, CID: 1004592977 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 
Option1:34 
Option5:198 
# Pri: Rest, DTG: 20140109183524.000580787, CID: 1004592977 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 
Option1:34 
Option5:298 

或者,如果你喜歡一個oneliner:

perl -wlne 'if(/^#.*?DTG:\s(.*?),/) {$DTG = $1;} if($DTG) {$data{$DTG} .= $_."\n"} END {for(sort keys %data) {print $data{$_},"\n"}}' datafile.txt 
+0

對不起,我對Perl不太瞭解,使用5.16版的原因是什麼? –

+0

這個解決方案並不是真的需要,但是當我創建一個新腳本時,它是我模板的一部分。如果使用在現代Perl版本中添加的功能,通常需要它。 – salparadise

+0

好:)我明白了,我只有5.14.2版本我的機器上(Ubuntu的12.04) –

1

嘗試做這種方式,假設你有之間的空行記錄如示例輸入中所示:

awk -v RS= '{gsub(/a/,"aA"); gsub(/\n/,"aB"); print $5, $0}' file | 
sort | 
awk -v ORS='\n\n' '{sub(/[^ ]+ /,""); gsub(/aB/,"\n"); gsub(/aA/,"a")}1' 

上面將每條記錄壓縮到一行通過將所有「a」轉換爲「aA」,然後將「\ n」轉換爲「aB」(所以您稍後知道在轉換回來時,輸入中的「aB」只能來自第二個gsub(),因爲所有原始的「aB」已被第一個gsub()轉換爲「aAB」,並將其前綴DTG值排序,然後反轉該過程。

我有10萬條記錄(樣品2您發佈重複50000次)定時該上的文件,一共只花了一下下7秒運行:

real 0m6.930s 
user 0m6.722s 
sys  0m0.388s 

爲了幫助@Jotne看看會發生什麼時運行是在bash 4.1.11 Cygwin的腳本(2),Perl 5.14.4,這裏對我來說發生的事情的2個版本的劇本,與修改後的print語句第二:

$ cat file 
# Pri: Rest, DTG: 20140109183524.013578591, CID: 1004592976 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 

# Pri: Rest, DTG: 20140109183524.013580787, CID: 1004592977 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 
Option1:34 
Option5:98 
$ printf "20140109183524.013580787\n20140109183524.013578591\n" | sort 
20140109183524.013578591 
20140109183524.013580787 

$ printf "20140109183524.013580787\n20140109183524.013578591\n" | sort -n 
20140109183524.013578591 
20140109183524.013580787 
$ cat script1.pl 
#!/usr/bin/perl -w 

use strict; 

my $dtg; 
my %data; 

while (<>) { 
    if (/^#.*?DTG: ([\d.]+)/) { 
     $dtg = $+; 
    } 
    $data{$dtg} .= $_; 
} 

print @data{sort keys %data}; 
$ cat script2.pl 
#!/usr/bin/perl -w 

use strict; 

my $dtg; 
my %data; 

while (<>) { 
    if (/^#.*?DTG: ([\d.]+)/) { 
     $dtg = $+; 
    } 
    $data{$dtg} .= $_; 
} 

print @data{sort { $a <=> $b } keys %data}; 
$ perl script1.pl < file 
# Pri: Rest, DTG: 20140109183524.013578591, CID: 1004592976 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 

# Pri: Rest, DTG: 20140109183524.013580787, CID: 1004592977 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 
Option1:34 
Option5:98 
$ perl script2.pl < file 
# Pri: Rest, DTG: 20140109183524.013580787, CID: 1004592977 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 
Option1:34 
Option5:98 
# Pri: Rest, DTG: 20140109183524.013578591, CID: 1004592976 
dn: op=applV,uid=2256c1e8-1166-46e4-a3a9-9fbe27694e12,Name=sub,loc=Ph,o=s,o=s.com 
action: add 
Class: c-subSpecData 
Option3: applView 
Option2: 3 

正如你所看到的Perl腳本的第二個版本打印以相反的順序記錄與期望無論是字母或數字排序。

+0

我測試了這個速度爲16 Mb隨機文件,80000條記錄:它似乎是'janos'解決方案的4倍左右.. @janos運行在0.063秒,這使用了2.43秒。 –

+2

當我在我的100K示例輸入文件上運行janos'解決方案(他在1.3秒內跑了6.9秒)時發現了類似的時間差異,但是當使用他的「校正」排序打印語句時,janos的輸出被錯誤地排序。他原來的那個似乎工作得很好,所以我不知道問題是什麼。 –

+0

是的@janos的新版本也沒有爲我工作.. –

1

在Gnu Awk第4版。1,你可以使用PROCINFO["sorted_in"]排序,像

gawk -f s.awk file 

其中s.awk是:

BEGIN {RS="^$"} 
{ 
    n=split($0,a,/# Pri: [^0-9]* DTG: [0-9]*\.[0-9]*/,s) 
    top=s[0] a[1] 
    for (i=1;i<n; i++) { 
     match(s[i],/DTG: ([0-9]*)\.([0-9]*)/,c) 
     b[(c[1] c[2])]=s[i] a[i+1] 
    } 
} 


END { 
    PROCINFO["sorted_in"]="@ind_num_asc" 
    printf "%s%s", s[0], a[1] 
    for (i in b) 
     printf "%s", b[i] 
    printf "%s", s[n] 
} 

更新

看來,這個方法是由match()功能放緩。 這裏是一個版本,即大約2.5倍的速度:

BEGIN {RS="^$"} 
{ 
    n=split($0,a,/# Pri: [^0-9]* DTG: [0-9]*\.[0-9]*/,s) 
    top=s[0] a[1] 
    for (i=1;i<n; i++) { 
     ind=index(s[i],"DTG:") 
     c=substr(s[i],ind+5) 
     ind=index(c,".") 
     c=substr(c,1,ind-1) substr(c,ind+1) 
     b[c]=s[i] a[i+1] 
    } 
} 


END { 
    PROCINFO["sorted_in"]="@ind_num_asc" 
    printf "%s%s", s[0], a[1] 
    for (i in b) 
     printf "%s", b[i] 
    printf "%s", s[n] 
} 
1

在這裏你去:

#!/usr/bin/perl -w 

use strict; 

my $dtg; 
my %data; 

while (<>) { 
    if (/^#.*?DTG: ([\d.]+)/) { 
     $dtg = $+; 
    } 
    $data{$dtg} .= $_; 
} 

print @data{sort keys %data}; 

如果您保存此腳本script.pl,那麼你可以使用它像這樣:

perl script.pl < data.txt 

然而,排序在最後一行的DTG值時,該sort功能對待值作爲字符串,按字母順序排序他們,因爲@hakon-haegland在他的評論中指出。因此,例如1001之類的值將錯誤地出現在110之前。

如果要強制執行數字排序,你可以試試這個

print @data{sort { $a <=> $b } keys %data}; 

不過,這並不能很好地與數字與數字太多,比如在OP的示例中的那些工作,並且行爲也可能取決於Perl的版本。從perl 5.12.4開始,這種技術似乎可以用小於16位的數字可靠地工作。

+0

+1好的一個!目前,這比我的解決方案快大約3倍,但它做了不同的排序。它根據字符串值進行排序,例如值爲'1,110,1001',它可以排列爲'1,1001,110'。 –

+0

@HåkonHægland很好看!我爲此增加了一個修復程序。 – janos

+0

我不能讓你的改進版的工作,它似乎有類似的問題@Kenosis溶液(它不正確排序)之前,他固定它.. –

0
#!/usr/bin/perl 

$/="#"; # to read the record from # to # as a single line 
open "FH" , "< input.txt " ; 
my @data = <FH> ; 
chomp(@data); 
my %data ; 

foreach my $line (@data) 
{ 
    $line =~ /DTG:\s*(.*?),/; 
chomp($1); 
    $data{$1}=$line ; 
} 


foreach (sort { $a <=> $b }(keys %data)) 
{ 
    print "$data{$_} \n"; 
} 
+0

這似乎是一個非常快的腳本。什麼是添加文本'「喜」'? –

+0

的目的笑..它只是一個帶來人們共同的魔法詞:P ..對不起,這正是我用來測試o/p。 – Gaurav

+0

我看到:)你的代碼仍然很快,但它修改了原始記錄文件,這樣排序後的記錄會丟失原始記錄中的'#'符號,此外,排序是通過邏輯方式完成的,而不是數字方式。 –

0

這個選項設置閱讀段落模式(local $/ = '';)文件,讀取它的塊,因爲這避免了需要來連接「記錄」線的時間。它也不會假定日期/時間標記都是獨一無二的,因此它創建數組用於記錄的哈希值(HOA):

use strict; 
use warnings; 

my %hash; 
local $/ = ''; 

while (<>) { 
    s/\s+$//; 
    push @{ $hash{$1} }, $_ if /DTG:\s+([^,]+?),/; 
} 

for my $key (sort timeStamp keys %hash) { 
    print $_, "\n\n" for @{ $hash{$key} }; 
} 

sub timeStamp{ 
    my @a = split /\./, $a; 
    my @b = split /\./, $b; 
    return $a[0] <=> $b[0] || $a[1] <=> $b[1] 
} 

用法:perl script.pl inFile [>outFile]

最後,可選的參數指示輸出到文件。

希望這會有所幫助!

+0

我試過你的腳本,速度非常快,但不知何故它不能正確排序..也許我在運行時犯了一些錯誤?我對perl還不是很熟悉:) –

+0

@HåkonHægland - 「排序」結果是什麼?記錄使用擴展數據集進行數字排序。此外,做了一個小小的編輯:從每個記錄中刪除尾隨空白,所以「印刷」是一致的。 – Kenosis

+0

仍然沒有正確排序:在此文件上嘗試:http://pastebin.com/5kurgq3L –