バッチファイルで外部ファイルから1行ずつ取得する

Y子です。
どのご家庭でも毎日行う、外部データの読み込み。
テキストデータを1行ずつ取り込む作業は、朝食前にササっと終わらせたいものです。;p
その例を、今日は書いてみます。

【2021/04/13 21:00 コードの順番を入れ替え、見やすくしました】


# 概要・用途
テキストファイルから1行ずつ読み込み、処理に使います。
たとえば、 __「不要なファイルのリストを用意しておき、削除処理に使う」__ のような用途を想定します。

# コード
## (1) 成功例
いくつか対策を盛り込んだ例です。

```bat:read_extfile.bat
@echo off

rem 引数がない場合は終了する
if "%~1"=="" (
  echo 引数がありません
  pause
  exit /b
)

rem 外部ファイル(引数から取得)
set str_file=%1

for /f "delims= eol=" %%a in (%str_file%) do (
  echo %%a
)

pause
```

外部ファイルとして、記号がいっぱいのヤバそうなファイルを用意してみました。
これを取り込みます。

```test.txt
 !"#$%&'()*+,-./0123456789:
;<=>?@ABCDEFGHIJKLMNOPQRSTU
VWXYZ[\]^_`abcdefghijklmnop
qrstuvwxyz{|}~ !"#$%&'()*+,
-./0123456789:;<=>?@ABCDEFG
HIJKLMNOPQRSTUVWXYZ[\]^_`ab
cdefghijklmnopqrstuvwxyz{|}
~
```

```:実行結果
> read_extfile.bat test.txt
 !"#$%&'()*+,-./0123456789:
;<=>?@ABCDEFGHIJKLMNOPQRSTU
VWXYZ[\]^_`abcdefghijklmnop
qrstuvwxyz{|}~ !"#$%&'()*+,
-./0123456789:;<=>?@ABCDEFG
HIJKLMNOPQRSTUVWXYZ[\]^_`ab
cdefghijklmnopqrstuvwxyz{|}
~
続行するには何かキーを押してください . . .
```

すべての行が出力されました。

なお、この例のバッチファイルは、外部ファイルのパスを引数で取得しています。
なので、バッチファイルに外部ファイルをマウスでドロップしても、同じ動作になります。

## (2) 説明
「`/f`」は`for`文のオプションで、「トークンを代入する」という意味です。
変数`%%a`に、テキストファイルから読み込んだ文字列（トークン）を格納するのに必要です。
なおトークンとは、「区切り文字で区切られた文字列」のことだそうです。

トークンの区切り文字（デリミタ）は、デフォルトでは`半角スペース`と`タブ`です。
また、コメント行は、デフォルトでは`;`で始まる行で、トークンとして無視されます。

上記の例では、これらをデフォルトから変更しています。
`"delims="`はデリミタの設定ですが、イコール（`=`）の右辺が空なので、デリミタを定義なしとしています。
また、`"eol="`はコメント行の設定ですが、これもイコール（`=`）の右辺が空なので、行をどんな文字で始めても、コメント行になりません。

## (3) 失敗例1
デリミタやコメント行の設定をデフォルトのままにすると、以下のような動作になります。

```bat:read_extfile_ng1.bat
@echo off

rem 引数がない場合は終了する
if "%~1"=="" (
  echo 引数がありません
  pause
  exit /b
)

rem 外部ファイル(引数から取得)
set str_file=%1

for /f %%a in (%str_file%) do (
  echo %%a
)

pause
```

`for`文の`"delims= eol="`を削除したので、デフォルトの下記設定になりました。
・デリミタは`半角スペース`と`タブ`
・コメント行は`;`始まり

これに、同じ外部ファイルを与えてみます。

```:実行結果
> read_extfile_ng1.bat test.txt
!"#$%&'()*+,-./0123456789:
VWXYZ[\]^_`abcdefghijklmnop
qrstuvwxyz{|}~
-./0123456789:;<=>?@ABCDEFG
HIJKLMNOPQRSTUVWXYZ[\]^_`ab
cdefghijklmnopqrstuvwxyz{|}
~
続行するには何かキーを押してください . . .
```

成功例から、下記3か所が変わりました。
・1行目の、先頭の`半角スペース`が消えた
・2行目（`;`で始まる行）が、丸ごと消えた
・4行目（`qrs`で始まる行）の、`半角スペース`以降が消えた

つまり、このように動作したことになります。
・`半角スペース`がデリミタ。
　→先頭のスペースは無視される（1行目）
　→途中のスペースでトークンが分割され、1件目だけ取得される（4行目）
・`;`が先頭に来るとコメント行。
　→コメント行は無視される（2行目）


## (4) 失敗例2
デリミタやコメント行の設定を復活させました。
加えて、遅延環境変数を展開したことで、別の問題が生じた例です。

```bat:read_extfile_ng2.bat
@echo off

rem 引数がない場合は終了する
if "%~1"=="" (
  echo 引数がありません
  pause
  exit /b
)

rem 遅延環境変数の展開を有効にする
setlocal enabledelayedexpansion

rem 外部ファイル(引数から取得)
set str_file=%1

for /f "delims= eol=" %%a in (%str_file%) do (
  echo %%a
)

pause
```

```:実行結果
> read_extfile_ng2.bat test3.txt
ECHO は <OFF> です。
;<=>?@ABCDEFGHIJKLMNOPQRSTU
VWXYZ[\]^_`abcdefghijklmnop
qrstuvwxyz{|}~ "#$%&'()*+,
-./0123456789:;<=>?@ABCDEFG
HIJKLMNOPQRSTUVWXYZ[\]^_`ab
cdefghijklmnopqrstuvwxyz{|}
~
続行するには何かキーを押してください . . .
```

成功例から、下記2か所が変わりました。
・1行目が、空文字列として出力された
・4行目（`qrs`で始まる行）の、`!`が消えた

遅延環境変数の展開を有効にすると、読み込んだトークン中の`!`が意味を持ち、未定義の変数として解釈されて空文字列になったり、`!`だけが消えたりします。

# 注意
参考にしたサイトの一部には、「`eol`は空にできないので、外部ファイルで使われない文字を入れて回避する」と書かれていましたが、わたしは空に設定できたようです。原因はわかりませんが、Windowsのバージョンなどに依存するのでしょうか。

# おわりに
外部ファイルが読み込めるようになりました。
また夢が広がりますね。
環境ごとに外部ファイルだけを変えることで、同じプログラムで複数の現場に対応できたりとか！

では！