6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

異体字を安易に使用してはいけない

Posted at

##はじめに
前回の投稿で「異体字を簡単に入力する」方法を紹介しました。
しかし、異体字や旧字体の安易な使用は
 ・環境に依存する
 ・「異体字セレクタ」は発見や除去が難しい
 ・文字列処理でトラブルの原因になり得る

など、問題を起こす可能性も高いので使用の際は要注意です。

前回の投稿のカバーと注意喚起のため、自身が遭遇した実例を基に記事にまとめました。

##作業環境ほか
OS : Windows 10 Pro
Excel : Excel for Microsoft 365 MSO 32bit
PowerShell : Ver. 7.2.1

##Excelでの問題事例
###(1) LEN関数 および LENB関数での問題
Excelで異体字・旧字体を使用した場合に起きやすい「想定外(見た目と結果が異なる)」の事例です。
LEN関数やLENB関数は、それぞれ「文字数」や「バイト数(全角2バイト・半角1バイト)」を返すとの解説が一般的ですが・・・。
異体字2-例.png
【図1】

B,C列はA列に対するLEN関数およびLENB関数の結果です。
「想定外」の部分には赤枠をつけています。
なお、A列のフォントは「游明朝」、2文字目(田・藤)は「通常の文字」です。
もちろん「空白文字」の混入は無く、編集時もカーソルは2文字分しか移動しません。

Google Drive にデータ(異体字問題.xlsx)を置いておきます。
Googleスプレッドシートが開きますが、ダウンロードは可能です。
(ちなみに、GoogleスプレッドシートはLENB関数の結果が異なっています。)

