hozugawa.net · A neutral informational page

CharLowerとCharUpperで文字の大文字小文字変換

Windowsアプリケーションを開発する際、ユーザーの入力や外部データに含まれる英字の大文字・小文字を統一する必要が生じることがあります。ロケールに依存せず高速に処理したい場合、Windows APIのCharLowerとCharUpperが有力な選択肢となります。これらは古くから提供されている基本的なAPIですが、バッファ指定版やANSI/Unicode版の使い分けなど、理解しておくべきポイントがいくつか存在します。

本記事では、CharLowerとCharUpperの基本仕様から、具体的なコード例、バッファサイズ指定の必要性、UnicodeとANSIの違い、そして実務で遭遇しやすい落とし穴までを順に整理します。C/C++でWindows向けソフトウェアを実装する方はもちろん、既存のWindows APIリファレンスを横断的に確認したい開発者にとっても参考になれば幸いです。

CharLower関数の概要

CharLowerは、文字列内の英字を小文字へ変換するAPIです。引数として渡したポインタが指すメモリ内容を直接書き換えるインプレース処理を行います。ロケール情報の影響を受けるため、トルコ語のように大文字小文字の規則が特殊な言語でも、想定通りの結果を得やすいという利点があります。

関数プロトタイプは単純で、LPSTR CharLowerA(LPSTR lpsz)のようにANSI版が定義されています。Unicode環境ではCharLowerW、マクロにより切り替えられるCharLowerが用意されており、TCHARベースで書かれたコードでも問題なく動作します。引数には変換対象の先頭ポインタを渡すだけで、終端のNULL文字までを自動で処理範囲とします。

戻り値は変換後の文字列ポインタですが、これは実質的に引数と同じアドレスを返す形になります。NULLを渡した際の挙動は定義されておらず、実装上は元のポインタを返すか例外を起こすかが分かれるため、呼び出し側で必ず有効な文字列を渡すよう保証する必要があります。

CharUpper関数の概要

CharUpperはCharLowerの逆で、英字を大文字化する役割を担います。基本的な仕組みや引数の扱い方はCharLowerとほぼ同様で、対象文字列の先頭ポインタを受け取り、NULL終端までの範囲をその場で変換します。

ANSI版の宣言はLPSTR CharUpperA(LPSTR lpsz)となり、UnicodeビルドではCharUpperWが使われます。文字コード体系を明示的に扱うプロジェクトでは、Aサフィックス付きの関数名を直接指定することでビルド構成に依存しないコードを保つことができます。

ロケールの影響に関してはCharLowerと対称的であり、システム既定のロケールではなく、スレッドに関連付けられたロケールが考慮されます。マルチスレッド環境でロケールを切り替えている場合は、GetThreadLocaleの結果を意識しておくと意図しない変換結果を防げます。

基本的な呼び出し方

実際に使う場合の流れはシンプルです。変換対象を格納するバッファを用意し、CharLowerまたはCharUpperにポインタを渡すだけで結果が得られます。たとえばchar buf[] = "Hello, World!";という配列を用意し、CharLowerに渡すと"hello, world!"に変換されます。

書き込みが行われるのは同じバッファであるため、入力と出力を別々に保持したい場合は事前にコピーを取っておく必要があります。変換前後で元の文字列を残したいケースは少なくないため、用途に応じてstrcpyやStringCchCopyなどで複製を作るのが安全です。

ロケールに依存する処理である以上、システム既定ではなく明示的に設定したいケースでは、setlocaleと組み合わせるCランタイム関数との挙動差を把握しておくことが重要です。Windows APIとCRTでは内部的に参照するテーブルが微妙に異なる場合があり、検証時に差異が見えることがあります。

バッファサイズを指定する派生関数

NULL終端を想定しない固定長バッファを扱いたい場面では、CharLowerBuffおよびCharUpperBuffを使います。これらは変換対象の長さを明示的に指定できるため、バイナリデータや部分的な文字列に対して安全な操作が可能です。

