Tcl – ファイルの入出力(3) 1行ずつリストに代入する

1行1要素としてリストに追加後、画面に出力する

通常ファイルから読み込んだデータは、計算や編集などの処理をしてから、再度ファイルに書き込んだり、画面やGUIの部品(ウィジェット)に出力します。

以下のサンプルは、ファイルから読み込んだデータを、一旦リストに格納しておき、リストの要素を1つずつ読み出して処理をしています。

※ファイルは前回の、str.txt を使用します。

[str.txt]

Hello! Hello!
Good morning!
Bye bye!
Have a nice day!
こんにちわ
おはようございます。
バイバイ
よい1日を!

[サンプル] rw6a.tcl

#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

# ファイルから1行ずつ読み込んで文字数をカウントする。

# 1行ずつ読み込んでリストに追加する。
set fid [open str.txt r]
while {[gets $fid line] >= 0} {
    lappend strlst $line
}
close $fid

# 文字数を調べて画面に表示する。
set sum 0
foreach tmp $strlst {
    set len [string length $tmp]
    set sum [expr $sum + $len] 
    puts $len
}
puts "行数は [llength $strlst] 行、文字数の総数は $sum です。"

[実行例]

$ ./rw6a.tcl
13
13
8
16
5
10
4
6
行数は 8 行、文字数の総数は 75 文字です。

stringコマンドは文字列操作のコマンドです。

string length string

string length string” はstringの文字数を返します。サンプルの場合は変数tmpに格納された文字列の文字数を返します。

以下のサンプルは、while {![eof $fid]} {…} で読み込んだ場合の例です。

[サンプル] rw6b.tcl

#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

# ファイルから1行ずつ読み込んで文字数をカウントする。

# 1行ずつ読み込んでリストに追加する。
set fid [open str.txt r]
while {![eof $fid]} {
    set line [gets $fid]
    lappend strlst $line
}
close $fid

# 文字数を調べて画面に表示する。
set sum 0
foreach tmp $strlst {
    set len [string length $tmp]
    set sum [expr $sum + $len] 
    puts $len
}
puts "行数は [llength $strlst] 行、文字数の総数は $sum 文字です。"

[実行例]

$ ./rw6b.tcl
13
13
8
16
5
10
4
6
0
行数は 9 行、文字数の総数は 75 文字です。

rw6b.tclは、getsコマンドが終端を検知した際に戻り値として返した空文字を処理した影響により、行数を誤って、1行多くカウントしています。

1行余分に処理するのはなぜなのかは、「ファイルの入出力(2)」で詳しく説明しています。

このように、while {![eof $fid]} {…} で読み込むと意図しない結果になる事があるので注意が必要です。

【問題です】
入力ファイルの各行の先頭に行番号を付加して画面に出力せよ。

  • 入力ファイルの指定は環境変数のargvを使う。
  • 読み込んだ文字列は一旦リストに格納する。

環境変数について説明していませんが、簡単に言うと以下の例のようにargvには引数リストが格納されます。

例.

$ ./xxxx.tcl yyyy.txt utf8 sjis

を実行すると、以下の情報が環境変数に格納されます。

argv0 : プログラム名 → xxxx.tcl
argv  : 引数リスト   → yyyy.txt utf8 sjis
argc  : 引数の数     → 3

これらの変数は、xxxx.tcl内で通常の変数と同じように参照できます。

[解答例] nl.tcl

#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

# 各行の先頭に行番号を付加して画面に出力する。

set fname [lindex $argv 0]    ;# ファイル名の取得

# ファイルの読み込み。
set fid [open $fname r]
while {[gets $fid line] >= 0} {
    lappend strlst $line
}
close $fid


# 各行の先頭に行番号を付ける。

set n 1    ;# 行番号
foreach tmp $strlst {
    puts [format "%03d : %s" $n $tmp]
    incr n
}

[実行例]

$ ./nl.tcl str.txt
001 : Hello! Hello!
002 : Good morning!
003 : Bye bye!
004 : Have a nice day!
005 : こんにちわ
006 : おはようございます。
007 : バイバイ
008 : よい1日を!

$ ./nl.tcl nl.tcl
001 : #!/bin/sh
002 : # the next line restarts using tclsh \
003 : exec tclsh "$0" "$@"
004 : 
005 : # 各行の先頭に行番号を付加して画面に出力する。
006 : 
007 : set fname [lindex $argv 0]    ;# ファイル名の取得
      ・
      ・
      ・
019 : set n 1    ;# 行番号
020 : foreach tmp $strlst {
021 :     puts [format "%03d : %s" $n $tmp]
022 :     incr n
023 : }

この記事は一旦リストに取り込んでから処理をするという内容なのでリストを使いましたが、これぐらいの処理だったら、リストを使わずに直接、変数lineをformatコマンドに渡した方が効率がいいと思います。

次回は読み込んだ文字列を1つの変数に追加していく方法とreadコマンドによるファイルの読み込みについて紹介します。

コメント

タイトルとURLをコピーしました