2008.06.12

[ASP.NET] Shift-JISのページにおけるJIS2004対応の注意点

WindowsVistaのJIS2004対応によって、
今までは使用できなかった文字が追加されました。
これらの文字について、システム上問題がないように、
何らかの対応をされていると思います。

今回はASP.NETでShift-JIS指定の場合における、
JIS2004対応の注意点について述べたいと思います。

JIS2004によって、追加された文字は
全部で907文字ですが、そのうち304字が、サロゲートペアと呼ばれる特殊な文字です。
従来、UniCodeでは2バイトの文字コードだったのが、
サロゲートペアでは4バイトになります。

既存システムで、これらの文字を入力されると、
文字化け、項目長エラーが予想されるので、
チェックしてはじく、という方法が、行われていると思います。

サロゲートペアをチェックする方法として、
もっとも簡単なのは、以下のように、1文字ずつチェックすることです。

'サロゲートペアチェック
Private Function ChkSurrogatePair(ByVal vsVal As String) As Boolean 

For Each c1 As Char In vsVal.ToCharArray()
If Char.IsSurrogate(c1) Then
Return False
End If
Next

Return True

End Function

(※このチェックでは、あくまでサロゲートペアの検出しかできません。
  例えば、森鴎外の「オウ」の字など、
  JIS2004で追加された文字であるが、サロゲートペアでない文字に関しては検出できません。
  文字コードによる範囲チェック等、別のメソッドを作成する必要がありますが、
  サンプルは省略します。)

サロゲートペアとは、4バイトのUnicodeの文字なので、
画面の文字コードがUTF-8だった場合は問題はないのですが、
Shift-JISを指定した場合には、問題が生じます。

サロゲートペアに限らず、Shift-JISに存在しないUnicode文字であると、
鷗のように、数値文字参照でサーバー側は受け取ります。
よって、単に受け取った文字を先程の関数でチェックするわけにはいかないことになります。

対応方法としては、以下の3種類になります。

 ① Shift-JISから、UTF-8に変更する。
 ② 数値文字参照であれば、すべてエラーにしてしまう。
 ③ 数値文字参照から、元の文字を復元し、それからチェックする。

①のSift-JISからの変更は、抜本的な解決策なのですが、
影響範囲が大きく、工数がかかります。
また、何らかの事情でShift-JISを指定しているのであれば、
変更が許されるかどうか検討が必要となります。

②の数値文字参照であればエラー、というのは
仕様として満たされるのであれば、問題ないと思います。、
もちろん、このチェックだけでは、文字コードを「UTF-8」にした場合に
無効になってしまうので、汎用的なロジックにするのであれば、
他のチェックと組み合わせる必要があります。

③の数値文字参照から元に戻す方法は、
処理の流れとしては、とてもわかりやすくなります。
ただ、この際に注意しなければならないのは、
数値文字参照から元の文字に戻す方法です。

一般的に、.NETでは、数値文字参照から元の文字に戻すには、
HttpUtility.HtmlDecode メソッドを使用します。
しかし、サロゲートペアの文字は、HtmlDecodeでは元に戻りません。
&#10131083のように、65536以上(&HFFFFより大きい)の文字コードで渡ってくるため、
文字化けをしてしまいます。

引き渡された文字を、上位サロゲート・下位サロゲートに分離してから、
HtmlDecodeを呼び出すと、元に戻ります。
サンプルコードを以下に示します。

    'サロゲートペアであれば変換する
    '(引数には文字コードを渡す)
    Public Shared Function ConvertSurrogatePair(ByVal viCode As Integer) As String
        Dim sRet As String = ""

'16進数で5文字以上であればサロゲートペアから変換した文字を返す
If viCode >= &H10000 Then
viCode -= &H10000
Dim a As Integer = CType(Math.Floor(viCode / &H400), Integer) '//Math.floor()で整数値に変換
a += &HD800
Dim b As Integer = viCode Mod &H400
b += &HDC00
'HtmlDecodeを呼び出し
sRet = HttpUtility.HtmlDecode("&#" & a & ";&#" & b & ";")
End If

Return sRet

End Function

※変換規則については、以下のページを参考にしました。
 CodeZine サロゲートペア入門
 

なお、今回はサーバ側での処理について
述べましたが、本来的にはJavaScriptで
画面上でチェックして、念のためサーバー側でもチェックする、
というのが正しい実装だと思います。
(もちろん、JavaScriptだけではダメですが)

以上です。特に、HtmlDecodeで元に戻らない点は気づきにくいので、ご注意ください。

コメントを投稿

(いままで、ここでコメントしたことがないときは、コメントを表示する前にこのブログのオーナーの承認が必要になることがあります。承認されるまではコメントは表示されません。そのときはしばらく待ってください。)

photo
takefuji