● EUCコードをS-JISコードに変換する ●

多分かなり変わった方法を使用していると思う。

EUC は UNIX で使用されている文字コードだよね。ひらがな、カタカナ(全角・半角)、漢字、全角アルファベットは2バイト、半角文字は1バイトという規格になっているみたい。半角の数字やアルファベットは EUC も S-JIS も同じ。従ってこの部分は考慮する必要が無いね。問題はそれ以外の文字コードだね。上位バイトが &H7F より大きければ2バイト文字と見なしてよい。ここより2バイト文字について考えていくからそのつもりで。それでは、EUCとS-JISの文字コードを簡単に比較してみよう。まず、1バイト目を見ると、次のようになっている。

EUC
S-JIS

EUC
S-JIS
&HA1
&H81
&HDF
&HE0
&HA2
&H81
&HE0
&HE0
&HA3
&H82
&HE1
&HE1
&HA4
&H82
&HE2
&HE1
&HA5
&H83
&HE3
&HE2
&HA6
&H83
&HE4
&HE2
  省略  
  省略  
  省略  
  省略  
&HDD
&H9F
&HFD
&HEF
&HDE
&H9F
&HFE
&HEF

  EUCコード &HDE と &HDF を境にして、S-JISコードが &H9F から &HE0 に数が飛んでいる所に注意してね。

  見ての通り、EUCコード&HA1、&HA2 に対応するS-JISコードは&H81。同様に、EUCコード&HA3、&HA4 に対応するS-JISコードは&H82。まずはプログラムでこれをどう認識させるか考えてみよう。ここでは Windows上で動くかすこと考慮しているということが重要。即ち EUC 文字列は、Windows上では結局 S-JIS として保持されるんだよね。従って、EUC と S-JIS の文字コードの差分を求めて、Windows上のS-JIS(本当はEUC)コードから引いてあげればよい。さて、さて…。EUC増分2に対し、S-JISの増分は1でこれは不変、というところに目を付けよう。これは数列で処理できそうだね、しかも等差数列、簡単や。EUCコードを基準にして、上位バイトが偶数か奇数かを見る。奇数であれば、(EUC, S-JIS) (&HA1, &H81), (&HA3, &H82), (&HA5, &H83) … というようになっていく。さて、これを数式で表すと、

    S-JIS上位バイト = 0.5(EUC上位バイト - &HA0) + 128.5

  途中計算・公式の説明は割愛するよ。この要領で上位バイト処理を処理する数式を一気に書いちゃおう。

  ◎上位バイトが奇数 の時

      ・上位バイト < &HDF の時
          S-JIS上位バイト = 0.5(EUC上位バイト - &HA0) + 128.5

      ・上位バイト ≧ &HDF の時
          S-JIS上位バイト = 0.5(EUC上位バイト - &HDE) + 223.5

  
  ◎上位バイトが偶数 の時

      ・上位バイト < &HDF の時
          S-JIS上位バイト = 0.5(EUC上位バイト - &HA0) + 128

      ・上位バイト > &HDF の時 (上位バイト = &HDF はありえない)
          S-JIS上位バイト = 0.5(EUC上位バイト - &HDE) + 223

  これでよろしい。何がなんだか分からない方は読み飛ばしちゃっても大丈夫です。用は動けばいいんだから。最後に半角カナについて触れておくね。半角カナは、上位バイトが &H8E のとき、下位バイトが半角カナのコードとなるんだよね。以上が上位バイトの変換方法だよ。さて次は、下位バイトや。下位バイトは、上位バイトが偶数か奇数かで処理が異なる。簡単だけど、文字コードを載せておくね。

上位バイトが奇数

上位バイトが偶数
    EUC         S-JIS         EUC         S-JIS    
