現在作業中のプロジェクトにて、テキストファイルをシステムにインポートする機能を作成・改修しているのですが、インポート可能なテキストファイルの文字コードが制限されているのが、ユーザとして非常に不便に感じたため、テキストファイルの文字コードをユーザが気にせずに利用できるように改良してみました。
経緯
このシステムでは、地図上に表示する地物情報を管理画面からインポートすることができます。これまではポイントデータのみを扱っていた為、インポートされるデータのフォーマットはCSVのみでした。↓のような形式のデータです。
ID,LONGITUDE,LATITUDE,VALUE,MEMO
1,134.8212,36.0299,0.54691,"サンプルデータ"
2,134.1077,36.3115,0.67319,"サンプルデータ"
CSVデータはExcel上で作成されることを想定していたため、このシステムで扱うテキストファイルの文字コードはshift-jisのみに対応していました。(※Excelで表をCSV形式で保存すると、文字コードはデフォルトでshift-jisとなる)
しかし、今回の改修にて、ラインやポリゴンの地物にも対応することになり、CSVでは表現しきれなくなったため、インポートされるデータフォーマットにGeoJSON形式が追加されました。↓のような形式のデータです。
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"geometry":{
"type":"Polygon",
"coordinates":[[
[137.393396,34.762134],[137.392828,34.761847],[137.392339,34.761625],
[137.392203,34.761569],[137.392123,34.761551],[137.393396,34.762134]
]]
},
"properties":{
"id":1,
"memo":"サンプルデータ"
}
},
...
]
}
GeoJSONはJSONに準拠していますので、JSONの仕様書を確認してみたところ、”文字エンコーディングはUTF-8でなければならない”と書かれていますね。正直なところ、これまで業務でJSONデータを扱う上で文字コードはあまり意識してこなかったのですが、仕様に従うのであれば、このシステムでインポートするGeoJSONファイルも、UTF-8で用意されるべきです。
しかし実際にユーザがGeoJSONデータを作る際に文字コードを意識して準備してくれるかというと、そうではありません。であれば、どの文字コードでインポートデータが用意されても読み込まれるように対応しておくのが無難ですね。
.NETにおける文字コード判別
C#でテキストファイルを読み込む際は、以下のように文字コードを指定します。ここが異なると文字化けします。
string text = string.Empty;
using (var stream = file.OpenReadStream())
using (var reader = new StreamReader(stream, Encoding.GetEncoding("Shift_JIS")))
{
text = reader.ReadToEnd();
}
つまり、ファイルの中身を読み込む前に文字コードを判別してやる必要があるのですが…その判別処理をフルスクラッチで作るのはなかなか面倒です。実装を解説して下さっているサイトを見て勉強してみましたが、BOMを使って判定したり、テキストデータのバイト配列の一部を読んで判定したりと、いざ実装するとなると結構なコード量になります。これは素直にライブラリを使う事にしましょう。
今回は.NETの文字コード判定ライブラリとして以下をを使用しました。
UTF-unkwon
https://github.com/CharsetDetector/UTF-unknown
NuGetでのダウンロード数がそこそこ多いのと、継続的にアップデートが行われている点が採用の理由です。このライブラリをインストールすると、以下の様に実装することで、テキストデータの文字コードを取得することができます。
using UtfUnknown;
...
string text = string.Empty;
using (var stream = file.OpenReadStream())
{
var charsetDetectedResult = CharsetDetector.DetectFromStream(stream);
stream.Position = 0;
using (var reader = new StreamReader(stream, charsetDetectedResult.Detected.Encoding))
{
text = reader.ReadToEnd();
}
}
簡単に使えますね。試しにUTF-8, UTF-16, shift-jis, euc-jpなどのテキストデータを用意して読み込んでみましたが、全て正しく読み込めました。これはなかなか便利です。
記事が面白かった方、参考になった方は、是非「イイね」お願いします!