2011-04-22 61 views
1

排序行我希望通過排序基於給定鍵的每一行修改文本文件和舊文件保存爲一個備份。密鑰是每行中包含的數字字符。perl的就地在文本文件中

是否有一個簡單的腳本來完成這件事,最好是在原地?

謝謝!

+0

在我看來,這樣做就地將是非常低效。排序需要重新排序,而這樣做意味着重複移動整個文件內容。你爲什麼要求它在原地而不是在記憶中完成?如果你想得到一個好的答案,你應該說明你的需求,而不是綁定一個特定的「解決方案」。 – ikegami 2011-04-22 23:22:31

+2

你嘗試過'sort'程序嗎? '排序'通常在所有nix平臺上可用。 – ewh 2011-04-22 23:37:54

+0

如果需要,「排序」實用程序在內存中使用臨時文件,而不是就地。儘管如此,它絕對是一個偉大而高效的工具。 – ikegami 2011-04-23 00:23:34

回答

0

沒有一個簡單的腳本來做到這一點,因爲你是在暗示其實是相當複雜和低效。除非你的文件在文件中的長度完全相同,否則幾乎是不可能的(或者,令人難以置信的愚蠢)。

如果你絕對不能做在內存中,並希望自己編寫的代碼,你最好的辦法可能是一個disk based merge sort。你如何使用磁帶驅動器的例子應該給你一些指導。

1

有O(n log n)複雜度的內置排序算法,如Heapsort,但我不明白爲什麼要使用它,而不是像Unix sort命令那樣簡單。除非你有嚴格的性能要求或龐大的數據集......但是,Perl和python可能不是這項工作的最佳工具。

1

說你的排序鍵是數字在每行的開始運行,如下面的例子。

5 Fine 
2 Good 
1 Every 
4 Does 
3 Boy

爲命令行指定一個或多個文件進行排序,您可以使用下面的代碼。

#! /usr/bin/env perl 

use strict; 
use warnings; 

die "Usage: $0 file ..\n" unless @ARGV; 

$^I = ".bak"; 
undef $/; 

while (<>) { 
    print map $_->[0], 
     sort { $a->[1] <=> $b->[1] } 
     map { [ $_, /^(\d+)/ ? $1 : -1 ] } 
     /^(.*\n?)/mg; 
} 

@ARGV包含來自命令行的參數。不帶任何參數運行程序會生成關於標準錯誤的使用指南。

$^I適用於就地編輯,您也可以用Perl的-i開關,覆蓋在perlrun documentation啓用創建備份時加入到文件名擴展。

-i [延伸]
指定由<>構建處理的文件是在就地進行編輯。它通過重命名輸入文件,按原始名稱打開輸出文件並將該輸出文件選擇爲print語句的默認值來完成此操作。

$/是輸入記錄分隔符。將其設置爲未定義值意味着您希望後續調用readline operator來讀取文件結束。性能會受到非常大的輸入影響。

while循環的每次迭代中,特殊變量$_將作爲一個整體保存當前文件的內容。爲了排序線條,我們首先將它們分開。

不要被循環內的print被嚇倒。它是Schwartzian Transform,Perl中的一種常用技術,即使它首次亮相爲less-than-rave reviews。要了解發生了什麼,請從頭到尾閱讀它。

  1. 捕獲當前文件中所有行的列表。/m正則表達式開關使得^匹配一行的開頭,而不僅僅是在目標字符串的開頭。
  2. 對於每一行,嘗試捕獲該行開頭的一個或多個數字,或者默認爲-1。
  3. 按照排序關鍵字的升序對行進行排序。
  4. 最後,按排序順序打印行。啓用就地編輯後,print將輸出到當前正在排序的文件。

在多個程序的風格,一旦你瞭解情況,與使用Schwartzian變換,所有的臨時工看似雜亂不當你寫環路

while (<>) { 
    my @lines = /^(.*\n?)/mg; 
    my @augmented = map { [ $_, /^(\d+)/ ? $1 : -1 ] } @lines; 
    my @sorted = sort { $a->[1] <=> $b->[1] } @augmented; 
    print map $_->[0], @sorted; 
}