2012-08-28 27 views
5

問題:我有在Windows和* nix上生成的數據(主要是CSV格式),並且主要在* nix上處理。 Windows使用CRLF作爲行尾,而Unix使用LF。對於任何特定的文件,我不知道它是否有Windows或* nix行結尾。到現在爲止,我已經寫了這樣的事情來處理的區別:在Perl中正確檢測文件的行尾?

while (<$fh>){ 
    tr/\r\n//d; 
    my @fields = split /,/, $_; 
    # ... 
} 

* nix上的\ n部分相當於大嚼,另外擺脫\ r(CR),如果它是一個窗口生成的文件。

但是現在我想Text :: CSV_XS b/c我開始用帶引號的數據,可能帶有嵌入換行符等等得到更多的數據文件。爲了讓這個模塊讀取這些文件,Text :: CSV_XS :: getline()要求你指定行尾字符。 (我不能像上面那樣讀取每一行,tr/\ n \ r // d,並且他們使用不能正確處理嵌入換行符的Text :: CSV b/c來解析它)。我如何正確檢測任意文件是否使用Windows或* nix樣式行結尾,所以我可以告訴Text :: CSV_XS :: eol()如何chomp()?

我無法在CPAN上找到僅檢測行結束的模塊。我不想首先通過dos2unix轉換所有數據文件,b/c文件很大(數百GB),每個文件花費10分鐘以上處理一些如此簡單的事情似乎很愚蠢。我想寫一個讀取文件頭幾百個字節的函數,並計算LF和CRLF的相關性,但我拒絕相信這沒有更好的解決方案。

任何幫助?

注意:所有文件要麼完全是windows-line結尾或* nix結尾,即它們不是混合在一個文件中。

回答

9

您可以使用:crlfPerlIO layer打開文件,然後告訴Text::CSV_XS使用\n作爲行尾字符。這將默默地將任何CR/LF對映射到單行提要,但這可能是你想要的。

use Text::CSV_XS; 
my $csv = Text::CSV_XS->new({ binary => 1, eol => "\n" }); 

open($fh, '<:crlf', 'data.csv') or die $!; 

while (my $row = $csv->getline($fh)) { 
    # do something with $row 
} 
+0

謝謝,我以前從來不知道PerlIO。這正是我需要的。 – user1481

3

閱讀每個文件的第一行,看看它的最後一個字符。如果是\r,則該文件來自Windows,如果不是,則爲* nix。然後seek開始並開始處理。

如果文件可能有混合行尾(例如嵌入換行符的不同類型),則只能猜測。

1

理論上行結尾不能可靠地確定:這個文件是單行與DOS行結尾嵌入\n s或這是一堆線在行尾有幾個迷路\r字符?

foo\n 
ba\r\n 

foo\nba\r\n 

如果統計分析是不是一種選擇,因爲它太不準確和昂貴的(它需要時間來掃描這種大文件),你必須真正知道什麼編碼是。

如果您可以控制生產應用程序或使用某種元數據來跟蹤生成數據的平臺,那麼最好指定確切的文件格式。

在Perl,\n代表字符依賴於語言環境:舊的Mac \n/\012在* nix的機器,\r/\015和序列上的DOS後裔又名的Windows \r\n/\015\012。所以要做可靠的處理,你應該使用八進制值。

5

因爲Perl 5.10,你可以用它來檢查總路線的結局,

s/\R//g; 

應該在所有情況下,無論是* nix中和Windows。

1

您可以使用PERLIO變量。這具有不需要根據平臺修改腳本的源代碼的優點。

如果你處理的DOS文本文件,設置PERLIO:unix:crlf環境變量:

$ PERLIO=:unix:crlf my-script.pl dos-text-file.txt 

如果你主要與DOS文本文件處理(例如,在Cygwin),你可以把這在你的.bashrc

export PERLIO=:unix:crlf 

(我覺得值應爲PERLIO在Cygwin默認,但顯然它不是)

相關問題