BLOG

2021.05.17

文字コードと改行コードを考慮したWindows環境でのテキストファイル置換手法

このエントリーをはてなブックマークに追加

こんにちは、クラウドソリューション部の平石です。

日々の開発業務で大量のテキストファイルを編集(置換)して作成する必要がある場合、どのように作成されているでしょうか。
先日、業務で大量のテキストファイルを作成する必要がありましたが、
使用するツールによって効率がたいぶ異なりましたので紹介したいと思います。

やりたいこと

  • ベースとなるテキストファイルがあり、ファイル内の一部の文字列をファイル毎に決められた文字列に置換して新規ファイルとして保存する。
  • 保存するファイル名は「固定の文字列+_連番」となっている。
  • 保存するファイルの文字コードはUTF-8で改行コードはLFとする。

一見簡単なバッチファイルを作れば出来そうなのですが、
Windows でよくある文字コードと改行コードの問題で実現に意外と手間がかかりました。

テキスト編集といえば UNIX 系の perl や sed , awk などを使用するのがスマートですが、
ソフトのインストールも出来ない環境を考慮し、今回は Windows に標準搭載のバッチファイルと PowerShell と VBScript を使用してコーディングした結果を比較したいと思います。

ベースとなるテキストファイル

TestMetaDataAAA.koumokuLLL_291.md

  • 文字コードがUTF-8
  • 改行コードがLF
  • 出力ファイル名と置換対象の文字列(今回は各ファイル内で2つの文字列を置換し、出力ファイル数は3とする)
出力ファイル1
  • ファイル名:TestMetaDataAAA.koumokuLLL_295.md
  • 置換対象文字列①:テスト項目_291 ⇒ テスト項目_295
  • 置換対象文字列②:TestvalueAA__c ⇒ TestvalueMM__c
出力ファイル2
  • ファイル名:TestMetaDataAAA.koumokuLLL_296.md
  • 置換対象文字列①:テスト項目_291 ⇒ テスト項目_296
  • 置換対象文字列②:TestvalueAA__c ⇒ TestvalueOO__c
出力ファイル2
  • ファイル名:TestMetaDataAAA.koumokuLLL_297.md
  • 置換対象文字列①:テスト項目_291 ⇒ テスト項目_297
  • 置換対象文字列②:TestvalueAA__c ⇒ TestvaluePP__c

今回は、上記のベースとなるテキストファイルに対して
仕様通りの文字列の置換を行い、ファイルに出力したいと思います。

それぞれの手法で実行した結果の出力ファイルは、以下の通りとなります。
全ての実行結果で同じ出力内容です。
文字列が置換されていて、文字コードがUTF-8で改行コードがLFとなっています。

出力ファイル

出力ファイル1
  • TestMetaDataAAA.koumokuLLL_295.md

出力ファイル2
  • TestMetaDataAAA.koumokuLLL_296.md

出力ファイル3
  • TestMetaDataAAA.koumokuLLL_297.md

それぞれのコーディング結果や問題点をまとめると以下のようになります。

コーディング結果比較

バッチファイル PowerShell VBScript
行数(コメント除く) 55行 19行 51行
実装時間 6h 4h 2h
問題点
  • 1時的に処理結果を保存するためのファイル出力が必要
  • 改行コードをLFに変換した際に最終行に空行が挿入されるため削除が必要。
  • UTF-8でテキストファイルを出力する際にバイトシーケンスに変換しないと最終行にCRLFが挿入される。
  • UTF-8でテキストファイルを出力するとBOM付きとなるため、変換が必要。

どのコーディング結果も、テキストの置換処理は簡単に出来ましたが
やはり文字コードと改行コードの指定にはひと手間必要でした。

また、それぞれの文法を調べる時間も含めていますので
言語の理解度によっても実装時間はたいぶ変わってくると思います。

最後に、それぞれのソースコードと実行方法は以下の通りですので参考にされて下さい。

1.バッチファイル

ファイル名:makefile.bat

chcp 65001

rem @echo off
set BEFORE_STRING=_291
set BEFORE_STRING2=TestvalueAA__c
set INPUT_FILE=TestMetaDataAAA.koumokuLLL_291.md
set OUTPUT_FILE=TestMetaDataAAA.koumokuLLL
set name_295=TestvalueMM__c
set name_296=TestvalueOO__c
set name_297=TestvaluePP__c
set LF=^

setlocal enabledelayedexpansion

rem ファイル数だけ繰り返して文字列を置換
set i=295
:FOREACH_MD
set it=!name_%i%!
if defined it (

  for /f "delims=" %%a in (%INPUT_FILE%) do (
    set line=%%a
    set line=!line:%BEFORE_STRING%=_%i%!
    set line=!line:%BEFORE_STRING2%=%it%!
    echo !line!>>.\tmp\%OUTPUT_FILE%_%i%.md
  )
  set /a i+=1
  goto :FOREACH_MD
)
else (
  goto :COM_MD2
)

:COM_MD2
rem ファイル数だけ繰り返して改行コードをLFに置換
set i=295
:FOREACH_MD2
if exist .\tmp\%OUTPUT_FILE%_%i%.md (
  for /f "delims=" %%1 in ('find /n /v "" .\tmp\%OUTPUT_FILE%_%i%.md') do (
    set LINE=%%1
    setlocal enabledelayedexpansion
    <NUL set LINE=!LINE:*]=!!LF!
    echo !LINE! >> .\tmp2\%OUTPUT_FILE%_%i%.md
    endlocal
  )
  set /a i+=1
  goto :FOREACH_MD2
)
else (
  goto :COM_MD3
)

