2009-01-23 15 views
6

這是一個Erlang的問題。意外的io行爲:frelang在Erlang

我遇到了一些意想不到的行爲:io:fread。

我想知道是否有人可以檢查我使用io的方式是否有問題:fread或io中是否存在錯誤:fread。

我有包含如下的「數字的三角形」的文本文件:

 
59 
73 41 
52 40 09 
26 53 06 34 
10 51 87 86 81 
61 95 66 57 25 68 
90 81 80 38 92 67 73 
30 28 51 76 81 18 75 44 
... 

有每一對數字之間的單個空間,並用回車換行對各行結束。

我使用下面的Erlang程序將這個文件讀入列表中。

 
-module(euler67). 
-author('Cayle Spandon'). 

-export([solve/0]). 

solve() -> 
    {ok, File} = file:open("triangle.txt", [read]), 
    Data = read_file(File), 
    ok = file:close(File), 
    Data. 

read_file(File) -> 
    read_file(File, []). 

read_file(File, Data) -> 
    case io:fread(File, "", "~d") of 
     {ok, [N]} -> 
      read_file(File, [N | Data]); 
     eof -> 
      lists:reverse(Data) 
    end. 

該程序的輸出是:

 
([email protected])30> euler67:solve(). 
[59,73,41,52,40,9,26,53,6,3410,51,87,86,8161,95,66,57,25, 
6890,81,80,38,92,67,7330,28,51,76,81|...] 

注意如何第四行(34)和第五線(10)的所述第一數量的最後一個數字已被合併成一個單一的編號3410.

當我使用「od」轉儲文本文件時,這些行沒有什麼特別之處;他們結束與CR-NL就像任何其他行:

 
> od -t a triangle.txt 
0000000 5 9 cr nl 7 3 sp 4 1 cr nl 5 2 sp 4 0 
0000020 sp 0 9 cr nl 2 6 sp 5 3 sp 0 6 sp 3 4 
0000040 cr nl 1 0 sp 5 1 sp 8 7 sp 8 6 sp 8 1 
0000060 cr nl 6 1 sp 9 5 sp 6 6 sp 5 7 sp 2 5 
0000100 sp 6 8 cr nl 9 0 sp 8 1 sp 8 0 sp 3 8 
0000120 sp 9 2 sp 6 7 sp 7 3 cr nl 3 0 sp 2 8 
0000140 sp 5 1 sp 7 6 sp 8 1 sp 1 8 sp 7 5 sp 
0000160 4 4 cr nl 8 4 sp 1 4 sp 9 5 sp 8 7 sp 

一個有趣的現象是,一些針對出現問題恰好是在文本文件中的16字節邊界(但不是所有的數字,例如6890)。

+0

這看起來像一個錯誤的一切。以Data的長度表示它是32個元素,而不是36個元素。以其他格式排列數據只是移動故障。 (我在Vista + Erland 5.6.5中試過)。 – Godeke 2009-01-23 19:24:27

回答

9

我打算將它作爲Erlang中的一個錯誤,也是一個奇怪的錯誤。更改格式字符串「〜2S」給出了同樣怪異的結果:

["59","73","4","15","2","40","0","92","6","53","0","6","34", 
"10","5","1","87","8","6","81","61","9","5","66","5","7", 
"25","6", 
[...]|...] 

這樣看來,它的票換行字符作爲一個普通字符計數的目的,但不是當它涉及到生產輸出。 Loopy就像所有地獄一樣。一個星期的Erlang編程,我已經深入瞭解源代碼。這可能是我的一個新紀錄......

編輯

多一點調查已證實對我來說這是一個錯誤。調用的是在fread內部使用的方法之一:

> io_lib_fread:fread([], "12 13\n14 15 16\n17 18 19 20\n", "~d").   
{done,{ok,"\f"}," 1314 15 16\n17 18 19 20\n"} 

基本上,如果有要讀取多個值,然後換行,那麼第一個換行符獲取的字符串「仍有待讀」的一部分吃掉。其他測試表明,如果你預留了一個空間,那麼確定,如果你用一個換行符引導字符串,它會要求更多。 (笑)沒有太多的代碼需要通過,沒有太多的代碼專門針對換行符,所以它不應該是'花太長的時間來縮小它並修復它。

編輯^ 2

哈哈!得到了小一點的。

這裏的補丁,STDLIB要(記得要重新編譯,並在舊的頂部掉落新的束文件):

--- ../erlang/erlang-12.b.3-dfsg/lib/stdlib/src/io_lib_fread.erl 
+++ ./io_lib_fread.erl 
@@ -35,9 +35,9 @@ 
    fread_collect(MoreChars, [], Rest, RestFormat, N, Inputs). 

fread_collect([$\r|More], Stack, Rest, RestFormat, N, Inputs) -> 
- fread(RestFormat, Rest ++ reverse(Stack), N, Inputs, More); 
+ fread(RestFormat, Rest ++ reverse(Stack), N, Inputs, [$\r|More]); 
fread_collect([$\n|More], Stack, Rest, RestFormat, N, Inputs) -> 
- fread(RestFormat, Rest ++ reverse(Stack), N, Inputs, More); 
+ fread(RestFormat, Rest ++ reverse(Stack), N, Inputs, [$\n|More]); 
fread_collect([C|More], Stack, Rest, RestFormat, N, Inputs) -> 
    fread_collect(More, [C|Stack], Rest, RestFormat, N, Inputs); 
