【BATCH】TSVファイルの特定の列だけを計算して変換する

バッチファイルを初めて使って仕事をしました。

同じようなことをやってる人がいるだろうと調べましたが案外誰もやっておらず、「使い勝手がよくわからないし、希望の要件を満たすのは難しいかも……」と思っていましたが、意外と形になってくれましたので記事化しました。

スポンサーリンク

プログラムの内容

https://storage.iehohs.com/batch-tsv-conversion.zip

バッチファイルは実行ファイルのため、そのままアップするのはよろしくなさそうなのでテキスト形式にしています。実行してみたい人は拡張子を「.txt」から「.bat」にしてください。

基本的に安全ではありますが、もし奇跡的に変換後のファイル名と同じ名前のファイルが存在している場合、そのテキストファイルを上書きしてしまいます。実行するのであれば自己責任でよろしくお願いします。

このようなテキストファイルがあります。1行目はタイトル、2行目以降は以下のようなテーブルを表しています。

商品原価販売価格数量
りんご10050020
みかん8040030
バナナ12060010

各列を区切っているのはスペースに見えますが、タブ記号で区切られています。タブ記号でセパレートされた値(バリュー)ってことで、このような形式のテキストファイルをTSVファイルとよびます。

販売価格は原価の5倍の値が入っています。この販売価格を4倍に再計算するというのが今回の目的です。

また、1回の実行で指定のディレクトリに入っているすべてのファイルに対して同じ操作を行います。

再計算した後のファイルはわかりやすいように「再計算済み_」とファイル名の頭に付けて保存するところまでが要件です。

そしてこれがバッチファイルを実行後のディレクトリ。「再計算済み_」と付いたファイルが増えています。

開くとこんな感じ。販売価格が原価の4倍になっています。

他の3つのファイルも同様の再計算が行われています。

プログラミング (BATCH)

@echo off
setlocal enabledelayedexpansion
for /f "usebackq" %%x in (`dir /B /S C:\保存先\*.txt`) do (
	set filename=%%x
	set filename=!filename:保存先\=保存先\再計算済み_!
	echo 商品一覧 >!filename!
	echo 商品	原価	販売価格	数量 >>!filename!
	set r=1
	for /f "tokens=1,2,3,4" %%a in (%%x) do (
		if !r! geq 3 (
			set /a kekka=%%b*4
			set line=%%a	%%b	!kekka!	%%d
			echo !line! >>!filename!
		)
		set /a r=r+1
	)
)

ポイントは3点。

  • 特定のディレクトリ内のすべてのTSVファイルを読み込む必要がある
  • 再計算するのは3行目からで、1行目と2行目はテンプレとしてそのまま
  • TSVの特定の列の操作が必要(今回は2列目を元に3列目を変換する)

ディレクトリ内のファイルを走査

for /f "usebackq" %%x in (`dir /B /S C:\保存先\*.txt`) do (
)

指定のディレクトリ内のファイルを走査するのはforループを使います。

意外と知られていないけど、知っていると便利な FOR コマンド
JavaScript, PHP, Apache, IIS, MySQL, SQL Server による Web 開発、及び C/C++ 言語による Windows 開発について入門編から詳細事項まで徹底解説しています。

usebackqをオプションに指定するのはシングルクォートを使えるようにするとのことなのですが、なぜこのオプションが必要なのかは不明。とりあえず動いたのでこれでいいかと。参考にしたのは上記のサイトです。

絶対パスでディレクトリを指定。「C:\保存先\*.txt」でCドライブ直下にある「保存先」ディレクトリ内にある拡張子が.txtのファイルを順番に%%xに入れて処理していきます。

バッチファイル本体が対象のディレクトリ内にある場合は「*.txt」だけ指定すればOKです。

%%xのアルファベットは何でもいいのですが、中にネストするループの中で使う変数の都合上xにしています。

ファイル名は置換で変更

set filename=%%x
set filename=!filename:保存先\=保存先\再計算済み_!

先に変換後のファイル名を設定します。

元のファイル名の頭に「再計算済み_」を付けるのですが、単純に変数の頭に文字列をくっつけるというわけにはいきませんでした。

変数%%xには、「C:\保存先\20210517.txt」が格納されており、頭に文字列をくっつけるだけでは「再計算済み_C:\保存先\20210517.txt」となってしまい正常に動作しません。

というわけで「保存先\」の部分を「保存先\再計算済み_」に置換してやれば「C:\保存先\再計算済み_20210517.txt」になりますね。このファイル名はfilenameという変数に格納します。

1~2行目のテンプレを入力

echo 商品一覧 >!filename!
echo 商品	原価	販売価格	数量 >>!filename!

1~2行目はテンプレで内容は固定です。

1行目に「商品一覧」と入力。この時点でfilename(再計算済み_20210517.txt)が作成されます。もし同名のファイルがあれば上書きされます。

続いて「商品 [TAB] 原価 [TAB] 販売価格 [TAB] 数量」と入れるのですが、ここで上書きしてしまうと1行目が消えてしまいますので、これ以降は追記となる「>>!filename!」と記述します。

ちなみにタブ記号はエディター上で直接Tabキーを打てばOK。ただし、エディターによっては半角スペースが複数個入力される場合もあってうまく認識されないことも。メモ帳で入れるのが吉。

列番号を指定してループ

set r=1
for /f "tokens=1,2,3,4" %%a in (%%x) do (
)

ここからいよいよTSVの処理です。

まず変数rに1を代入します。この変数rは処理している行番号を意味します。今回のメインとなる2列目の値を4倍にして3列目に変換する処理は3行目以降のみに必要です。処理している行番号で分岐させるために使います。

ここから1行ずつ処理をしていきます。オプションの「tokens」は指定の列を読み込むという意味で、例えば3だけを指定したら3列目だけを読み込みます。計算対象は2列目ですが、1列目と4列目はそのまま出力する必要がありますのでオプションで指定。3列目は使わないのですが、わかりやすいように一応読み込んでおこうということで、すべての列を指定しています。

変数は%%aとしており、今回4列分のデータをそれぞれ変数に格納する形になっており、%%aに1列目、%%bに2列目、%%cに3列目、%%dに4列目が入っています。この数が多くなってくると変数が被る可能性があるので、ファイル名を指定する変数を%%xとしたわけです。

そしてその後ろの「in (%%x)」で処理対象のファイルを指定しています。

計算処理

if !r! geq 3 (
	set /a kekka=%%b*4
	set line=%%a	%%b	!kekka!	%%d
	echo !line! >>!filename!
)
set /a r=r+1

if文で出てくる「geq」は「>=」、つまり行番号が3以上ならという条件分岐です。3未満であれば処理は行わず変数rに1をプラスします。

3行目以降であれば、変数%%b、すなわち2列目に4を掛けた値を変数kekkaに格納、変数lineに1、2,4列目の値と、3列目に変数kekkaを入れたタブ区切りの1行を格納、この変数lineを「再計算済み_20210517.txt」に追記します。

という処理を最終行まで繰り返し、すべての行が終わったらディレクトリ内にある次のファイルへ、すべてのファイルが終わったら処理終了という感じです。

コメント

コメントする前にお読みください

プログラミングに関する質問について、詳細なコードはお答えしませんのでご了承ください。
また、迷惑コメント防止のために初回のコメント投稿は承認制です。投稿が反映されるまで少し時間がかかります。