未来を創る、テックコミュニティー

どうしてもWindowsで改行コードLFのCSVを加工したい人へ

おおのしょうへい
2021/02/27

こんにちは。シンラボエンジニアおおのです。

今回の記事では、ここ数日対応していた
「Windows環境上での改行コードLF(※)のファイル加工」
についての奮闘記を書き連ねていきます。

※改行コードLF:主にLinuxで利用される改行文字の形式

Windowsで改行コードLFのファイルなんて扱うなよ、という指摘はごもっともです。しかし、これしか受け入れないという顧客からの面倒な要望に対応せざるを得ない残念な境遇の方々に少しでも有用な情報を残せればと思い、取り組んだ内容を記事にしてみました。

もし似たような課題で困っている方がいれば参考にしてみてください。

本記事のサマリ

本記事はわたしのやったことを時系列に沿って書いています。
なので、そんな冗長な内容はいいから早く答えを教えてくれ!という方は「やったこと」の章だけ掻い摘んでお読みください。

・やりたいこと:そもそもの要件
・やったこと1:Windowsバッチで対応
・罠その1:改行コードLF
・やったこと2:Pythonで対応
・罠その2:Pyinstaller
・やったこと3:Powershellスクリプト
・まとめ:がんばった(小並感)

やりたいこと

今回の要件は一言で言うと「CSVファイルから特定の条件のデータのみを抽出する」というもの。内容的には、1年目社員のお勉強にちょうど良さそうなテーマという感じです。

もう少し詳しく今回の要件を説明しておきます。
入力ファイルとなるCSVは、Aという他社製システムから出力されるファイルです。このCSVファイルから特定の条件に一致する行のみを抽出して新たなCSVファイルを生成します。新しく生成されたCSVファイルは、Bという自社システムのインプットとして利用される、といった流れとなります。

ここまでの条件なら1〜2時間もあれば完成できそうな要件です。

ところが…

こちらのデータ抽出ツールは「Windows上」で動かしたい、かつ顧客利用端末で動かす前提なので特別な実行環境の用意はできない、という制約が付くといったものでした。
Linux上での実行ならawkやsedを利用すれば簡単にできそうですが、Windows上での実行しか許されないというのであればパッと思いつくのはWindowsバッチでの実装でしょう。
ということで、まずはWindwosバッチを利用して特定条件の行を抽出するコードを書いてみました。

やったこと1:Windowsバッチで対応

Windowsバッチは、Windowsに昔から標準で備わっているコマンドプロンプト(設定とかいじるときに使う黒い画面)などで実行できるプログラムです。
特別な設定は不要、ファイルをダブルクリックするだけでも実行可能、と顧客の要望である特別な実行環境の用意はせずにWindows環境でツールを動かすという要件にはマッチしそうです。

まあ色々書いていく中で工夫した点はあるのですが、そこは置いておいて作成したサンプルは以下のようになりました。

@setlocal enabledelayedexpansion
@echo off

type nul > %2

set inCsv=%1
set outCsv=%2
set targetFlag=FALSE
for /f “delims=, tokens=1*” %%a in (!inCsv!) do (
 if %%a==”Item” (
  set targetFlag=FALSE
  set rowStr=%%b
  if not “!rowStr:”targetItem1″=!”==”!rowStr!” set targetFlag=TRUE
  if not “!rowStr:”targetItem2″=!”==”!rowStr!” set targetFlag=TRUE

  if !targetFlag!==TRUE (
   echo %%a,%%b>>!outCsv!
  )
 ) else (
  if !targetFlag!==TRUE (
   if “%%a”==”” (
    echo.>>!outCsv!
   ) else if “%%b”==”” (
    echo %%a>>!outCsv!
   ) else (
    echo %%a,%%b>>!outCsv!
   )
  )
 )
)

※公開用に一部改変しています

実行引数の1つ目で入力ファイル名を、2つ目で出力ファイル名を指定します。
ファイルを1行ずつ読み込み、指定された文字列(例では”targetItem”)が入っていた場合のみファイルに出力するといった処理を行っています。

targetFlagを持たせている理由としては、CSVファイル内に1項目中に改行を含む項目が存在するためです。下記の例のように「”(ダブルクォート)」で囲われた範囲を1項目とみなすデータが含まれていた場合、単純に該当条件が存在する行だけを抽出すると2行目や3行目のデータが無視されてしまい欲しい結果が得られなくなります。

“no”,”id”,”c1″,”c2″
“1”,”targetItem”,”c1
aaaa
bbbbb”,”c2″
“2”,”dummyId”,”c1″,”c2″

罠その1:改行コードLF

多少のめんどくささはありましたが、無事Windowsバッチを利用して欲しい行のみを抽出するプログラムができました。
と思ったらまさかの罠が…

Windowsバッチを利用して出力したCSVファイルは改行コードがCRLFというWindowsで一般的に利用されている形式で生成されていました。
これに対して、元のファイルや出力ファイルを利用するBシステムでは、Linux等で一般的に利用されているLFという改行コードになっている必要がありました。

じゃあWindowsバッチで改行コードをLFに変換したらいいか、と思い色々と検索してみました。

