ファイル／フォルダ名の全角・半角文字を変換するバッチファイルをささっと作る

初出の記事ではファイル名に括弧 `()` 含まれる場合にエラーになっていたため，これを修正しました（2024年8月31日）

# 0. はじめに

筆者は半角カタカナ滅ぶべし・・・と思っている。他にも英数字の半角・全角を混合したファイル名や文章を平気で量産している人種も美意識が欠けており，前世紀中に滅ぶべきだった・・・

かつて下記のようなスクリプトを作ったこともあるくらいだ。

[クリップボード経由で全角半角を変換するスクリプトを HTA でささっと作る（IE のバージョン違いに悩む）- Qiita](https://qiita.com/TETSURO1999/items/e1604f876a35deb52405)

今回は会社の共有ドキュメントが対象である。一応，ファイル名の命名ルールは緩やかに決まっているのだが，英数字の半角・全角文字を混用していても気にしない人が多く，これだとファイル名でソートできないのだ。

幸い，前回の記事でファイル名の大文字・小文字を変換するバッチファイルを作成したこともあり，大半のコードを流用することができそうだ。

[ファイル／フォルダ名の大文字・小文字を変換するバッチファイルをささっと作る - Qiita](https://qiita.com/TETSURO1999/items/c88e1c3d078079b7f385)

# 1. お品書き（仕様案）

- 半角カタカナを全角文字に変えるほうは to Zenkaku Case ということで `ZCASE.CMD`，全角英数文字を半角文字に変えるほうは to Hankaku Case ということで `HCASE.CMD` とする。
- 複数のファイル名を指定できる。ファイル名にはワイルドカードを使用できる。
- オプション `/V` を指定すると変更対象となるファイル名を表示するだけで変更しない。
  ※これは事前確認のため
- オプション `/Q` を指定すると変更したファイル名を表示しない。デフォルトは表示する。
- オプション `/D` を指定するとディレクトリ名（のみ）を変更する。デフォルトはファイル名（のみ）を変更する。

# 2. ファイル名（フォルダ名）の変更方法

[前回の記事](https://qiita.com/TETSURO1999/items/c88e1c3d078079b7f385)でアルファベットの大文字・小文字変換は `for` ループ処理で簡素に記述できたが，残念ながら全角・半角変換は次のように一文字ずつ変換処理を記述しなくてはならない。

```:全角数字の半角変換
set NEWNAME=%NEWNAME:０=0%
set NEWNAME=%NEWNAME:１=1%
set NEWNAME=%NEWNAME:２=2%
set NEWNAME=%NEWNAME:３=3%
set NEWNAME=%NEWNAME:４=4%
set NEWNAME=%NEWNAME:５=5%
set NEWNAME=%NEWNAME:６=6%
set NEWNAME=%NEWNAME:７=7%
set NEWNAME=%NEWNAME:８=8%
set NEWNAME=%NEWNAME:９=9%
```

加えて半角カタカナの場合，濁音・半濁音がややこしい。半角カタカナと半角カナ記号（濁点・半濁点）は別の文字コードが割り当てられているが，全角文字に変換する際，一つの文字コードにまとめる。ここで変換順序を間違えると半角の濁点・半濁点記号のみ取り残されてしまうので要注意だ。つまり清音よりも先に濁音・半濁音のほうから変換する必要があるということだ。

```plantuml
@startuml
<style>
root {
  margin 0
  padding 0
}
</style>
sprite FIGURE <svg width="224" height="256">
<text x="16"  y="64"  fill="black" font-size="64">ﾊ</text>
<text x="16"  y="80"  fill="black" font-size="12">FF8A</text>
<text x="84"  y="52"  fill="black" font-size="48">→</text>
<text x="144" y="64"  fill="black" font-size="64">ハ</text>
<text x="160" y="80"  fill="black" font-size="12">30CF</text>

<text x="16"  y="144" fill="black" font-size="64">ﾊﾞ</text>
<text x="16"  y="160" fill="black" font-size="12">FF8A+FF9E</text>
<text x="84"  y="132" fill="black" font-size="48">→</text>
<text x="144" y="144" fill="black" font-size="64">バ</text>
<text x="160" y="160" fill="black" font-size="12">30D0</text>

<text x="16"  y="224" fill="black" font-size="64">ﾊﾟ</text>
<text x="16"  y="240" fill="black" font-size="12">FF8A+FF9F</text>
<text x="84"  y="212" fill="black" font-size="48">→</text>
<text x="144" y="224" fill="black" font-size="64">パ</text>
<text x="160" y="240" fill="black" font-size="12">30D1</text>
</svg>
title <$FIGURE>
@enduml
```

:::note
単独の半角の濁点・半濁点記号 `U+FF9E`, `U+FF9F` は変換しないものとした。ちなみに Unicode には全角で単独の濁点・半濁点記号 `U+309B`, `U+309C` もあるので，全角文字に変換できなくはないが，あくまで社内の文書ファイルが対象なので，そういう事例は起こり得ないと割り切った。加えて Unicode には前の文字（カタカナ，ひらがなに限らない）と合成して一つの文字に見える合成文字の濁点・半濁点記号 `U+3099`, `U+309A` もある。ファイル名のソートという意味では，これらの合成文字を結合して一つの文字にまとめたほうが良いと思うが，そもそも Shift-JIS で記述するバッチファイルでは書けない・・・と思っていたら，バッチファイルって UTF-8 でも書けるらしい。※詳しくは参考文献参照のこと
:::

# 3. 実装コード

実装コードを以下に示す。ちなみにコードの大半は[前回の記事](https://qiita.com/TETSURO1999/items/c88e1c3d078079b7f385)を流用することができた。

```:ZCASE.CMD（ファイル名の半角カタカナを全角に変換するバッチファイル）
@echo off
rem ------------------------------------------------------------------------
rem ファイル名（複数可）の半角カタカナを全角カタカナに変換する
rem ------------------------------------------------------------------------ 
setlocal enabledelayedexpansion
set DOPT=0
set QOPT=0
set VOPT=0
set ARGS=
rem ------------------------------------------------------------------------
rem オプション解析
rem ------------------------------------------------------------------------
if "%~1"=="" goto USAGE
:LOOP
  set OPT=%~1
  if /I "%OPT%"=="/D" set DOPT=1&&goto ENDIF
  if /I "%OPT%"=="/Q" set QOPT=1&&goto ENDIF
  if /I "%OPT%"=="/V" set VOPT=1&&goto ENDIF
  if "%OPT:~0,1%"=="/" goto ERROR
  if defined ARGS (
    set ARGS=%ARGS% "%~1"
  ) else (
    set ARGS="%~1"
  )
  :ENDIF
  shift
  if "%~1"=="" goto BREAK
goto LOOP
:ERROR
echo オプション %OPT% には対応していません！
exit /b
:BREAK
if not defined ARGS (
  echo ファイル名を指定して下さい！
  exit /b
)
rem ------------------------------------------------------------------------
rem ファイル名／フォルダ名の変更
rem ------------------------------------------------------------------------
if "%DOPT%"=="1" (
  for /D %%I in ( %ARGS% ) do (
    set ATTR=%%~aI
    if "!ATTR:~0,1!"=="d" call :CONVERT "%%~I"
  )
) else (
  for %%I in ( %ARGS% ) do (
    set ATTR=%%~aI
    if "!ATTR:~0,1!"=="-" call :CONVERT "%%~I"
  )
)
exit /b
rem ------------------------------------------------------------------------
rem ファイル名／フォルダ名の変更
rem ------------------------------------------------------------------------
:CONVERT
set OLDNAME=%~nx1
set NEWNAME=%OLDNAME%
set NEWNAME=%NEWNAME:ｶﾞ=ガ%
set NEWNAME=%NEWNAME:ｷﾞ=ギ%
set NEWNAME=%NEWNAME:ｸﾞ=グ%
set NEWNAME=%NEWNAME:ｹﾞ=ゲ%
set NEWNAME=%NEWNAME:ｺﾞ=ゴ%
set NEWNAME=%NEWNAME:ｻﾞ=ザ%
set NEWNAME=%NEWNAME:ｼﾞ=ジ%
set NEWNAME=%NEWNAME:ｽﾞ=ズ%
set NEWNAME=%NEWNAME:ｾﾞ=ゼ%
set NEWNAME=%NEWNAME:ｿﾞ=ゾ%
set NEWNAME=%NEWNAME:ﾀﾞ=ダ%
set NEWNAME=%NEWNAME:ﾁﾞ=ヂ%
set NEWNAME=%NEWNAME:ﾂﾞ=ヅ%
set NEWNAME=%NEWNAME:ﾃﾞ=デ%
set NEWNAME=%NEWNAME:ﾄﾞ=ド%
set NEWNAME=%NEWNAME:ﾊﾞ=バ%
set NEWNAME=%NEWNAME:ﾋﾞ=ビ%
set NEWNAME=%NEWNAME:ﾌﾞ=ブ%
set NEWNAME=%NEWNAME:ﾍﾞ=ベ%
set NEWNAME=%NEWNAME:ﾎﾞ=ボ%
set NEWNAME=%NEWNAME:ﾊﾟ=パ%
set NEWNAME=%NEWNAME:ﾋﾟ=ピ%
set NEWNAME=%NEWNAME:ﾌﾟ=プ%
set NEWNAME=%NEWNAME:ﾍﾟ=ペ%
set NEWNAME=%NEWNAME:ﾎﾟ=ポ%
set NEWNAME=%NEWNAME:｡=。%
set NEWNAME=%NEWNAME:｢=「%
set NEWNAME=%NEWNAME:｣=」%
set NEWNAME=%NEWNAME:､=、%
set NEWNAME=%NEWNAME:･=・%
set NEWNAME=%NEWNAME:ｦ=ヲ%
set NEWNAME=%NEWNAME:ｧ=ァ%
set NEWNAME=%NEWNAME:ｨ=ィ%
set NEWNAME=%NEWNAME:ｩ=ゥ%
set NEWNAME=%NEWNAME:ｪ=ェ%
set NEWNAME=%NEWNAME:ｫ=ォ%
set NEWNAME=%NEWNAME:ｬ=ャ%
set NEWNAME=%NEWNAME:ｭ=ュ%
set NEWNAME=%NEWNAME:ｮ=ョ%
set NEWNAME=%NEWNAME:ｯ=ッ%
set NEWNAME=%NEWNAME:ｰ=ー%
set NEWNAME=%NEWNAME:ｱ=ア%
set NEWNAME=%NEWNAME:ｲ=イ%
set NEWNAME=%NEWNAME:ｳ=ウ%
set NEWNAME=%NEWNAME:ｴ=エ%
set NEWNAME=%NEWNAME:ｵ=オ%
set NEWNAME=%NEWNAME:ｶ=カ%
set NEWNAME=%NEWNAME:ｷ=キ%
set NEWNAME=%NEWNAME:ｸ=ク%
set NEWNAME=%NEWNAME:ｹ=ケ%
set NEWNAME=%NEWNAME:ｺ=コ%
set NEWNAME=%NEWNAME:ｻ=サ%
set NEWNAME=%NEWNAME:ｼ=シ%
set NEWNAME=%NEWNAME:ｽ=ス%
set NEWNAME=%NEWNAME:ｾ=セ%
set NEWNAME=%NEWNAME:ｿ=ソ%
set NEWNAME=%NEWNAME:ﾀ=タ%
set NEWNAME=%NEWNAME:ﾁ=チ%
set NEWNAME=%NEWNAME:ﾂ=ツ%
set NEWNAME=%NEWNAME:ﾃ=テ%
set NEWNAME=%NEWNAME:ﾄ=ト%
set NEWNAME=%NEWNAME:ﾅ=ナ%
set NEWNAME=%NEWNAME:ﾆ=ニ%
set NEWNAME=%NEWNAME:ﾇ=ヌ%
set NEWNAME=%NEWNAME:ﾈ=ネ%
set NEWNAME=%NEWNAME:ﾉ=ノ%
set NEWNAME=%NEWNAME:ﾊ=ハ%
set NEWNAME=%NEWNAME:ﾋ=ヒ%
set NEWNAME=%NEWNAME:ﾌ=フ%
set NEWNAME=%NEWNAME:ﾍ=ヘ%
set NEWNAME=%NEWNAME:ﾎ=ホ%
set NEWNAME=%NEWNAME:ﾏ=マ%
set NEWNAME=%NEWNAME:ﾐ=ミ%
set NEWNAME=%NEWNAME:ﾑ=ム%
set NEWNAME=%NEWNAME:ﾒ=メ%
set NEWNAME=%NEWNAME:ﾓ=モ%
set NEWNAME=%NEWNAME:ﾔ=ヤ%
set NEWNAME=%NEWNAME:ﾕ=ユ%
set NEWNAME=%NEWNAME:ﾖ=ヨ%
set NEWNAME=%NEWNAME:ﾗ=ラ%
set NEWNAME=%NEWNAME:ﾘ=リ%
set NEWNAME=%NEWNAME:ﾙ=ル%
set NEWNAME=%NEWNAME:ﾚ=レ%
set NEWNAME=%NEWNAME:ﾛ=ロ%
set NEWNAME=%NEWNAME:ﾜ=ワ%
set NEWNAME=%NEWNAME:ﾝ=ン%
if "%OLDNAME%"=="%NEWNAME%" exit /b
if "%QOPT%"=="0" echo %~1	%NEWNAME%
if "%VOPT%"=="0" ren "%~1" "%NEWNAME%"
exit /b
rem ------------------------------------------------------------------------
rem ヘルプメッセージ
rem ------------------------------------------------------------------------
:USAGE
echo ファイル名（複数可）の半角カタカナを全角カタカナに変換します。
echo.
echo ZCASE(.CMD) [/D] [/Q] [/V] [ドライブ:][パス][ファイル名]
echo.
echo /D  フォルダ名を変更します。
echo /Q  変更したファイル名を表示しません。
echo /V  変更対象となるファイル名を表示するだけで変更しません。
exit /b
```

```:HCASE.CMD（ファイル名の英数全角文字を半角に変換するバッチファイル）
@echo off
rem ------------------------------------------------------------------------
rem ファイル名（複数可）の全角英数字・全角スペースを半角文字に変換する
rem ------------------------------------------------------------------------
setlocal enabledelayedexpansion
set DOPT=0
set QOPT=0
set VOPT=0
set ARGS=
rem ------------------------------------------------------------------------
rem オプション解析
rem ------------------------------------------------------------------------
if "%~1"=="" goto USAGE
:LOOP
  set OPT=%~1
  if /I "%OPT%"=="/D" set DOPT=1&&goto ENDIF
  if /I "%OPT%"=="/Q" set QOPT=1&&goto ENDIF
  if /I "%OPT%"=="/V" set VOPT=1&&goto ENDIF
  if "%OPT:~0,1%"=="/" goto ERROR
  if defined ARGS (
    set ARGS=%ARGS% "%~1"
  ) else (
    set ARGS="%~1"
  )
  :ENDIF
  shift
  if "%~1"=="" goto BREAK
goto LOOP
:ERROR
echo オプション %OPT% には対応していません！
exit /b
:BREAK
if not defined ARGS (
  echo ファイル名を指定して下さい！
  exit /b
)
rem ------------------------------------------------------------------------
rem ファイル名／フォルダ名の変更
rem ------------------------------------------------------------------------
if "%DOPT%"=="1" (
  for /D %%I in ( %ARGS% ) do (
    set ATTR=%%~aI
    if "!ATTR:~0,1!"=="d" call :CONVERT "%%~I"
  )
) else (
  for %%I in ( %ARGS% ) do (
    set ATTR=%%~aI
    if "!ATTR:~0,1!"=="-" call :CONVERT "%%~I"
  )
)
exit /b
rem ------------------------------------------------------------------------
rem ファイル名／フォルダ名の変更
rem ------------------------------------------------------------------------
:CONVERT
set OLDNAME=%~nx1
set NEWNAME=%OLDNAME%
set NEWNAME=%NEWNAME:　= %
set NEWNAME=%NEWNAME:０=0%
set NEWNAME=%NEWNAME:１=1%
set NEWNAME=%NEWNAME:２=2%
set NEWNAME=%NEWNAME:３=3%
set NEWNAME=%NEWNAME:４=4%
set NEWNAME=%NEWNAME:５=5%
set NEWNAME=%NEWNAME:６=6%
set NEWNAME=%NEWNAME:７=7%
set NEWNAME=%NEWNAME:８=8%
set NEWNAME=%NEWNAME:９=9%
set NEWNAME=%NEWNAME:Ａ=A%
set NEWNAME=%NEWNAME:Ｂ=B%
set NEWNAME=%NEWNAME:Ｃ=C%
set NEWNAME=%NEWNAME:Ｄ=D%
set NEWNAME=%NEWNAME:Ｅ=E%
set NEWNAME=%NEWNAME:Ｆ=F%
set NEWNAME=%NEWNAME:Ｇ=G%
set NEWNAME=%NEWNAME:Ｈ=H%
set NEWNAME=%NEWNAME:Ｉ=I%
set NEWNAME=%NEWNAME:Ｊ=J%
set NEWNAME=%NEWNAME:Ｋ=K%
set NEWNAME=%NEWNAME:Ｌ=L%
set NEWNAME=%NEWNAME:Ｍ=M%
set NEWNAME=%NEWNAME:Ｎ=N%
set NEWNAME=%NEWNAME:Ｏ=O%
set NEWNAME=%NEWNAME:Ｐ=P%
set NEWNAME=%NEWNAME:Ｑ=Q%
set NEWNAME=%NEWNAME:Ｒ=R%
set NEWNAME=%NEWNAME:Ｓ=S%
set NEWNAME=%NEWNAME:Ｔ=T%
set NEWNAME=%NEWNAME:Ｕ=U%
set NEWNAME=%NEWNAME:Ｖ=V%
set NEWNAME=%NEWNAME:Ｗ=W%
set NEWNAME=%NEWNAME:Ｘ=X%
set NEWNAME=%NEWNAME:Ｙ=Y%
set NEWNAME=%NEWNAME:Ｚ=Z%
set NEWNAME=%NEWNAME:ａ=a%
set NEWNAME=%NEWNAME:ｂ=b%
set NEWNAME=%NEWNAME:ｃ=c%
set NEWNAME=%NEWNAME:ｄ=d%
set NEWNAME=%NEWNAME:ｅ=e%
set NEWNAME=%NEWNAME:ｆ=f%
set NEWNAME=%NEWNAME:ｇ=g%
set NEWNAME=%NEWNAME:ｈ=h%
set NEWNAME=%NEWNAME:ｉ=i%
set NEWNAME=%NEWNAME:ｊ=j%
set NEWNAME=%NEWNAME:ｋ=k%
set NEWNAME=%NEWNAME:ｌ=l%
set NEWNAME=%NEWNAME:ｍ=m%
set NEWNAME=%NEWNAME:ｎ=n%
set NEWNAME=%NEWNAME:ｏ=o%
set NEWNAME=%NEWNAME:ｐ=p%
set NEWNAME=%NEWNAME:ｑ=q%
set NEWNAME=%NEWNAME:ｒ=r%
set NEWNAME=%NEWNAME:ｓ=s%
set NEWNAME=%NEWNAME:ｔ=t%
set NEWNAME=%NEWNAME:ｕ=u%
set NEWNAME=%NEWNAME:ｖ=v%
set NEWNAME=%NEWNAME:ｗ=w%
set NEWNAME=%NEWNAME:ｘ=x%
set NEWNAME=%NEWNAME:ｙ=y%
set NEWNAME=%NEWNAME:ｚ=z%
if "%OLDNAME%"=="%NEWNAME%" exit /b
if "%QOPT%"=="0" echo %~1	%NEWNAME%
if "%VOPT%"=="0" ren "%~1" "%NEWNAME%"
exit /b
rem ------------------------------------------------------------------------
rem ヘルプメッセージ
rem ------------------------------------------------------------------------
:USAGE
echo ファイル名（複数可）の全角英数字・全角スペースを半角文字に変換します。
echo.
echo HCASE(.CMD) [/D] [/Q] [/V] [ドライブ:][パス][ファイル名]
echo.
echo /D  フォルダ名を変更します。
echo /Q  変更したファイル名を表示しません。
echo /V  変更対象となるファイル名を表示するだけで変更しません。
exit /b
```

# 4. 参考文献

- [サロゲートペア・結合文字列・合字 - Qiita](https://qiita.com/Nabetani/items/8c69bdd8060f2503683c)
- [【バッチファイル】文字コードはUTF-8のほうがいい - Qiita](https://qiita.com/toracatman/items/a7fefb352ce8210e986d)