「同じ会社のはずなのに、検索に引っかからない」「取引先マスタに同じ会社が 2 件ある」「電話番号で名寄せしたら 1 件もマッチしなかった」— 業務データを扱っていると、必ずどこかで全角・半角の表記ゆれにぶつかります。

人間の目には同じに見えても、コンピュータにとっては別の文字です。

  • ドコモ(半角カナ)と ドコモ(全角カナ)は別文字列
  • 03-1234(全角数字)と 03-1234(半角数字)は別文字列
  • (株)(半角括弧)と (株)(全角括弧)も別物
  • そして 123(全角数字)は、そのままでは 数値として合計できない

入力する人によって、IME の設定によって、コピー元の Excel や CSV によって、全角と半角が混ざります。これを「気をつけて入力してください」で解決しようとすると、必ず破綻します。直すべきは入力する人ではなく、受け取るアプリ側です。

この記事では ReoGrid を使い、Excel 互換の JIS / ASC 関数による一括変換と、AfterCellEdit イベントによる入力時の自動正規化を C# で組み立てます。WinForms / WPF どちらでも、Excel のインストールは不要です。


なぜ全角・半角は業務データを壊すのか

全角・半角の混在は、見た目の問題ではありません。データとして次の機能が静かに壊れます。

  • 検索が当たらないドコモ で検索しても、データが ドコモ だとヒットしない
  • 名寄せ・重複排除が効かない(株)山田商店(株)山田商店 が別レコード扱いになり、同じ取引先が二重登録される
  • 合計できない123(全角数字)は数値ではなく文字列なので、SUM が無視する
  • 並べ替えが直感に反する — 全角と半角の文字コードは離れているため、混在した列はソートしても期待どおりに並ばない

つまり「表記ゆれ」は、検索・集計・名寄せという業務データの基本機能を片っ端から無効化します。対策は 1 つ。**比較・集計する前に、全角か半角か、どちらかに寄せて統一する(正規化する)**ことです。


一括変換 — Excel と同じ JIS / ASC 関数

ReoGrid には、Excel と同名・同義の全角/半角変換関数が組み込まれています(v4.5 で追加)。数式に書くだけで、列まるごとを一発で変換できます。

関数変換
ASC(文字列)全角 → 半角ABC123ABC123
JIS(文字列)半角 → 全角ABC123ABC123
DBCS(文字列)半角 → 全角(JIS の別名)同上
using unvell.ReoGrid;

var sheet = grid.CurrentWorksheet;

// A 列に入力されたバラバラの表記を、B 列で半角に正規化する
sheet["A2"] = "03-1234-5678";   // 全角数字の電話番号
sheet["B2"] = "=ASC(A2)";                  // → 03-1234-5678(半角)

sheet["A3"] = "ドコモ ショップ";              // 半角カナ
sheet["B3"] = "=JIS(A3)";                  // → ドコモ ショップ(全角)

数式なので、A 列のデータが変われば B 列も自動で追従します。元データ(生の入力)を残したまま、正規化後の値を別列で持てるのが利点です。

半角カナの濁点は「自前変換」では壊れる

ここで自前の変換コードを書こうとすると、たいてい半角カナでつまずきます。半角の ド は、実は (濁点)の 2 文字です。これを全角にするときは (1 文字)に結合しなければなりません。

文字コードに一定値を足すだけの素朴な変換(char + 0xFEE0 のような実装)は、この濁点・半濁点の結合を扱えず、ト゛ のような壊れた文字列を生みます。JIS / ASC は JIS X 0208 の対応表に基づいて濁点を正しく結合・分離するので、ここを自分で実装する必要はありません。


どちらに寄せるか — 正規化の方針を決める

「全角に寄せる」「半角に寄せる」のどちらが正解かは、データの種類で決まります。日本の業務システムでの定番はこうです。

  • 英数字・記号は半角に寄せるASC)— 電話番号・郵便番号・金額・商品コード・メールアドレス。検索や数値計算の対象になるものは半角に統一する
  • カタカナは全角に寄せるJIS)— 半角カナ(ハンカク)は印刷・帳票・他システム連携で文字化けや桁ズレの原因になりやすいので、全角に統一するのが無難

「英数記号は半角、カナは全角」を 1 つの方針として決め、アプリ全体で一貫して適用するのが肝心です。列ごとにどちらへ寄せるかを決めておけば、後段の検索・集計・名寄せがすべて噛み合います。


入力時に自動で正規化する — AfterCellEdit

一括変換は「すでにあるデータ」を直す方法です。しかし本当に効くのは、入力された瞬間に正規化して、そもそも表記ゆれを溜めないことです。

ReoGrid では AfterCellEdit イベントで、ユーザーが編集を終えた値を確定前に書き換えられます。e.NewData に正規化後の値を入れるだけです。

using unvell.ReoGrid;
using unvell.ReoGrid.Events;
using Microsoft.VisualBasic;   // StrConv(WinForms / WPF なら追加参照は不要)

var sheet = grid.CurrentWorksheet;

sheet.AfterCellEdit += (s, e) =>
{
    if (e.NewData is string text && !string.IsNullOrEmpty(text))
    {
        // 入力されたテキストの英数記号を半角へ正規化してから確定する
        e.NewData = Strings.StrConv(text, VbStrConv.Narrow, 0x411);  // 全角 → 半角
    }
};

Strings.StrConv は ReoGrid の ASC / JIS と同じ変換を C# 側で行うための標準 API です(VbStrConv.NarrowASC 相当、VbStrConv.WideJIS 相当、0x411 は日本語ロケール)。WinForms / WPF は Windows 前提なので、追加のパッケージなしでそのまま使えます。

