Tcl- ファイルの入出力(4) 1つの変数に複数行の文字列を追加する

複数行の文字列を1つの変数に格納する

複数行あるテキストファイルを読み出して変数に格納するには、appendコマンドを使って1行ずつ追加していく方法とreadコマンドで一気に読み込む方法があります。

変数に読み込んだデータを行単位に分割して処理を行うにはsplitコマンドを使います。

これらのやり方について次の項目から紹介していきます。

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

[str.txt]

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

appendコマンド

[書式]

append varName ?value value value ...?

すべてのvalue引数を変数varNameの現在の値に追加します。
varNameが存在しない場合は、すべてのvalue引数を連結した値をvarNameに追加します。

このコマンドは、長い変数を段階的に構築する効率的な方法を提供します。
例えば、$aが長い場合、”append a $b” は、”set a $a$b” よりもはるかに効率的です。

[サンプル] append.tcl

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

# 変数に値を追加していく

# 1つめ
set lst "A B C D E F G H I J K"
foreach c $lst {
    append str $c
}
puts $str

# 2つめ
set n 0
for {set i 1} {$i <= 10} {incr i} {
    append n "," $i
}
puts $n

[実行例]

$ ./append.tcl
ABCDEFGHIJK
0,1,2,3,4,5,6,7,8,9,10

1つめの例は、A~K の文字を連結しています。

2つめの例は、0に「,数字」を連結しています。

1つの変数に1行ずつ追加する

ファイルから読み込んだデータを変数に代入した後、同じ変数にappendコマンドを使ってデータを追加していきます。

[サンプル] rw7.tcl

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

# ファイルから1行ずつ読み込んで連結する。

set fid [open str.txt r]
set str [gets $fid]

# 2行目以降を変数strに連結する。
while {[gets $fid line] >= 0} {
    append str "\n" $line
}
close $fid

puts $str

[実行例]

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

上のサンプルは、ファイルから1行目を読み込み後、行末の改行文字(\n)と次の行の文字列を変数strに1行ずつ連結しています。

最後の行の末尾にも改行文字を含んだ状態にするには以下のようにします。

set fid [open str.txt r]

# 1行目から変数strに連結する。
while {[gets $fid line] >= 0} {
    append str $line "\n"
}
close $fid

puts -nonewline $str

変数strの末尾には改行文字を含んでいるので、putsコマンドで出力する場合、-nonewline を指定しないと末尾に空行を出力します。

1行ずつではなく、ファイルの末尾まで1度の操作で読み込むにはreadコマンドを使います。

readコマンド

[書式]

read ?-nonewline? channelId
read channelId numChars

1つ目の書式は、channelIdからファイルの末尾まで全てのデータを読み込みます。 -nonewline を指定するとファイルの末尾が改行文字であれば、改行文字は破棄されます。

2つ目の書式は、読み込む文字数を指定します。チャネルがマルチバイト・エンコーディングを使用するように構成されている場合、読み取られる文字数は読み取られたバイト数と同じではない可能性があります。

rw7.tcl をreadコマンドを使って書き直すと以下のようになります。

[サンプル] rw8.tcl

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

# ファイルから全て読み込む。

set fid [open str.txt r]
set str [read -nonewline $fid]
close $fid

puts $str

[実行例]

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

str.txtの最終行の末尾には改行文字があります。

readコマンドの引数に-nonewlineを指定しなかった場合、この改行文字も読み込みます。putsコマンドは改行文字を付加して出力するので空の行が余分に出力されます。

これを回避するには、readコマンドまたはputsコマンドのどちらかの引数に、-nonewlineを指定してファイルの末尾の改行を2回行われないようにします。

読み込んだデータを行単位に分割する

readコマンドで読み込んだデータを行単位で分割するには以下のようにします。

[サンプル] rw9.tcl

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

# ファイルから全て読み込む。
set fid [open str.txt r]
set str [read -nonewline $fid]
close $fid

# 行単位に分割して末尾に文字数を表示する。
foreach tmp [split $str "\n"] {
    set len [string length $tmp]
    puts "$tmp $len"
}

[実行例]

$ ./rw9.tcl
Hello! Hello! 13
Good morning! 13
Bye bye! 8
Have a nice day! 16
こんにちわ 5
おはようございます。 10
バイバイ 4
よい1日を! 6

上のサンプルは、readコマンドで読み込んだデータを、splitコマンドを使って行単位に分割しています。

この場合も同じように、-nonewline の指定をしなかったら最終行の末尾にある改行文字を読み込むので、出力結果は以下のようになります。

$ ./rw9.tcl
Hello! Hello! 13
Good morning! 13
Bye bye! 8
Have a nice day! 16
こんにちわ 5
おはようございます。 10
バイバイ 4
よい1日を! 6
 0

※splitコマンドは、「リスト(3) 文字列を分離する – splitコマンド –」で紹介しています。

次回はファイルのコピーとファイルの文字コード・改行コードを変更する方法を紹介します。

コメント

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