C# Tips | 文字列処理:大文字変換

C# C#
スポンサーリンク

はじめに 「大文字変換」は“比較とフォーマットを安定させるための基本技”

文字列処理の中でも、「大文字変換(Upper)」はかなり頻出のテクニックです。
特に業務システムでは、こんな場面でよく使われます。

検索キーを大文字にそろえて比較したい。
コード値(ID)を大文字で保存するルールにしたい。
ログや帳票で、英字を全部大文字で出したい。

C# には ToUpper というメソッドが用意されていますが、
「何も考えずに使う」と、実はちょっとした落とし穴もあります。

ここでは、プログラミング初心者向けに、

ToUpper の基本
カルチャ(文化圏)による違い
null 安全な大文字変換ユーティリティ
比較や検索での“正しい使い方”

を、例題付きで丁寧に解説していきます。


C# の ToUpper の基本を押さえる

一番シンプルな使い方

まずは、素の ToUpper から見てみます。

string s = "Abc123";

string upper = s.ToUpper();

Console.WriteLine(upper); // ABC123
C#

英字は大文字に、数字や記号、日本語はそのままです。

string s = "Id: ab-123 テスト";

string upper = s.ToUpper();

Console.WriteLine(upper); // ID: AB-123 テスト
C#

ここまでは直感通りで、特に難しいところはありません。

ただし、ToUpper() には「カルチャ(文化圏)」の影響を受ける、という重要なポイントがあります。


重要ポイント① ToUpper は“カルチャ依存”である

カルチャって何?

C#(.NET)の世界では、「カルチャ」という概念があります。
これは、「どの国・地域のルールで文字や日付などを扱うか」という設定です。

例えば、日本なら "ja-JP"、アメリカなら "en-US"、トルコなら "tr-TR" といった具合です。

ToUpper() を引数なしで呼ぶと、「現在のスレッドのカルチャ」に基づいて大文字変換が行われます。
多くの場合は問題になりませんが、一部の言語では「大文字・小文字の変換ルール」が特殊で、
それがバグの原因になることがあります。

有名な例:トルコ語問題

トルコ語には、「i」と「I」に関して特殊なルールがあります。
ここでは詳細には踏み込みませんが、

カルチャによっては、"i".ToUpper() の結果が "I" ではない

ということが起こりえます。

日本語環境で普通に開発していると、あまり遭遇しませんが、
「サーバーのカルチャ設定が変わった」「海外向けに展開した」などのタイミングで、
思わぬ影響が出ることがあります。


重要ポイント② 比較や検索には ToUpperInvariant を使う

“カルチャに左右されたくない”ときの書き方

業務システムでよくあるのが、

「英字の大文字・小文字を区別せずに比較したい」

という要件です。

例えば、ユーザーが入力したコードと、マスタに登録されているコードを比較するときに、

abc123ABC123 を同じとみなしたい

という場面です。

このとき、次のような書き方は「一見正しそうで、実は危うい」パターンです。

bool IsSameCode(string? left, string? right)
{
    if (left is null || right is null)
    {
        return false;
    }

    return left.ToUpper() == right.ToUpper();
}
C#

理由は二つあります。

カルチャ依存の ToUpper() を使っている。
null チェックが毎回必要で、呼び出し側が面倒になる。

ここで使いたいのが、

ToUpperInvariant()

です。

これは、「カルチャに依存しない(不変な)ルールで大文字変換する」メソッドです。
英字の大文字・小文字を扱うときは、基本的にこちらを使うのが安全です。

大文字変換+比較をユーティリティ化する

using System;

public static class StringCaseUtil
{
    public static string ToUpperSafe(string? value)
    {
        return value?.ToUpperInvariant() ?? string.Empty;
    }

    public static bool EqualsIgnoreCase(string? left, string? right)
    {
        string l = ToUpperSafe(left);
        string r = ToUpperSafe(right);

        return l == r;
    }
}
C#

ここでは、

ToUpperSafe で「null を空文字にそろえつつ、カルチャ非依存で大文字変換」
EqualsIgnoreCase で「大文字変換済み同士を比較」

という形にしています。

使い方の例です。

string? input = "abc123";
string? master = "ABC123";

if (StringCaseUtil.EqualsIgnoreCase(input, master))
{
    Console.WriteLine("コード一致");
}
else
{
    Console.WriteLine("コード不一致");
}
C#

これで、input"abc123" でも "AbC123" でも "ABC123" でも、
同じとみなされます。


null 安全な大文字変換ユーティリティ

そのまま ToUpper を呼ぶと null で落ちる

よくあるパターンがこれです。

string? code = GetCodeOrNull();

string upper = code.ToUpperInvariant(); // ← code が null だと NullReferenceException
C#

code が null の可能性があるのに、そのまま ToUpperInvariant() を呼ぶと落ちます。

実務では「null かもしれない文字列」はたくさん出てくるので、
大文字変換も「null 安全」にしておくと安心です。

ToUpperSafe の実装をもう少し丁寧に見る

