[Raku] 如何簡便地讀取文字檔
作者:gugod 發佈於: ,更新於: #rakulang將文字檔案內容讀取出來,都需要經過開檔、讀取、關檔這三步曲,但另外也有些較簡短的寫法,可讓人略檔開檔及關檔,讓 raku 內部自動去處理。雖然也因在使用情境上較有限制,但比較容易,也比較不會寫錯。
讀取全檔
可用 slurp 函式一次讀取全檔:
my $content = slurp("example.txt");slurp 函式會將其參數視為檔名,並假設檔案的文字編碼為 UTF-8。
例如、讓 example.txt 內容為這兩行:
你好
Hello將全檔讀取進來、存於 $content 中後的寫法是
my $content = slurp("example.txt");若對 $content 進行基本檢查,就可看到 $content 內裝了 9 個字符,與預期相同。
say $content.chars;
#=> 9
say $content.comb.Array.raku;
#=> ["你", "好", "\n", "H", "e", "l", "l", "o", "\n"]一次讀一行、一行一行讀
如果要以行單位讀檔,可用 .lines。
從檔名做出 IO::Path 物件的寫法是透過 .IO
"example.txt".IO這寫法有點像是型別轉換。在此物件消滅時會自動關檔。
將全檔以行單位讀取後全數裝到陣列變數 @content 中:
my @content = "example.txt".IO.lines;
say @content.raku;
#=> ["你好", "Hello"].lines 方法其實不會立刻就把全檔讀進來,而是會做個迭代器物件,一次讀一部份,再把讀進來的部份依換行字符切開後提供出來。如果把這迭代器物件存於陣列裡,那使用起來就與普通陣列無異。使用那陣列,就相當於是在讀檔。在取第 $i 個元素時,就會讀檔讀到第 $i 行後記著,而如果之前已經讀過第 $i 行了,就會直接取出之前的記憶,不會再次讀檔。
如果不需讀取全檔,只需要前 10 行,可透過對 .lines 取切片的方式來完成:
my @content = "example.txt".lines.[0..^10];
say @content.raku;
#=> ["你好", "Hello", Any, Any, Any, Any, Any, Any, Any, Any]可看到,後面有很多怪怪的東西。這是因為我們的 example.txt 內容只有兩行,不到十行。因此在取切片時得到了很多「未定義值」。可利用 .grep(&defined) 將那些值去掉:
my @content = "example.txt".lines.[0..^10].grep(&defined);
say @content.raku;
#=> ["你好", "Hello"]如果所需要的是第 10 行到第 19 行,一樣可透過對 .lines 取切片的方式來完成:
my @content = "example.txt".lines.[10..19];如果所需要的是「最後 5 行」,可在取切片時使用 * 算符。此時若 example.txt 內容不到 5 行,則 @content 會是空的:
my @content = "example.txt".lines.[*-5..*];在上述幾種寫法裡,@content 會對應到一個迭代器,就算是取得 .lines 的切片後,也是得到一個只會迭代十個字串的迭代器,在第一次取值時,才會真的進行讀檔。要讓其立刻讀檔,使 @content 內容物裝的是字串,加上 .Array:
@content = "example.txt".lines.Array;若 "example.txt" 本身有 50GB 那麼大,.lines.Array 會讓 raku 立刻把 50GB 讀進來,而 .lines 則是會將讀取的過程稍微往後延。若後方的程式裡只需要看檔案前十行就結束,那這兩種寫法所花費的記憶體量就會差很多。