rem ファイル数だけ繰り返して空行と不要な行を除外
:COM_MD3
set i=295
:FOREACH_MD3
set OUTPUT_FILE=TestMetaDataAAA.koumokuLLL_%i%.md
if exist .\tmp2\%OUTPUT_FILE% (
  findstr /v /r /c:"^\ *$" .\tmp2\%OUTPUT_FILE% | findstr /v /r /c:"----------"  > .\output\%OUTPUT_FILE%
  set /a i+=1
  goto :FOREACH_MD3
)
endlocal

上記のバッチファイルを文字コードSJIS、改行コードをCRLFで保存して同じフォルダに置換のベースとなるテキストファイルを保存します。makefile.batをダブルクリックまたはコマンドプロンプトから実行するとoutputフォルダ以下にファイルが出力されます。

実行フォルダ

出力ファイル

2.PowerShell

ファイル名:makeMeta.ps1

$BEFORE_STRING="_291"
$BEFORE_STRING2="TestvalueAA__c"
$INPUT_FILE="TestMetaDataAAA.koumokuLLL_291.md"
$OUTPUT_FILE="TestMetaDataAAA.koumokuLLL"
$map = @{
295 = "TestvalueMM__c";
296 = "TestvalueOO__c";
297 = "TestvaluePP__c";
}

# ファイル数だけ繰り返しforeach ($Ke
y in $map.Keys) {
  $afterTxt = "_"+$Key
  $name = $map[$Key]
  ((Get-Content -Path $INPUT_FILE -Encoding UTF8) -join "`n") + "`n"  `
     | % { $_ -replace $BEFORE_STRING, $afterTxt } `
     | % { $_ -replace $BEFORE_STRING2, $name } `     | % {
[Text.Encoding]::UTF8.GetBytes($_) } `
     | Set-Content -Path .\output\TestMetaDataAAA.koumokuLLL_$Key.md -Encoding Byte
}

上記の実行ファイルを保存して、同じフォルダに置換のベースとなるテキストファイルを保存します。
makeMeta.ps1を右クリックし、メニューから「powershellで実行」を選択すると
outputフォルダ以下にファイルが出力されます。

実行フォルダ

出力ファイル

3.VBScript

ファイル名:cscriptTest.vbs

Option Explicit

Dim objStream
Dim tmpStream
Dim strScriptPath
Dim strOpenFile
Dim strWriteFile
Dim objWriteStream
Dim strText
Dim strText1
Dim strText2
Dim adTypeBinary : adTypeBinary = 1
Dim adTypeText : adTypeText = 2
Dim intTxt(2) '置換対象の文字列
intTxt(0) = "TestvalueMM__c"
intTxt(1) = "TestvalueOO__c"
intTxt(2) = "TestvaluePP__c"
Dim intNum : intNum = 295

'--------------------------------------
' 実行パスを取得
'--------------------------------------
strScriptPath = Replace(WScript.ScriptFullName,WScript.ScriptName,"")

'--------------------------------------
' ファイル数だけ繰り返し
'--------------------------------------
Do While intNum < 298
Set objStream = CreateObject("ADODB.Stream")
objStream.Charset = "UTF-8"
objStream.Open
Set tmpStream = CreateObject("ADODB.Stream")
tmpStream.Type = adTypeText
tmpStream.Charset = "UTF-8"
tmpStream.Open

'--------------------------------------
' strWriteFileは書き込むファイル
'--------------------------------------
strWriteFile = "TestMetaDataAAA.koumokuLLL_" & Cstr(intNum) & ".md"

'--------------------------------------
' strOpenFileは読込むファイル
'--------------------------------------
strOpenFile = "TestMetaDataAAA.koumokuLLL_291.md"
objStream.LoadFromFile strScriptPath & strOpenFile

'--------------------------------------
' 読み込み処理
'--------------------------------------
Do Until objStream.EOS = True

'--------------------------------------
' ファイルを1行ずつ読込み置換
'--------------------------------------
strText = objStream.ReadText(-1)
strText1 = Replace(strText,"_291","_" & intNum)
strText2 = Replace(strText1,"TestvalueAA__c", intTxt(intNum-295))

'--------------------------------------
' tmpStreamは一時書き込みファイル
'--------------------------------------
tmpStream.WriteText strText2, 0
intNum = intNum + 1
Loop

'--------------------------------------
' BOMなしでstrWriteFileに書き込む
'--------------------------------------
tmpStream.Position = 0
tmpStream.Type = adTypeBinary
tmpStream.Position = 3
Dim bin : bin = tmpStream.Read()
tmpStream.Close

Dim outStream : Set outStream = CreateObject("ADODB.Stream")
outStream.Type = adTypeBinary
outStream.Open()
outStream.Write(bin)
outStream.SaveToFile strScriptPath & "output\" & strWriteFile, 2
outStream.Close()

Loop

Set objStream = NothingSet o
utStream = Nothing
Set tmpStream = Nothing

上記の実行ファイルを保存して、同じフォルダに置換のベースとなるテキストファイルを保存します。
cscriptTest.vbsをダブルクリックすると
outputフォルダ以下にファイルが出力されます。

実行フォルダ

出力ファイル

カレンダー

«5月»
      1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31      

ブログ内検索