public static class StringCaseUtil
{
    public static string ToUpperSafe(string? value)
    {
        return value?.ToUpperInvariant() ?? string.Empty;
    }
}
C#

ここで使っている構文を分解してみます。

value?.ToUpperInvariant()

これは「null 条件演算子」です。
value が null でなければ ToUpperInvariant() を呼び、
null ならそのまま null を返します。

その結果に対して、

?? string.Empty

を適用しています。
これは「null 合体演算子」で、左側が null なら右側(ここでは空文字)を返します。

つまり、

value"Abc" なら "ABC" を返す。
value が null なら string.Empty を返す。

という動きになります。

これを一度ユーティリティとして定義しておけば、
呼び出し側は「null かもしれない」を気にせずに書けます。

string? raw = GetCodeOrNull();

string normalized = StringCaseUtil.ToUpperSafe(raw);

// normalized は必ず null ではない(空文字以上)
Console.WriteLine(normalized);
C#

大文字変換とトリム・全角半角変換の組み合わせ

実務では「大文字変換だけ」では足りないことが多い

現実の業務データは、だいたいこんな感じで“汚れています”。

前後にスペースが付いている。
全角英数字が混ざっている。
小文字・大文字がバラバラ。

例えば、ユーザーが入力した「顧客コード」がこうだとします。

" abC123 "

これをマスタの "ABC123" と比較したいとき、
やりたいことはこうです。

前後の空白をトリムする。
全角英数字を半角にそろえる。
大文字にそろえる。

これを毎回バラバラに書くと、すぐにカオスになります。
なので、「コード用の正規化ユーティリティ」としてまとめてしまうのがおすすめです。

コード例:コード値を比較用に正規化する

public static class CodeNormalizeUtil
{
    public static string NormalizeCode(string? value)
    {
        // 1. null を空文字に
        string s = value ?? string.Empty;

        // 2. トリム(全角スペースも含めて)
        s = StringTrimUtil.TrimWithZenkaku(s);

        // 3. 全角英数字→半角
        s = ZenkakuConverter.ToHankakuBasic(s);

        // 4. 大文字にそろえる(カルチャ非依存)
        s = s.ToUpperInvariant();

        return s;
    }

    public static bool CodeEquals(string? left, string? right)
    {
        string l = NormalizeCode(left);
        string r = NormalizeCode(right);

        return l == r;
    }
}
C#

使い方の例です。

string? input = " abC123  ";
string? master = "ABC123";

if (CodeNormalizeUtil.CodeEquals(input, master))
{
    Console.WriteLine("コード一致");
}
else
{
    Console.WriteLine("コード不一致");
}
C#

ここまでやっておくと、

全角・半角
前後の空白
大文字・小文字

といった“よくある揺れ”を全部吸収したうえで比較できるようになります。

大文字変換は、この「正規化パイプライン」の最後の仕上げとして使うイメージです。


大文字変換を“保存時”に使うか“比較時”に使うか

保存時にそろえるパターン

一つの考え方は、

「データベースに保存するときに、大文字にそろえてしまう」

というものです。

例えば、顧客コードを登録するときに、

entity.Code = CodeNormalizeUtil.NormalizeCode(inputCode);
C#

のようにしておけば、DB 上の Code は常に「トリム済み・半角・大文字」という状態になります。

この場合、検索時も同じ NormalizeCode を通して比較すればよいので、
システム全体で一貫性が保ちやすくなります。

比較時だけそろえるパターン

もう一つの考え方は、

「保存時は元の形を残し、比較や検索のときだけ大文字にそろえる」

というものです。

例えば、ユーザーが入力したコードをそのまま保存しつつ、
検索時には NormalizeCode を通して比較する、という形です。

どちらが正解というわけではなく、

元の入力をそのまま残しておきたいか
それとも、システム内では“正規化された形”だけを持ちたいか

という設計の好みと要件次第です。

大事なのは、

「どこで大文字変換をかけるか」をチーム内で決めておく

ことです。


まとめ 「大文字変換ユーティリティ」は“カルチャと揺れを吸収するための基礎パーツ”

大文字変換は、一見シンプルですが、
カルチャや null、全角半角、トリムなどと絡むと、途端にバグの温床になります。

押さえておきたいポイントはこうです。

ToUpper() はカルチャ依存なので、比較や検索には ToUpperInvariant() を使う。
null 安全な ToUpperSafe のようなユーティリティを用意しておくと、呼び出し側が楽になる。
実務では「トリム」「全角→半角」「大文字変換」をセットで行う“正規化パイプライン”を作っておくと、コードが一気に安定する。
保存時に大文字にそろえるか、比較時だけそろえるかは設計の問題だが、「どこでやるか」を決めておくことが大事。

ここまで理解できれば、「とりあえず ToUpper しておく」段階から一歩進んで、
「カルチャや入力の揺れを意識したうえで、大文字変換を設計に組み込む」ことができるようになります。

タイトルとURLをコピーしました