スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門編落穂拾い その8 (テキストファイルの読み書き)

今回はテキストファイルの読み込みを紹介します。
まずは読み込む例として次のようなファイルを"s:\number.txt"という名前で保存してあるとしておきます。
 
123 567,864 235 432,223 456 323 832
567 88 96 43 23 22 24
15,11 69 55 11 22 28 56 23 43
 
(注意)数字のあいだはタブ、カンマ、半角スペースのどれかで区切られています。
 
さてファイルの読み込みは.NETのライブラリを使用します。
 
全部読み込むのなら次のようにReadAllLinesメソッドを使うのが一般的です。
 
> let allLineToArr = System.IO.File.ReadAllLines @"s:\number.txt";;
 
val allLineToArr : string [] =
  [|"123 567,864 235 432,223 456 323 832"; "567 88 96 43 23 22 24";
    "15,11 69 55 11 22 28 56 23 43"|]
    
 このように結果が配列に読み込まれます。
    
書き込みはこれの逆で、#seq型のもの(list,array,seq等) を準備しておいて、WriteAllLinesメソッドを使うのが一般的なようです。
 

File.WriteAllLines(@"s:\test.txt",["153,263";"123,537"]);
 
次にseq型を使う方法を紹介します。
まずはファイル名を与えて読み込む関数を定義します。
 
open System.IO
 
let linesRead (fileName: string) =
    seq{                                             //#1
        use fileReader = new StreamReader(fileName)  //#2
        while not fileReader.EndOfStream do          
            let line = fileReader.ReadLine()
            printfn "%s" line //読み込み状態をみるため(実際には不必要)
            yield line                               //#3
        }
 

val linesRead : string -> seq<string>
 
まず#1でsequence expressionsを用いて#2から#3の部分で読み込んだものを、seqの要素とします。
#2ではuseキーワードを使って、fileReaderがスコープから外れた時に、fileReaderを後始末(dispose)するようにしています。後はwhileループを使って、ファイルの終わりまで、一行を一つの要素として書き出しています。
 
それでは、実際にこれを使って上のファイルを読み込んでみます。
 
> linesRead  @"s:\number.txt";;
 
123 567,864 235 432,223 456 323 832
567 88 96 43 23 22 24
15,11 69 55 11 22 28 56 23 43
 
val it : seq<string> =
 seq
    ["123 567,864 235 432,223 456 323 832"; 
     "567 88 96 43 23 22 24";
     "15,11 69 55 11 22 28 56 23 43"]
 
次に2行だけ取り込んでみます。
 
> linesRead  @"s:\number.txt" |> Seq.take 2;;
 
123 567 864 235 432 223 456 323 832
567 88 96 43 23 22 24
 
val it : seq<string> =
  seq ["123 567,864 235 432,223 456 323 832"; "567 88 96 43 23 22 24"]
 
上の例から分かるように、Seqを使っているので、必要な部分しか読み込まれていません。
(ファイルを全部読みこんでから、2行目までを、返したのではなく、ファイル自体2行目までしか読み込んでないということです。このファイルは小さいのでファイル自体はバッファに全部読み込まれていると思います。)
 
お題からははずれますが、ここで、各文字列から数値をとりだしてみます。これにはまずstringの持つSplitメソッドをつかいます。

 
>"23,105".Split([|','|]);;
val it : string [] = [|"23"; "105"|]
 
上の例より分かるように、Splitの引数としては、区切りとなるchar型の値を配列に入れて渡します。
また結果は配列で返ります。区切りとなる値を複数指定するには次のようにします。
 
> "23 105,506".Split([|',';' '|]);;
val it : string [] = [|"23"; "105"; "506"|]
 
次に文字列を数値に変換する方法ですが、int型に変換するのであればSystem.Int32.Parse もしくはSystem.Int32.TryParseを利用します。Parseの方は、変換に失敗すると例外をraiseするので、ここではTryParseを使ってみます。では一つ、変換に成功すればSome (その値),失敗すればNoneを返す関数を定義してみます。
 
let myTryParseInt str =
    let v = ref 0 //特に0に意味なし
    let isSuccess = System.Int32.TryParse(str,v)
    if isSuccess  then
        Some (!v)
    else
        None
 
使ってみます。
 
> myTryParseInt "345";;
val it : int option = Some 345
 
