【全角半角・大文字小文字正規化】VBA非依存の組み込み関数のみで構築する文字列クリーニングモジュール
開発現場で度々エンジニアを絶望の淵に追い込むもの、それは「野良データ」だ。
Excelから吐き出されたCSV、ユーザーが適当に入力したWebフォームのデータ、そしてレガシーなDBから抽出されたテキスト。これらは半角と全角がカオスに混ざり合い、大文字と小文字が乱れ飛んでいる。プライマリキーの比較やマスタ照合において、この揺らぎは一瞬でシステムを沈黙させる。
多くのVBAプログラマであれば `StrConv` 関数に頼るところだろう。しかし、私たちVBScript(WSH)を主戦場とするアーキテクトにとって、それは甘美な罠だ。VBScriptのランタイム環境には `StrConv` なんて便利なものは標準搭載されていない。Excelの裏技(`Application.WorksheetFunction`)を呼ぶ? サーバーサイドやタスクスケジューラで動かすバッチ処理において、Excelのプロセスを背後で立ち上げるなど、パフォーマンスの観点からもメモリリークの観点からも「悪手」の極みだ。
今回は、外部ライブラリやExcelに一切依存せず、VBScriptのプリミティブな `AscW` / `ChrW` 関数のみを駆使して、極限まで最適化された「全角半角・大文字小文字正規化モジュール」の設計思想と実装を伝授する。
—
1. なぜ `StrConv` なしで文字列操作を挑むのか?
VBScriptで文字列を正規化する際、安易に正規表現(`RegExp`)を乱発する開発者がいる。しかし、文字種ごとの細かいオフセット計算や、全角英数(`A-Za-z0-9`)と半角英数(`A-Za-z0-9`)の1文字ずつのマッピングを正規表現だけで行おうとすると、コードは肥大化し、メンテナンス不可能なスパゲッティと化す。
私たちが目指すべきは、「UTF-16の文字コード空間(Code Point)を直接制御する」というアプローチだ。
Windows環境のVBScriptが扱う文字列は内部的にUTF-16(UCS-2)でエンコーディングされている。つまり、すべての文字は数値(コードポイント)に他ならない。
全角英数と半角英数は、Unicode空間上で「一定の規則的なオフセット(距離)」をもって配置されている。この数学的な規則性を利用すれば、ループと条件分岐だけで、圧倒的な高速性と確実性を担保したクリーニングが可能になる。
—
2. 文字コードの物理法則を知る
実装に入る前に、Unicode上における文字の「物理配置」を確認しておこう。
1. 半角英数字:
- 半角英数(`0-9`, `A-Z`, `a-z`)は、ASCIIコードの範囲に収まる。
- `0-9`: `&H30` ~ `&H39` (48 ~ 57)
- `A-Z`: `&H41` ~ `&H5A` (65 ~ 90)
- `a-z`: `&H61` ~ `&H7A` (97 ~ 122)
2. 全角英数字:
- 全角英数(`0-9`, `A-Z`, `a-z`)は、全角記号・カタカナ領域の近く、UTF-16では `FF00` 番台(全角・半角幅調整済み文字ブロック)に配置されている。
- `0-9`: `&HFF10` ~ `&HFF19`
- `A-Z`: `&HFF21` ~ `&HFF3A`
- `a-z`: `&HFF41` ~ `&H5A` (※ `&HFF5A`)
ここで重要な法則に気づくだろう。
「全角英数字のコードポイント = 半角英数字のコードポイント + &HFEE0 (65248)」 という美しいオフセット関係が成り立っている。
この数学的法則さえ握っていれば、全角から半角への変換は、該当範囲の文字コードから `&HFEE0` を引くだけで完了する。逆もまた然りだ。
—
3. プロダクションコード:ポータブル文字列クリーニングモジュール
実務の現場でそのままコピペして組み込める、堅牢でモジュール化されたVBScriptコードを提示する。エラーハンドリング(`On Error Resume Next` の乱用禁止、型安全な処理)を徹底し、巨大なテキストファイルやDB連携時のストリーム処理でも耐えうる設計にしている。
‘ ==============================================================================
‘ Module Name: StringNormalizer.vbs
‘ Description: VBA/Excel非依存。AscW/ChrWのみで高速に全角半角・大文字小文字を正規化する
‘ ==============================================================================
Option Explicit
Class StringNormalizer
‘ ————————————————————————–
‘ Method: ToHalfWidthAlphaNumeric
‘ Description: 全角の英数字を半角に変換する
‘ ————————————————————————–
Public Function ToHalfWidthAlphaNumeric(ByVal strInput)
If IsNull(strInput) Or Len(strInput) = 0 Then
ToHalfWidthAlphaNumeric = “”
Exit Function
End If
Dim i, c, code, result
result = “”
For i = 1 To Len(strInput)
c = Mid(strInput, i, 1)
code = AscW(c)
‘ 負の値(符号付き16ビットのオーバーフロー)対策
If code < 0 Then code = code + 65536
' 全角英数 (A-Z: FF21-FF3A, a-z: FF41-FF5A, 0-9: FF10-FF19)
If (code >= &HFF21 And code <= &HFF3A) Or _
(code >= &HFF41 And code <= &HFF5A) Or _
(code >= &HFF10 And code <= &HFF19) Then
code = code - &HFEE0
result = result & ChrW(code)
' 全角スペース ( : 3000) -> 半角スペース (20)
ElseIf code = &H3000 Then
result = result & ” ”
Else
result = result & c
End If
Next
ToHalfWidthAlphaNumeric = result
End Function
‘ ————————————————————————–
‘ Method: ToUpperCase
‘ Description: 半角/全角英字の大文字化(必要に応じて全角も同時に統一)
‘ ————————————————————————–
Public Function ToUpperCase(ByVal strInput)
If IsNull(strInput) Or Len(strInput) = 0 Then
ToUpperCase = “”
Exit Function
End If
Dim i, c, code, result
result = “”
For i = 1 To Len(strInput)
c = Mid(strInput, i, 1)
code = AscW(c)
If code < 0 Then code = code + 65536
' 半角小文字 (a-z: 61-7A) -> 大文字化 (-20h = 32)
If code >= &H61 And code <= &H7A Then
result = result & ChrW(code - 32)
' 全角小文字 (a-z: FF41-FF5A) -> 大文字化 (オフセット維持したまま大文字全角へ)
ElseIf code >= &HFF41 And code <= &HFF5A Then
result = result & ChrW(code - &H20)
Else
result = result & c
End If
Next
ToUpperCase = result
End Function
' --------------------------------------------------------------------------
' Method: CleanText (統合パイプライン)
' Description: 全角半角統一 + 大文字化 + 前後の空白トリムをワンストップで実行
' --------------------------------------------------------------------------
Public Function CleanText(ByVal strInput)
Dim temp
temp = Me.ToHalfWidthAlphaNumeric(strInput)
temp = Me.ToUpperCase(temp)
CleanText = Trim(temp)
End Function
End Class
' ==============================================================================
' 【実行・検証用サンプルコード】
' ==============================================================================
Dim normalizer
Set normalizer = New StringNormalizer
Dim rawData, cleanedData
rawData = " ABCdef12345 "
cleanedData = normalizer.CleanText(rawData)
WScript.Echo "変換前: [" & rawData & "]"
WScript.Echo "変換後: [" & cleanedData & "]"
' 出力結果: 変換後: [ABCDEF12345]
---
4. 実務(ファイル・データベース連携)における注意点
このモジュールを実際の業務自動化スクリプトに組み込む際、チーフアーキテクトとしていくつか警鐘を鳴らしておきたい。
① 符号付き16ビット(`AscW` の罠)
VBScriptの `AscW` は、戻り値として `Integer` 型(-32768 ~ 32767)を返す。
Unicodeのコードポイントが `&H8000` を超える領域(一部の漢字や特殊記号)を扱う際、値がマイナスに反転する。今回のコードでは `If code < 0 Then code = code + 65536` というビット補正を入れているため安全だが、この配慮を怠ると予期せぬ `Type mismatch` や文字化けを引き起こす。
② ファイル入出力時の文字コード(BOMとUTF-8)
データベースやCSVからデータを読み込む際、暗黙的にシフトJIS(CP932)で処理していると、Unicodeの特殊文字が丸められて `?` に変わる(ロストする)。
FSO(FileSystemObject)でファイルを読み書きする際は、ADODB.Streamオブジェクトを使用し、明示的に `Charset = “UTF-8″` を指定してテキストをメモリ上に展開してから、今回の正規化モジュールに流し込むのがプロの作法だ。
—
5. まとめ
VBScriptはレガシーな言語と揶揄されることもあるが、環境依存性を極限まで排除した「自立したスクリプト」を書く上で、そのプリミティブな仕様は逆に強力な武器となる。
Excelのインスタンスに頼らず、メモリ上の文字コード操作だけで完結するこのクリーニングモジュールを導入すれば、あなたの自動化バッチはより軽快に、より頑健に、どんな環境でもエラーなく走り続けるだろう。
現場のインフラやミドルウェアの制約に泣かされる時代はもう終わりだ。正しいアーキテクチャで、スマートな自動化を実現してほしい。