さて、先頭文字が問題を起こしているので、CODE関数とUNICODE関数、念のため、VBAのLen関数とLenB関数の戻り値も確認してみます。(下図【図2(a)】
セルA4,A5は、見た目が同じなのに文字コードは異なります。また、VBAのLenB関数はワークシート関数のLENB関数と異なる結果を返しますが、本題から逸れますのでコメントは控えます。
異体字2-問題例1.png
【図2(a)】CODE関数は、JISコードが存在しない場合 0x3F('?') を返します。(一部例外あり)

数式は以下のとおりです。
異体字2-数式.png
【図2(b)】 ※ F,G列は、次のユーザー定義関数です。

Excel VBA
Function vba_len(c)
  vba_len = Len(c.Value)
End Function

Function vba_lenb(c)
  vba_lenb = LenB(c.Value)
End Function

先頭文字だけでは情報不足なので、セル内の全文字列をPowerShellで確認します。
セルA2:A14をデータをクリップボードにコピーして、次のワンライナーを実行します。

当記事に載せたPowerShellスクリプトの多くは、Windows PowerShell 5 で動作しません。
また、データの受け渡しにクリップボードを使用しますが、ExcelとPowerShellの相性問題(詳細は過去の投稿をご参照ください)から、PowerShell 7 の使用を前提とします。

【スクリプト1】(セル検査)

PowerShell 7
(gcb) -ne '' | %{($_.EnumerateRunes().Value | %{"U+{0:X4}" -f $_}) -join ' '} | scb

クリップボードの文字列をコードポイントで取得し、クリップボードに戻す単純なスクリプトです。String.EnumerateRunesメソッドは .NET Core 以降の機能です。

スクリプト実行後、セルF2でペーストしたものが下図【図3】です。
異体字2-文字コード.png
【図3】

セルA3,A4,A7には異体字セレクタが使われていることがわかります。
結局、異体字セレクタの有無にかかわらず、LEN関数およびLENB関数(VBAも含め)は「想定外」となる可能性があり、あてにできません。
特に人名に使われる異体字・旧字体は問題が発生する可能性が高くなります。

ちなみに、下図【図4】を見ると「UnicodeがU+FFFFに収まっているか?」、「JISコードが割り当てられているか?」の2条件からLEN関数およびLENB関数の結果は推測できそうです。
異体字2-LEN推察.png
【図4】

###(2) 印刷や表示が乱れる可能性がある。
下図【図5(a)】でB,C列はA列の値を参照し、配置やフォントを変更したものです。
一部のセルは影響を受けています。(赤枠部
異体字2-問題例2.png
【図5(a)】

このシートの印刷プレビュー画面【図5(b)】でも、異常が確認でき、実際の印刷結果にも不具合が生じます。特に、異体字セレクタとして「字形選択子」(U+FE0X)を使った場合には、表示や印刷、pdf化などに問題が生じます。(【図5(c)】【図5(d)】
このような問題は、原因の特定に苦労しそうです。
異体字2-印刷.png
【図5(b)】 印刷プレビュー
異体字2-pdf印刷.png
【図5(c)】 印刷でpdf化「Microsoft Print to PDF」
異体字2-pdf保存.png
【図5(d)】 保存でpdf化「名前をつけて保存 - ファイルの種類:PDF」

##対策について
上記の事例をベースにPowerShellを使った対応手段をいくつか示します。

###(1) 異体字セレクタの除去
例示するスクリプトは、人名データを想定して、漢字・ひらがな・カタカナ、長音記号「ー」・同音の繰り返し記号「ゝ」「ゞ」「々」や空白文字で構成されるデータを処理します。

Rune.Is~メソッドを使った異体字セレクタ除去例です。
Excelでコピーしたセルから、「文字」「空白文字(改行文字含む)」以外を除去してクリップボードに戻します。

【スクリプト2】(異体字セレクタの除去)

PowerShell 7
-join ((gcb -raw).EnumerateRunes() | ?{[Text.Rune]::IsLetter($_) -or [Text.Rune]::IsWhiteSpace($_)} | %{$_.ToString()}) | scb

【図1】のセルA2:A14のデータで処理してB列にペーストしました。
結果を確認するため、あらためてB列(B2:B14)をコピーし、先の【スクリプト1】でデータを検査します。(C列)
異体字2-IVS除去.png
【図3】と比較して、異体字セレクタが除去されています。

ちなみに、上記スクリプトは、㈱(U+3231)などの記号も除去します。
「㈱」を残したい場合は判断条件を追加します。
ついでに、「ゞ」は文字扱いであり、除去されないことも確認します。

PowerShell 7
# ㈱が除去される
-join ("いすゞ自動車㈱".EnumerateRunes() | ?{[Text.Rune]::IsLetter($_) -or [Text.Rune]::IsWhiteSpace($_)} | %{$_.ToString()})
# いすゞ自動車

# ㈱が除去されないようにする
-join ("いすゞ自動車㈱".EnumerateRunes() | ?{[Text.Rune]::IsLetter($_) -or [Text.Rune]::IsWhiteSpace($_) -or [Text.Rune]::IsSymbol($_)} | %{$_.ToString()})
# いすゞ自動車㈱

###(2) LEN関数の代替手段
LEN関数について、先の推測が正しいとすれば、ワークシート関数やVBAで「正しい」文字数を得ることは可能かと思いますが、個人的には気乗りしません。
関数にこだわらなければ、PowerShellで見た目どおりの「正しい」文字数を取得できます。もちろん、異体字セレクタの有無に関係なく機能します。

【スクリプト3】(見た目どおりの文字数を取得)

PowerShell 7
(gcb) -ne '' | %{[Globalization.StringInfo]::new($_).LengthInTextElements} | scb

【図1】のデータに手を加えて、LEN関数と上記スクリプトの結果(C列)を比較してみます。
異体字2-LEN対策.png
C列は見た目どおりの文字数を返しています。

###(3) Unicode正規化
異体字・旧字体の中でも「CJK互換漢字」「CJK互換漢字補助」の範囲に割り当てられた文字は、Unicode正規化が可能です。(「﨑」(U+FA11)などの例外あり)
異体字が原因で検索や並べ替えに不都合が生じるような場合、一考の余地はあります。
【図1】のセルA5の「梅」(U+FA44)、A9の「塚」(U+FA10)などが該当します。
「梅」(U+6885)を検索するとA2~A4は引っかかりますが、A5はスルーされます。

.NETではString.Normalizeメソッドを使ってUnicode正規化を実現できるので、PowerShellで確認してみます。

PowerShell 7
([int][char]"`u{fa44}".Normalize()).ToString("X")
# 6885
([int][char]"`u{fa10}".Normalize()).ToString("X")
# 585A

梅(U+FA44) は 梅(U+6885) に、塚(U+FA10) は 塚(U+585A) に正規化されています。
必要に応じて.Normalize()を追加するだけなので手間はかかりません。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?