GoでのShift_JISファイル作成時に生じた問題点と解決策

はじめに

はじめまして、ALTURA X株式会社でバックエンドエンジニアを担当している山中です。
本記事では弊社のシステム開発においてGoでのShift_JISファイル作成処理構築時に生じた問題とその解決策ついて説明していきます。
まず、一般的によく使われる文字コードであるShift_JISとUTF-8およびGoでのShift_JISファイル作成方法について説明し、その後に開発時の問題と解決策について述べさせていただきます。

Shift_JISとUTF-8について

Shift_JISとUTF-8はいずれも文字コードの一つで、それぞれ以下のような違いがあります。
  • Shift_JIS
    • 日本語をコンピュータで表現するために日本国内で広く使用されている文字コードの一つです。1980年代に日本で広く普及し、特にWindows環境で多く使用されています。ASCII文字は1バイト、日本語の文字(ひらがな、カタカナ、漢字など)は2バイトで表現されます。プログラミング等で正しく扱うためには専用のライブラリやエンコード設定が必要になります。
  • UTF-8
    • Unicodeをベースとした文字コードで世界中のほぼすべての文字を表現でき、インターネットや多くのプラットフォームで標準的に使用されています。ASCII文字は1バイト、非ASCII文字(日本語の漢字や特殊文字など)は2〜4バイトで表現されます。ほとんどのプログラミング言語やシステムでデフォルトでサポートされているため、特別な設定なしで利用できることが多いです。

GoでのShift_JISファイル作成

Goでは一般的に準標準ライブラリのパッケージとパッケージを使用して、Shift_JISファイルを作成します。このパッケージを利用することで、UTF-8からShift-JISへの変換を簡単に行うことができます。
以下は、Shift_JISでファイルを書き込む一般的な方法のサンプルコードです。

全角文字変換

問題点

弊社のシステムではShift_JISファイルを作成する際、出力するファイルのフォーマットが定められており、項目によっては全角文字固定で出力するように指定されているケースがありました。一方で、出力対象となるデータは半角文字と全角文字が混在するケースが存在するため、全角文字への変換処理が必要となりました。その際、準標準ライブラリのパッケージを使用して全角文字変換を行うことにしました。
以下が、上記のShift_JISのサンプルコードに全角文字変換を加えたコードとなります。
上記を実行した際に出力されるShift_JISファイルの中身は以下のようにすべて全角文字となります。
 
ところが、上記コードでは正しくファイルが作成されないケースが存在しました。それは「ガ」のように半角カタカナに濁点・半濁点があるケースです。これは「ガ」のような半角カタカナが 実際には2つの文字(「カ 」と 「゙」)で構成されているためです。これを全角変換すると各文字が全角変換され、その状態でShift_JIS変換をおこなうと変換に失敗してしまいます。ちなみに、Shift_JIS変換せずにUTF-8のまま出力すると、UTF-8の文字合成により表示上は1文字で表示されます(ただし、文字コード上は「カ」と濁点の文字コードとなり、「ガ」の文字コードとは異なります)。
上記のコードで、 に変更した際に出力されるShift_JISファイルの中身は以下となり、「カ」以降が出力されなくなります。

解決策

上記問題点を解決するために、準標準ライブラリのパッケージを使用することにしました。具体的には、全角変換後の文字に対して メソッドを使うことで濁点や半濁点を合成し1文字にすることが可能になります。
以下、上記コードの変更箇所を抜粋。
上記を実行した際に出力されるShift_JISファイルの中身は以下の通り、濁点の半角カタカナも正しく出力されています。

全角・半角混在文字列のバイト数制限

問題点

出力するファイルはファイルが固定長となっており、各項目にバイト数が決められていました。そのため、出力対象のデータが指定されたバイト数を超える文字列の場合、先頭から指定バイト数分だけ切り出して設定するように処理する必要がありました。 Shift_JISの場合、半角文字は1byte、全角文字は2byteのため半角のみや全角のみ(バイト数は偶数)の場合は、単純に先頭から指定バイト数分を切り出すことで正しく設定することが可能となります。ただし、半角文字と全角文字が混在する場合は、全角文字が2byteであるため、例えば100byteのバイト数制限がある場合に、出力対象のデータの99byte目に全角文字があるようなケースでは、単純に先頭から100byteを切り出してしまうと最後が全角の先頭1byteだけを設定することになり、最後の文字が文字化けしてしまいました。

解決策

上記問題点を解決するため、指定バイト数だけ切り出したあとに最後のバイトが全角文字の先頭1byteかどうかを判定し、該当する場合はその1byteのかわりに半角空白(1byte)を設定することにしました。 Shift_JISでは、半角文字・全角文字の1byte目・全角文字の2byte目はそれぞれ設定されるバイト列の範囲が以下の通りとなっています。
  • 半角文字(制御文字含む):0x00〜0x7F, 0xA1~0xDF
  • 全角文字の1byte目(Shift_JIS-2004):0x81~0x9F、 0xE0~0xFC
  • 全角文字の2byte目(Shift_JIS-2004):0x40~0x7E、0x80~0xFC
上記の通り、全角文字の1byte目と全角文字の2byte目では設定されるバイト列に重複があるため、最後の1byteだけを見て全角文字の1byte目であるかどうかを判定することはできません。一方で、全角文字の1byte目と半角文字は設定されるバイト列に重複がありません。そのため、設定する文字列の先頭から順番に半角文字が全角文字かを判定していくことで、最後のバイトが全角文字の1byte目であるかを判定することができるようになりました。
以下、最後のバイトが全角文字の先頭1バイトであるかの判定処理のサンプルコードとなります。

最後に

本記事はGoでShift_JISファイルを作成する際に直面した問題点とその解決策について詳述しました。本記事が、GoでShift_JISファイルを扱う際の参考となり、皆様の開発の一助となれば幸いです。