宣言はそれぞれDWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength)、DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength)となっており、第2引数に変換対象の文字数を渡します。戻り値は処理した文字数で、エラー時には0が返されます。

バッファオーバーフローの観点では、文字数指定があっても変換によって1文字あたりのバイト数が増えるわけではないため、書き込みサイズが入力サイズを超えることはありません。ただしNULL終端は保証されないので、後続処理で文字列関数を使う場合は手動で終端を付ける必要があります。

Unicode環境とANSI環境での挙動

Windows APIは歴史的にANSI版とUnicode版を別々に提供してきました。CharLower、CharUpperについてもこの構成が受け継がれており、UNICODE定義の有無で自動的に切り替わるマクロと、明示的にA/Wを選択する関数が用意されています。

ANSI版はコードページに基づく変換を行うため、日本語環境のコードページでは半角英字の範囲だけが処理対象になります。Unicode版はWCHAR配列を直接扱うため、サロゲートペアを含む文字列でも正しく動作する点が大きな利点です。

モダンな開発ではUnicode前提のプロジェクトが増えているため、Wサフィックス付きの関数やTCHARマクロの利用が推奨されます。逆に、既存資産との連携でANSIを維持しなければならないケースでは、変換時のコードページ差異をテストでカバーしておくことが欠かせません。

実装時の注意点

APIとしての落とし穴としては、まずバッファの寿命管理が挙げられます。CharLowerやCharUpperは静的バッファへのポインタを返す実装が許可されており、スタック領域のアドレスを直接渡しても問題ありませんが、結果ポインタの保持期間には十分配慮すべきです。

次にマルチバイト文字を含む文字列です。Shift-JISやUTF-8をANSI版CharLowerに渡すと、2バイト文字の途中で処理が食い違う可能性があります。ロケールと文字コードの対応を事前に確認し、必要に応じてUnicode版へ切り替えるのが望ましいでしょう。

また、エラー検出の仕組みが弱い点も見逃せません。CharLower自体には明確なエラーコードが定義されておらず、不正な入力を渡した場合の挙動は保証されません。信頼できないユーザー入力を扱う場合は、前段にバリデーションを挟むことが安全策となります。

CharLowerBuffとCharUpperBuffの特徴比較

固定長バッファを多用するプロトコル処理やバイナリ解析では、CharLowerBuffとCharUpperBuffが特に重宝します。これらはNULL終端を前提としないため、パケットのペイロードや固定フォーマットのレコードに対しても安全に適用できます。

文字数指定があることから、誤って巨大なバッファ全体を処理してしまう心配も少なくなります。性能面では、長さが分かっている分だけ処理が確定するため、コードが読みやすく、レビュー時の安全性も高めやすいと言えます。

なお、CharLowerBuffAとCharLowerWの間でもANSI/Unicodeの挙動差は同様に発生します。コードページの影響を受けないよう、入力データをWCHARに統一してから処理する設計にすると、長期的な保守性が向上するでしょう。ロケール挙動の細部や他APIとの組み合わせについては、Windows APIの技術情報をまとめた技術情報サイトのリファレンスも参考になります。

関数名 入力形式 バッファ終端 主な用途
CharLower NULL終端文字列 必須 一般的なテキストのインプレース変換
CharUpper NULL終端文字列 必須 一般的なテキストのインプレース変換
CharLowerBuff 文字数指定 不要 固定長データや部分文字列の変換
CharUpperBuff 文字数指定 不要 固定長データや部分文字列の変換

モジュールハンドル周りのAPIと組み合わせる場合、GetModuleHandle解説のような関連エントリを参照すると、ロード済みモジュール内のリソース文字列を統一する際にも応用が利きます。最終的に重要なのは、文字コードとロケールを早期に決定し、データ構造に合わせてCharLower系とCharLowerBuff系を使い分けること、そしてバッファの所有権と書き戻し範囲を常に意識しておくことです。この基本を押さえておけば、Windows環境での文字変換処理は安定して動作させられ、国際化対応にも無理なく拡張できます。