> myTryParseInt "34.5";;
val it : int option = None
 
それではこれらを組み合わせてファイルから数値をとりだして返してみます。
 
let result   = linesRead @"s:\number.txt"
               |> Seq.map (fun s -> s.Split([|'\t';',';' '|]))
               |> Seq.concat
               |> Seq.map myTryParseInt
               |> Seq.filter (fun x -> x.IsSome )
               |> Seq.map (fun x -> x.Value)
 
 
> result;; //seqなので最後まで読み込まない
123 567,864 235 432,223 456 323 832
val it : seq<int> = seq [123; 567; 864; 235; ...]
 
> Seq.iter (printf "%d ") result;; //seqなので一行毎読み込んでは処理している
123 567,864 235 432,223 456 323 832
123 567 864 235 432 223 456 323 832
 567 88 96 43 23 22 24
567 88 96 43 23 22 24 
15,11 69 55 11 22 28 56 23 43
15 11 69 55 11 22 28 56 23 43 
val it : unit = ()
 
> Seq.length result;; //また最初から読み込まれます
123 567,864 235 432,223 456 323 832
567 88 96 43 23 22 24
15,11 69 55 11 22 28 56 23 43
val it : int = 26
 
> let resLst = List.of_seq result;; //リストにしてみます。
123 567,864 235 432,223 456 323 832
567 88 96 43 23 22 24
15,11 69 55 11 22 28 56 23 43
 
val resLst : int list =
  [123; 567; 864; 235; 432; 223; 456; 323; 832; 567; 88; 96; 43; 23; 22; 24;
   15; 11; 69; 55; 11; 22; 28; 56; 23; 43]
   
なお、読み込み先をseqではなく、リストや配列にすると、一気に全部読みます。
 
//seq{ }の代わりに[ ]にすると...
let linesRead (fileName: string) =
    [
        use fileReader = new StreamReader(fileName)
        while not fileReader.EndOfStream do
            let line = fileReader.ReadLine()
            printfn "%s" line //読み込み状態をみるため(実際には不必要)
            yield line
        ]
 
> linesRead @"s:\number.txt";;
123 567,864 235 432,223 456 323 832
567 88 96 43 23 22 24
15,11 69 55 11 22 28 56 23 43
val it : string list =
  ["123 567,864 235 432,223 456 323 832"; "567 88 96 43 23 22 24";
   "15,11 69 55 11 22 28 56 23 43"]
   
//seq{ }の代わりに[| |]にすると...
let linesRead (fileName: string) =
    [|
        use fileReader = new StreamReader(fileName)
        while not fileReader.EndOfStream do
            let line = fileReader.ReadLine()
            printfn "%s" line //読み込み状態をみるため(実際には不必要)
            yield line
        |]
 
> linesRead @"s:\number.txt";;
123 567,864 235 432,223 456 323 832
567 88 96 43 23 22 24
15,11 69 55 11 22 28 56 23 43
val it : string array =
  [|"123 567,864 235 432,223 456 323 832"; "567 88 96 43 23 22 24";
    "15,11 69 55 11 22 28 56 23 43"|]
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

No title

number.txt とnumber.text でファイル名に揺れがあります。

>File.WriteAllLines(@"s:test.txt",["153,263";"123,537"]);
ウチでは、配列でないと実行できませんでした。
バージョンの違いでしょうか?

No title

BLUEPIXYさん、こんにちは。ありがとうございます。
ファイル名については訂正しました。
File.WriteAllLines(@"s:test.txt",["153,263";"123,537"]);;については、ベーター2に付属のものでは
> File.WriteAllLines(@"s:test.txt",["153,263";"123,537"]);;
val it : unit = ()
と実行できました。たぶんバージョンの問題かと思います。
しかしバージョンによって、かなり違っているところがありますね。
とくにList関連の関数は、すごく変化した感じがします。

WriteAllLines

やはり、バージョンの違いなのですね。
.NET の方のバージョンの違いもあるのかもしれませんね。
(^_^;

File.WriteAllLines

.NET 4.0 から String[] だけじゃなくて、
IEnumerable(String) を、受け付けるようになっていますね。

No title

>.NET 4.0 から String[] だけじゃなくて、 IEnumerable(String) を、受け付ける...

BLUEPIXYさん情報ありがとうございます。
プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。