「同じ会社のはずなのに、検索に引っかからない」「取引先マスタに同じ会社が 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(文字列) | 全角 → 半角 | ABC123 → ABC123 |
JIS(文字列) | 半角 → 全角 | ABC123 → ABC123 |
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.Narrow が ASC 相当、VbStrConv.Wide が JIS 相当、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 で数値に直してセルへ入れて初めて、SUM も SUMIF も並べ替えも噛み合います(金額を 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(全角→半角)/JIS・DBCS(半角→全角) - 自前の文字コード加算は 半角カナの濁点(
ド)で壊れる。JIS/ASCは対応表で正しく結合・分離する - 方針は「英数記号は半角、カナは全角」を全体で一貫適用するのが定番
- 入力時の自動正規化は
AfterCellEditでe.NewDataを書き換える。e.Cell.Columnで列ごとに寄せ方を変えられる - 全角数字は半角化しただけでは集計できない —
decimal.TryParseで数値に直してセルへ入れる - 検索・名寄せは 比較する両側を同じ関数で正規化してから突き合わせる
表記ゆれは「あとでクレンジングする」ものではありません。入力された瞬間に揃え、比較する前に揃える — この 2 か所さえ押さえれば、検索も名寄せも最初から噛み合います。
次に読むもの
- 数式と関数 —
JIS/ASC/DBCSなど組み込み関数の一覧 - セルの編集 —
AfterCellEditほか編集イベントの扱い - ReoGrid 4.5 リリース —
JIS/ASCを含む Excel 互換関数 47 種を追加 - 「CSV を Excel で開いたら 0 が消えた」を防ぐ — 文字コードと列の型を開発者が握る
- 金額・消費税・端数処理を正しく扱う — 数値を数値のまま持つ