これで、ユーザーが 03-1234 と全角で打っても、セルを離れた瞬間に 03-1234 へ直ります。入力規則を押し付けるのではなく、受け取り側が黙って整える——これがいちばんストレスのない方法です。

列ごとに寄せ方を変える

実際の表では、列によって寄せ方を変えたいはずです。e.Cell の位置を見て分岐します。

sheet.AfterCellEdit += (s, e) =>
{
    if (e.NewData is not string text || string.IsNullOrEmpty(text)) return;

    switch (e.Cell.Column)
    {
        case 1:   // B 列 = フリガナ → 全角カナに寄せる
            e.NewData = Strings.StrConv(text, VbStrConv.Wide, 0x411);
            break;

        case 2:   // C 列 = 電話番号 → 半角に寄せる
            e.NewData = Strings.StrConv(text, VbStrConv.Narrow, 0x411);
            break;
    }
};

全角数字を「数値」に戻して集計を生かす

全角数字には、表記ゆれ以上に厄介な落とし穴があります。123数値として認識されないので合計に入りません=SUM(...) が黙って 0 を返したり、桁がずれたりする原因の多くがこれです。

入力時に半角化したうえで、数値に変換できるものは数値として格納すれば、合計も並べ替えも生きます。

sheet.AfterCellEdit += (s, e) =>
{
    if (e.NewData is not string text || string.IsNullOrEmpty(text)) return;

    // まず半角へ正規化
    string half = Strings.StrConv(text, VbStrConv.Narrow, 0x411);

    // 数値として読めるなら、数値そのものをセルに入れる(文字列のままにしない)
    if (decimal.TryParse(half, out var number))
    {
        e.NewData = number;   // → SUM も並べ替えも効く
    }
    else
    {
        e.NewData = half;     // 数値でなければ半角化した文字列として確定
    }
};

ポイントは、全角数字を半角の「文字列」にして満足しないことです。"123" のままでは依然として合計できません。decimal.TryParse で数値に直してセルへ入れて初めて、SUMSUMIF も並べ替えも噛み合います(金額を double ではなく decimal で扱う理由は 通貨書式・消費税・端数処理の記事 で詳しく扱っています)。


既存データを一括クリーニングする

取り込み済みの CSV や Excel、あるいは過去に溜まったマスタを、まとめて正規化したい場面もあります。数式を経由せず、セルの値を直接書き換えるなら、範囲を走査して StrConv を当てます。

using Microsoft.VisualBasic;

var sheet = grid.CurrentWorksheet;

// C 列(電話番号)の 2〜1000 行目を半角に統一する
for (int row = 1; row < 1000; row++)
{
    if (sheet.GetCell(row, 2)?.Data is string text && !string.IsNullOrEmpty(text))
    {
        sheet.SetCellData(row, 2, Strings.StrConv(text, VbStrConv.Narrow, 0x411));
    }
}

数式で一旦変換して、その結果を値として貼り戻す(=ASC(A2) を計算 → 値を確定)方法もありますが、列まるごとを恒久的に直すなら、上のように値を直接書き換えるほうが後腐れがありません。

検索・名寄せの前に「同じ土俵」へ揃える

正規化のいちばんの目的は、検索と名寄せです。鉄則は、比較する両側を同じ関数で正規化してから突き合わせることです。

// 検索キーもデータ側も、同じ正規化を通してから比較する
static string Normalize(string s) =>
    Strings.StrConv(s ?? "", VbStrConv.Narrow, 0x411).Trim();

bool isMatch = Normalize(cellValue) == Normalize(searchKeyword);

データだけ、あるいは検索キーだけを正規化しても意味がありません。両側を同じ土俵に乗せて初めてドコモ で打っても ドコモ のレコードが見つかります。


フリガナ(PHONETIC)についての注意

ReoGrid には PHONETIC 関数もありますが、これは Excel の PHONETIC とは挙動が異なります。Excel の PHONETIC は「入力時の読み仮名(IME の変換履歴)」を保持して返しますが、ReoGrid にはその入力履歴の仕組みがないため、セル内のカナ部分を抽出する簡易版として動作します(ひらがなはカタカナへ変換)。

漢字氏名から正確なフリガナを自動生成する用途には使えません。フリガナは別列でユーザーに入力・確定させ、その列に対して JIS(全角カナ正規化)をかける、という運用が現実的です。


まとめ

  • 全角・半角の混在は見た目の問題ではなく、検索・名寄せ・合計・並べ替えを片っ端から壊す
  • 対策は「入力者に気をつけさせる」ではなく「受け取るアプリ側で正規化する
  • 一括変換は Excel 互換の数式で — ASC(全角→半角)/ JISDBCS(半角→全角)
  • 自前の文字コード加算は 半角カナの濁点(ド)で壊れるJIS / ASC は対応表で正しく結合・分離する
  • 方針は「英数記号は半角、カナは全角」を全体で一貫適用するのが定番
  • 入力時の自動正規化は AfterCellEdite.NewData を書き換える。e.Cell.Column で列ごとに寄せ方を変えられる
  • 全角数字は半角化しただけでは集計できないdecimal.TryParse で数値に直してセルへ入れる
  • 検索・名寄せは 比較する両側を同じ関数で正規化してから突き合わせる

表記ゆれは「あとでクレンジングする」ものではありません。入力された瞬間に揃え、比較する前に揃える — この 2 か所さえ押さえれば、検索も名寄せも最初から噛み合います。


次に読むもの