&HA1
&H40
&HA1
&H9F
&HA2
&H41
&HA2
&HA0
&HA3
&H42
&HA3
&HA1
省略
省略
省略
省略
&HDF
&H7E
&HDF
&HDD
&HE0
&H80
&HE0
&HDE
&HE1
&H81
&HE1
&HDF
省略
省略
省略
省略
&HFD
&H9D
&HFD
&HFB
&HFE
&H9E
&HFE
&HFC

  ここでも注意する部分がある。なんと、上位バイトが奇数の場合、下位バイトは &H7F が存在しないのである。よう分からない規格だね。本当にこの点は注意!! んじゃぁ、簡単にまとめてみよう。

  ◎上位バイトが奇数 の時

          S-JIS下位バイト = EUC下位バイト - &H61

          この時、S-JIS下位バイト > &H7E であれば、
                S-JIS下位バイトをインクリメント(1足す)する。

  ◎上位バイトが偶数 の時

          S-JIS下位バイト = EUC下位バイト - &H2

  あと、よく分かんないんだけれど、改行コードについてもう1つ。EUCではキャッジリターンが無くて、ラインフィードがあるケースがあるみたい。これについても対応しといたからね。いらないと思ったら取っちゃいましょう。

  はあはあ、疲れた。でもまだコーディングが残ってるね。じゃあ、早速といいたい所だけどコーディングしていたら不都合な局面に遭遇したんだよね。文字列変数に EUC 文字列を格納させ、関数を作ったんだけど、バイトデータが変更されてしまっている!!という状況に陥った。SJISの何でこのようなことが言えるのかというと、ファイルから読み込んだらばっちり変換されたからだよ。

  例えばこの文字列、"・キ・ケ・ニ・・"。SJISでは「システム"」という文字列だよ。ファイルから読みこむと、

   上位    下位   変換前    変換後
    A5      B7      シ        シ
    A5      B9      ス        ス
    A5      C6      テ        テ
    A5      E0      ム        ム
    22       0      "         "

  文字列変数に保持させて変換すると、

   上位    下位   変換前    変換後
    A5      B7      シ        シ
    A5      B9      ス        ス
    A5      C6      テ        テ
    A5      81      ム        ・
    45       0      "         ?(壊れた文字)

  となってしまう。「システ」までは正しく変換できるんだけど「ム」の下位バイトと「"」の上位バイトが変わってしまってるんだよね。Unicode がいけないのかと思い、Cでコーディングしたんだけど結果は同じだった。いや、わからん、わからん。

  私のあらゆる知識(まだほんの少しだけど)をつぎ込んで正しい文字コードを取得しようとしたんだけど駄目だった。しょぼん。このようなことがあり、結局、ファイルから読み込むという方法で変換するようにしてしまった。トホホ。それでは、コードを載せましましょ。

  