fread_collect([], Stack, Rest, RestFormat, N, Inputs) -> 
@@ -55,8 +55,8 @@ 
       eof -> 
        fread(RestFormat,eof,N,Inputs,eof); 
       _ -> 
-     %% Don't forget to count the newline. 
-     {more,{More,RestFormat,N+1,Inputs}} 
+     %% Don't forget to strip and count the newline. 
+     {more,{tl(More),RestFormat,N+1,Inputs}} 
      end; 
     Other ->        %An error has occurred 
      {done,Other,More} 

現在提交我的補丁,二郎,補丁和收穫由此產生的名望和榮耀...

0

我注意到有兩個數字合併的多個實例,它看起來是在第四行以後的每一行的行邊界上。

我發現,如果添加空格字符,每行開始在第五之初,即:

59 
73 41 
52 40 09 
26 53 06 34 
10 51 87 86 81 
61 95 66 57 25 68 
90 81 80 38 92 67 73 
30 28 51 76 81 18 75 44 
... 

的數字得到正確解析:

39> euler67:solve(). 
[59,73,41,52,40,9,26,53,6,34,10,51,87,86,81,61,95,66,57,25, 
68,90,81,80,38,92,67,73,30|...] 

它也適用如果您將空白添加到前四行的開頭,也是如此。

這是一個比實際的解決方案更多的解決方法,但它的工作原理。我想弄清楚如何爲io設置格式字符串:fread,這樣我們就不必這樣做了。

UPDATE 這是一個解決方法,不會強制您更改文件。這假定所有數字是兩個字符(< 100):

read_file(File, Data) -> 
case io:fread(File, "", "~d") of 
    {ok, [N] } -> 
     if 
      N > 100 -> 
       First = N div 100, 
       Second = N - (First * 100), 
       read_file(File, [First , Second | Data]); 

      true -> 
       read_file(File, [N | Data]) 
     end; 
    eof -> 
     lists:reverse(Data) 
end. 

基本上,代碼捕獲任何它們是兩個級聯跨越換行,並將它們分割成兩個的數字。

同樣,這是一個意味着可能存在io:fread錯誤的混亂,但是應該這樣做。

再次更新上面將只兩位數輸入工作,但由於例如包的所有數字(甚至是那些< 10)插入一個兩位數字格式,這將在這個例子中工作。

+0

謝謝!這有幫助。如果你發現一個可以工作的格式字符串,那肯定會有幫助。但是爲了確保我們在同一頁面上:您認爲我現在使用的格式字符串(即「〜d」)應該與我的原始文件一起使用嗎?換句話說:io中存在一個錯誤:fread? – 2009-01-28 20:12:08

+0

我沒有看到爲什麼它不應該與原始文件一起工作,但我對Erlang還是有點新,所以我可能會錯過一些東西。這當然可能是一個錯誤,但我不確定在這一點上。 – Vector 2009-01-28 20:47:57

1

除了它似乎是一個erlang庫中的錯誤,我認爲你可以(很容易)繞過這個問題。

鑑於你的文件是面向行的,我認爲最好的做法是你也一行一行地處理它。

請考慮以下構造。它在未修補的erlang上很好地工作,並且因爲它使用延遲評估,它可以處理任意長度的文件,而無需首先將其全部讀入內存。該模塊包含一個應用於每行的函數的示例 - 將一行文本表達的整數轉換爲整數列表。


-module(liner). 
-author("Harro Verkouter"). 
-export([liner/2, integerize/0, lazyfile/1]). 

% Applies a function to all lines of the file 
% before reducing (foldl). 
liner(File, Fun) -> 
    lists:foldl(fun(X, Acc) -> Acc++Fun(X) end, [], lazyfile(File)). 

% Reads the lines of a file in a lazy fashion 
lazyfile(File) -> 
    {ok, Fd} = file:open(File, [read]), 
    lazylines(Fd). 
% Actually, this one does the lazy read ;) 
lazylines(Fd) -> 
    case io:get_line(Fd, "") of 
     eof -> file:close(Fd), []; 
     {error, Reason} -> 
      file:close(Fd), exit(Reason); 
     L -> 
      [L|lazylines(Fd)] 
    end. 

% Take a line of space separated integers (string) and transform 
% them into a list of integers 
integerize() -> 
    fun(X) -> 
     lists:map(fun(Y) -> list_to_integer(Y) end, 
       string:tokens(X, " \n")) end. 


Example usage: 
Eshell V5.6.5 (abort with ^G) 
1> c(liner). 
{ok,liner} 
2> liner:liner("triangle.txt", liner:integerize()). 
[59,73,41,52,40,9,26,53,6,34,10,51,87,86,81,61,95,66,57,25, 
68,90,81,80,38,92,67,73,30|...] 

And as a bonus, you can easily fold over the lines of any (lineoriented) file w/o running out of memory :) 

6> lists:foldl(fun(X, Acc) -> 
6>     io:format("~.2w: ~s", [Acc,X]), Acc+1 
6>     end, 
6>    1, 
6>    liner:lazyfile("triangle.txt")).           
1: 59 
2: 73 41 
3: 52 40 09 
4: 26 53 06 34 
5: 10 51 87 86 81 
6: 61 95 66 57 25 68 
7: 90 81 80 38 92 67 73 
8: 30 28 51 76 81 18 75 44 

乾杯, h。