どちらかというと、つぶやきとか備忘録に近いので、ここに書いておきます。
テキストファイルのエンコード判定を、
ええ感じに(ラクに、しかし、それなりな精度で)、実現してみたいと思い、
数ヶ月ほど模索していた次第です。
結局、自力で実装するのは早々にあきらめて、使えそうなライブラリを探す旅をしていました。
で、IMultiLanguageとかいうやつが敷居が低そうだったので使ってみました。
(COMだかIEの機能だか、そういうやつだそうです。まぁ、COMとか知らんのですが・・・)
↓実行して表示されるウィンドウにファイルをドロップするとあら不思議、エンコード形式が表示される(素敵度☆☆)
► スポイラーを表示
#include
#include
#include // ロケール設定用
#include // IMultiLanguage用
#include // DXライブラリ
using namespace std ;
// ソフト名
const string SOFT_NAME = "IMultiLanguageテスト(ファイルドロップでエンコードを判定します)" ;
// 画面サイズ
const int WIN_SIZE_W = 640 ;
const int WIN_SIZE_H = 480 ;
// DetectEncodingInfoの候補数
const int DETECT_ENC_NUM = 10 ;
// 処理本体:ファイルを読み込んでIMultiLanguageでエンコード判定する
//
// 引数1(I):ファイルパス
// 引数2(O):コードページとスコアのペアを配列で返す
// 戻り値(O):S_OKは判定成功, S_FALSEは判定不能, E_FAILはエラー
//
// 参考元1:http://yuzublo.blog48.fc2.com/blog-entry-29.html
// 参考元2:http://yuzuhi.web.fc2.com/tips/textenc-sample1.html
// 参考元3:http://ztms.blog.fc2.com/blog-entry-33.html
// 参考元4:http://ofuton.tk/93/memo/codepage.php
// 参考元5:http://edn.embarcadero.com/print/images/39348/b4.pdf
HRESULT MainProc( const string& filePath, vector>& out_val )
{
// 戻り値クリア
out_val.clear() ;
//++++++++++++++++++++++++++++
// ファイルロード
//++++++++++++++++++++++++++++
HANDLE hfile = CreateFile(
filePath.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL) ;
DWORD fsize = GetFileSize( hfile, NULL ) ;
BYTE* buf = (BYTE*)LocalAlloc( LMEM_FIXED, fsize );
ReadFile( hfile, buf, fsize, &fsize, NULL ) ;
CloseHandle( hfile );
//++++++++++++++++++++++++++++
// IMultiLanguageを生成
//++++++++++++++++++++++++++++
IMultiLanguage3* mlang = NULL ;
if( FAILED( CoCreateInstance(
CLSID_CMultiLanguage,
NULL,
CLSCTX_INPROC_SERVER,
IID_IMultiLanguage3,
( void** )& mlang )
) )
{
// 失敗したらここで終了
delete mlang ;
return E_FAIL ; // エラーを返す
}
//++++++++++++++++++++++++++++
// エンコード判定
//++++++++++++++++++++++++++++
int srcSize = fsize ;
int detectEncNum = DETECT_ENC_NUM ; // 取得する候補の数
DetectEncodingInfo *encoding = new DetectEncodingInfo[detectEncNum] ;
HRESULT result = mlang->DetectInputCodepage(
MLDETECTCP_NONE, 0,
(char*)buf, &srcSize,
encoding, &detectEncNum ) ;
//++++++++++++++++++++++++++++
// 判定失敗したときの処理
//++++++++++++++++++++++++++++
if( FAILED( result ) ) // E_FAILならこの式が真になる
{
// 解放して処理終了
delete[] encoding ;
mlang->Release() ;
// 結果を返す
return result ; // S_OKは判定成功, S_FALSEは判定不能, E_FAILはエラー
}
//++++++++++++++++++++++++++++
// 結果を戻り値に設定
//++++++++++++++++++++++++++++
for( int i=0; iRelease() ;
// 結果を返す
return result ; // S_OKは判定成功, S_FALSEは判定不能, E_FAILはエラー
}
// メイン関数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
#if defined( DEBUG ) | defined( _DEBUG )
// メモリリーク検出
_CrtSetDbgFlag( _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF ) ;
#endif
//++++++++++++++++++++++++++++
// デフォルトロケール設定
//++++++++++++++++++++++++++++
setlocale( LC_ALL, "Japanese_Japan.932" ) ;
//++++++++++++++++++++++++++++
// DXライブラリ設定
//++++++++++++++++++++++++++++
// ログ出力をしない
DxLib::SetOutApplicationLogValidFlag( FALSE ) ;
// ソフトタイトルを設定
DxLib::SetMainWindowText( SOFT_NAME.c_str() ) ;
DxLib::SetMainWindowClassName( SOFT_NAME.c_str() ) ;
// 標準文字セットを日本語にする
DxLib::SetUseCharSet( DX_CHARSET_SHFTJIS ) ;
// 2重起動禁止
DxLib::SetDoubleStartValidFlag( FALSE ) ;
// 非活性時も実行する
DxLib::SetAlwaysRunFlag( TRUE ) ;
// ウィンドウモードで起動
DxLib::ChangeWindowMode( TRUE ) ;
// 画面モード設定
DxLib::SetGraphMode( WIN_SIZE_W, WIN_SIZE_H, 32 ) ;
// ただのサンプルプログラムなのでXInputやXAudioを使わない(初期化の高速化のため)
DxLib::SetUseXInputFlag( FALSE ) ;
DxLib::SetEnableXAudioFlag( FALSE ) ;
// DXライブラリ初期化(失敗したら終了)
if( DxLib::DxLib_Init() != 0 ){ return 0 ; }
// Altキーで一時停止させない
DxLib::SetSysCommandOffFlag( TRUE ) ;
// 描画先を裏画面に設定
DxLib::SetDrawScreen( DX_SCREEN_BACK ) ;
// ファイルのD&D機能を使う
DxLib::SetDragFileValidFlag( TRUE ) ;
// ドロップされたかどうかの情報を初回クリア
DxLib::DragFileInfoClear() ;
//++++++++++++++++++++++++++++
// フォント設定
//++++++++++++++++++++++++++++
const int fontSize = 18 ;
const int fontHandle = DxLib::CreateFontToHandle( "メイリオ", fontSize, 1, DX_FONTTYPE_ANTIALIASING ) ;
const int fontColor = DxLib::GetColor( 255, 255, 255 ) ;
//++++++++++++++++++++++++++++
// 内部変数宣言と初期化
//++++++++++++++++++++++++++++
bool isDropped = false ; // ファイルドロップを1度でも行ったかどうか
char *filePath = new char[MAX_PATH] ; // ドロップされたファイルパスを格納するためのchar配列
vector> vecCodePage ; // コードページとスコアのペアの配列
HRESULT result = S_OK ; // 判定試行の成否(S_OKは判定成功, S_FALSEは判定不能, E_FAILはエラー)
// ------------------------------TODO START------------------------------
//// 扱うコードページを初期化する
//InitDetectCodePage() ;
// ------------------------------ TODO END ------------------------------
//++++++++++++++++++++++++++++
// メインループ
//++++++++++++++++++++++++++++
while( DxLib::ProcessMessage() == 0 )
{
// ESCキーで終了
if( DxLib::CheckHitKey( KEY_INPUT_ESCAPE ) != 0 ){ break ; }
//------------------------
// 更新処理
//------------------------
// ドロップされたかどうかを毎回監視
if( DxLib::GetDragFilePath( filePath ) == 0 ) // ドロップされるとfilePathが設定される
{
// ドロップされた
isDropped = true ;
// ドロップされたときの処理本体を実施
result = MainProc( filePath, vecCodePage ) ;
}
// ドロップされたかどうかの情報を毎回クリア
DxLib::DragFileInfoClear() ;
//------------------------
// 描画処理
//------------------------
// ドロップされたファイル名を表示
if( isDropped )
{
DxLib::DrawStringToHandle( 0, 0, filePath, fontColor, fontHandle ) ;
// エンコード判定試行の成否を表示
if( result == S_OK )
{
DxLib::DrawStringToHandle( 0, fontSize * 2, "判定成功o(^o^)/", fontColor, fontHandle ) ;
}
else if( result == S_FALSE )
{
DxLib::DrawStringToHandle( 0, fontSize * 2, "判定不能", fontColor, fontHandle ) ;
}
else
{
DxLib::DrawStringToHandle( 0, fontSize * 2, "エラー発生orz", fontColor, fontHandle ) ;
}
}
else
{
DxLib::DrawStringToHandle( 0, 0, ">", fontColor, fontHandle ) ;
}
// エンコード判定した結果のコードページとスコアを表示
for( int i=0, n=vecCodePage.size(); i<n; ++i )
{
DxLib::DrawFormatStringToHandle( 20, fontSize * 3 + ( fontSize * i ), fontColor, fontHandle, "コードページ=%d スコア=%d", vecCodePage[i].first, vecCodePage[i].second ) ;
}
//------------------------
// 画面反映
//------------------------
if( DxLib::ScreenFlip() != 0 ){ break ; }
//------------------------
// 裏画面クリア
//------------------------
if( DxLib::ClearDrawScreen() != 0 ){ break ; }
//------------------------
// しばらく待つ
//------------------------
Sleep( 10 ) ;
}
// メモリクリア
delete[] filePath ;
// DXライブラリ終了 ※COMの解放はDXライブラリにまかせないといかんそうな
DxLib::DxLib_End() ;
// ソフトの終了
return 0 ;
}
CP932(Shift_Jis)なのに、US-ASCIIとか意味のわからんエンコードと勘違いするケースがあることが分かりますた(-_- )
さらに、UnicodeにいたってはBOMありじゃないと判定不能ですた。
いずれも、IMultiLanguageの精度の問題です。
なによりも致命的なのは、それなりにファイルに中身(文字列)がつまってないとちゃんと判定できないってことです。
そこで疑問がわきます。
世のテキストエディタ(サクラとか秀丸とか)は、いったいどうやってんのだろう。
あいつら、数文字しか書いてないテキストでも、ピッってドロップするだけで、いかにも正しいエンコードを超高速で表示してくれるんだけど・・・。
評価時の重みづけとかがすごく練られてるんだろうか・・・。
旅を続けます((((( -_-)