'-----------------------------------------------------------
' 関数名: EucToSJis
' 機能 : EUCコードをS-JISコードに変換する
' 引数 : (in) srcFileName … ファイル名(変換前)
'          (in) dstFileName … ファイル名(変換後)
' 返り値: なし
'-----------------------------------------------------------
Public Sub EucToSJis(ByVal srcFileName As String, ByVal dstFileName As String)

    Dim srcBinLen As String '変換前文字列バイト数
    Dim gHighASC As Byte 'Unicode文字1バイト目の値(上位)
    Dim gLowASC As Byte 'Unicode文字2バイト目の値(下位)
    Dim i As Long '作業用
    Dim PreBinBuff() As Byte '変換前のバイトデータ
    Dim BinBuffMain() As Byte '変換後のバイトデータ
    Dim BinIndex As Long 'BinBuffMain のインデックス
    Dim FileNum As Integer 'ファイルナンバー

    On Error GoTo ErrHandler

    'ファイルナンバー取得
    FileNum = FreeFile

    'ファイル読み込み
    Open srcFileName For Binary Access Read As #FileNum
        srcBinLen = LOF(FileNum) 'ファイルサイズ取得
        ReDim Preserve PreBinBuff(srcBinLen + 1) As Byte
        ReDim Preserve BinBuffMain(srcBinLen * 2) As Byte
        Get #FileNum, , PreBinBuff
    Close #FileNum

    '1バイト分余分にループ
    For i = 0 To srcBinLen

        'アスキーコードを取得
        gHighASC = PreBinBuff(i) '上位
        gLowASC = PreBinBuff(i + 1) '下位

        '2バイト文字である
        If gHighASC > &H7F Then
            '半角カナである
            If gHighASC = &H8E Then
                BinBuffMain(BinIndex) = gLowASC

                BinIndex = BinIndex + 1
            'ひらがな・漢字
            Else
                '1バイト目変換
                '偶数である
                If (gHighASC Mod 2) = 0 Then
                    Select Case gHighASC
                        Case Is < &HDF
                            BinBuffMain(BinIndex) = 0.5 * (gHighASC - &HA0) + 128
                            BinBuffMain(BinIndex) = 0.5 * gHighASC + 48
                        Case &HE0 To &HFE
                            BinBuffMain(BinIndex) = 0.5 * (gHighASC - &HDE) + 223
                            BinBuffMain(BinIndex) = 0.5 * gHighASC + 112
                      'Case Else
                          'ありえない
                    End Select
                '奇数である
                Else
                    Select Case gHighASC
                        Case Is < &HDF
                            BinBuffMain(BinIndex) = 0.5 * (gHighASC - &HA0) + 128.5
                            BinBuffMain(BinIndex) = 0.5 * gHighASC + 48.5
                        Case &HDF To &HFD
                          BinBuffMain(BinIndex) = 0.5 * (gHighASC - &HDE) + 223.5
                            BinBuffMain(BinIndex) = 0.5 * gHighASC + 112.5
                        'Case Else
                            'ありえない
                    End Select
                End If

                '2バイト目変換
                '1バイト目が偶数である
                If (gHighASC Mod 2) = 0 Then
                    BinBuffMain(BinIndex + 1) = gLowASC - &H2
                '1バイト目が奇数である
                Else
                    BinBuffMain(BinIndex + 1) = gLowASC - &H61

                    If BinBuffMain(BinIndex + 1) > &H7E Then
                        BinBuffMain(BinIndex + 1) = BinBuffMain(BinIndex + 1) + 1
                    End If
                End If

                BinIndex = BinIndex + 2
            End If

            '1バイト分進める
            i = i + 1
        '半角文字である
        Else
            '上位バイトがキャリッジリターン
            '下位バイトがラインフィードである
            If gHighASC = &HD And gLowASC = &HA Then
                BinBuffMain(BinIndex) = &HD
                BinBuffMain(BinIndex + 1) = &HA
                BinIndex = BinIndex + 2
                i = i + 1
            '上位バイトがラインフィードである
            ElseIf gHighASC = &HA Then
                BinBuffMain(BinIndex) = &HD
                BinBuffMain(BinIndex + 1) = &HA
                BinIndex = BinIndex + 2
            'ラインフィードでない半角文字である
            Else
                '上位バイトがキャリッジリターンである
                If gHighASC = &HD Then
                    BinBuffMain(BinIndex) = &HD
                    BinBuffMain(BinIndex + 1) = &HA
                    BinIndex = BinIndex + 2
                Else
                    BinBuffMain(BinIndex) = gHighASC
                    BinIndex = BinIndex + 1
                End If
            End If

            '下位バイトがラインフィードである
            If gLowASC = &HA Then
                '上位バイトがキャリッジリターンでない
                If gHighASC <> &HD Then
                    BinBuffMain(BinIndex) = &HD
                    BinBuffMain(BinIndex + 1) = &HA
                    BinIndex = BinIndex + 2
                    i = i + 1
                End If
            End If
        End If

    Next i

    'メモリー確保
    ReDim Preserve BinBuffMain(BinIndex - 2) As Byte

    'ファイルナンバー取得
    FileNum = FreeFile

    'ファイル書き込み
    Open dstFileName For Binary Access Write As #FileNum
        Put #FileNum, 1, BinBuffMain()
        'Beep
    Close #FileNum

    Exit Sub

ErrHandler:
    Call MsgBox("エラーNo." & Err.Number & vbCrLf & vbCrLf & _
                Err.Description, vbExclamation, "EUC -> S-JIS変換 - エラー")

End Sub

こんな感じでよろしいかな。

なお、コード上の数式

    BinBuffMain(BinIndex) = 0.5 * (gHighASC - &HA0) + 128

は、この先を計算して

    BinBuffMain(BinIndex) = 0.5 * gHighASC + 48

とした方が処理としては速いよ。ここでのコードは上で挙げた数式に従って書いただけなんで、実際、使用するときは直してあげた方がいいと思うよ。


戻る