検索してみました…

…あれ?Google先生??

Windowsバッチは自動的にCRLFに変換される、なんて嘘ですよね?

まあもっとしっかり調べていけば対処法もあったのかもしれませんが、わたしが調べてみた範囲では明確な答えは見つかりませんでした。

文字コード変換なんてPythonとかスクリプト言語で書けば簡単に変えられるのに…

そんなときにこんな情報が
「PythonをWindows環境で動作するexeファイルに変換する方法」

やったこと2:Pythonで対応

Pythonは機械学習の実装などにもよく利用されるプログラミング言語で、多種多様なライブラリを利用して短い文で色々なことができます。
今回の事例も、特別な実行環境の用意はせずにWindows環境でツールを動かすという要件がなければ初めからPythonでコードを書いていたでしょう。

ということで実際に書いてみたコードはこちら。

# -*- coding: utf-8 -*-
import sys
import csv

idColNum = 6 # 判定対象列番号
targetIds = [‘targetItem1′,’targetItem2’]

# 出力CSVの準備
outCsv = open(‘out.csv’, ‘w’, encoding=’UTF_8′, newline=’\n’)
writer = csv.writer(outCsv,quotechar='”‘,quoting=csv.QUOTE_ALL,lineterminator=’\n’)

# CSV読み込み
with open(sys.argv[1], ‘r’, encoding=’UTF_8′, newline=’\n’) as f:
 reader = csv.reader(f)
 writer.writerow(next(reader))

 # 対象IDの行に絞り込み
 for row in reader:
  if row[idColNum] in targetIds:
   writer.writerow(row)

outCsv.close()

ものの数分で完成。Windowsバッチで苦労したCSVの扱いも難なくこなせます。
あとはこのプログラムをWindows環境で実行するために「Pyinstaller」という仕組みを使ってexeファイルに変換するのみです。

罠その2:Pyinstaller

※この章は一般的な環境では発生しない場合が多い問題を扱っています

Pyinstallerは、ざっくりいうとPythonのソースコードとそのプログラムを実行するために必要なライブラリをまとめてexeファイル化することで、Pythonの実行環境がない場合でもPythonで書いたプログラムを動かせるといった便利なツールです。
pipが用意されている環境でしたら下記のコマンド一発でPyinstallerの利用準備は完了します。

pip install pyinstaller

ただし、これはpipが利用できる環境に限られます。
通常の環境では問題ないのですが、こちらのスクリプトは会社業務の一環で作成していたためpythonの実行環境も社内ネットワーク。そのため、pipなどの通信は遮断されてしまいます。(そんな環境は時代遅れだという意見はごもっともですのでここでは置いておいてください)

それならばとzipをダウンロードしてインストールも試みたのですが、zipファイルからインストールする際にも依存性解決のために外部ライブラリを探しに行くような挙動がありこの部分でNG。家のMacbookなら即インストール完了なのに何という時間の無駄…

そんな文句を言っていても変わらないので仕方なく代替案を考えます。

やったこと3:Powershellスクリプト

batファイルでは改行コードLFへの変換が難しい、pythonはwindowsかつ社内ネットワーク上での構築が難しい、そうなると残された選択肢はwindowsに標準で用意されており改行コード変更も可能なソリューションを探す必要があります。

おしえてgoogle先生→「powershellスクリプト」

個人的にはwindowsのCLI操作=コマンドプロンプト(cmd.exe)なのですが、現在のwindowsはPowershellというCLIツールをデフォルト設定としています。
そして今回の要件になっている改行コードLFへの変換がこのpowershellで実施できるというのです。
なら最初からpowershellでスクリプトも書けよと言われそうですが、powershellスクリプトは多少クセがあり慣れた方で作りたかったという点はあります。
また、windowsバッチで作成すれば、.batファイルをダブルクリックするだけで実行できるという点もプラスになります。

さてこのpowershellスクリプト、powershell経由で利用するのが正攻法ですが、従来のコマンドプロンプトからも実行できたりします。
今回はやったこと1で紹介したwindowsバッチのスクリプトがあったので、こちらを活用して改行コードの変換を行う下記の文を追加しました。

powershell -Command “ls input | foreach{ (cat $_) -join \””`n\”” | set-content $_ }”

こちらの文では、-Command以下の文字列をpowershellで実行します。inputフォルダ内に配置されたファイル(今回の例では抽出済みのデータ)を読み込み、各行の改行コードをLFに変更して読み込んだファイルに上書きしています。

batファイルの最後に上記の文を入れておくことで無事に出力ファイルを改行コードLFの形式に変更できました。

まとめ:がんばった(小並感)

今回の抽出ツール作成、windowsバッチ→python→powershellスクリプトとかなり遠回りして完成したような感覚です。
大した処理でもないので、正直もうちょっとスマートな書き方はあるような気がしますが、無駄に色々と面倒な点があったので備忘録としてこちらのブログ記事にしてみました。

もしどこかで同じような要件にあたり、解決方法に困っている人がいるならその助けになれればと思います。

以上、私の雑記にお付き合いいただきありがとうございました。

この記事を書いた人
おおのしょうへい
エディター