なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

Shift-JISからutf-8に直接変換

C言語で2ちゃんにつないで、ダウンロードした内容をUTF-8に変換する処理。
文字コード対応表はここのサイトUnicode対応 文字コード表のものを使わせてもらった。漢字とカナを変換するために、16進数で「9,a,b,c,d,e」から始まるものを全てくっつけた。
たぶん文字コード変換ではShift-JIS→UTF-8に直接変換する手段はなかったような…iconvとかでできるかな?

追記:2014/9/8

こういう処理はiconvで書きましょう。そのほうがコードも短いし処理も早いです(泣)。
iconv - Wikipedia

iconvがGPLだから企業のコードに使えない?
ならばnkf, icu, babel等優れた文字コード変換ライブラリは多数存在します。自分お手製の文字コード変換ライブラリなんて死んでも 作ってはいけません!


nkf Network Kanji Filter プロジェクト日本語トップページ - SourceForge.JP


ICU - International Components for Unicode


バベル - extra - C++ - TrickLibrary

以下、自作の文字コード変換処理…

#include <fstream>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>

#include <zlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace std;
using namespace boost::asio;
namespace fs = boost::filesystem;

#define SJIS_CHECK_STR	strncmp(SJISHEX ,"8" , 1) == 0|| \
strncmp(SJISHEX ,"9" , 1) == 0|| \
strncmp(SJISHEX ,"e" , 1) == 0|| \
strncmp(SJISHEX ,"a" , 1) == 0|| \
strncmp(SJISHEX ,"b" , 1) == 0|| \
strncmp(SJISHEX ,"c" , 1) == 0|| \
strncmp(SJISHEX ,"d" , 1) == 0

#define HANKAKU_CHECK_STR strncmp(SJISHEX ,"a" , 1) == 0|| \
strncmp(SJISHEX ,"b" , 1) == 0|| \
strncmp(SJISHEX ,"c" , 1) == 0|| \
strncmp(SJISHEX ,"d" , 1) == 0

int main()
{
    // ディレクトリ作成
    fs::path dir( "./dat" );
    fs::create_directory( dir );

    // menu.2ch.net の http サービスに接続
    ip::tcp::iostream s( "menu.2ch.net", "http" );

    // 送信
    s << "GET /bbsmenu.html HTTP/1.0 \r\n";
    s << "Accept-Encoding: gzip \r\n";
    s << "Host: menu.2ch.net \r\n";
    s << "Accept:  \r\n";
    s << "Referer: http://menu.2ch.net/ \r\n";
    s << "Accept-Language: ja \r\n";
    s << "User-Agent: Mozilla/5.0 \r\n";
    s << "Connection: close \r\n";
    s << "\r\n";
    s << flush;

    // 受信
    string line;
    bool flag = false;
    // gzipのヘッダ作成
    char HEX[] = {0x1f,0x8b,0x08,0x00};
    //  ios::binary はバイナリ形式で出力(省略するとアスキー形式で出力)
    fs::ofstream outputfilegzip( dir/"BoardList.gzip", std::ios::binary );

    while( getline(s, line) ){
	if ( flag ) {
	    // 真の場合
	    outputfilegzip << line << endl;
	} else {
	    // 偽の場合
	    unsigned int loc = line.find( *HEX , 0 );
	    if( loc != string::npos ) {
		outputfilegzip << line << endl;
		flag = true;
	    }
	}
    }
    outputfilegzip.close();
    line.clear();

    // gzファイルをZlibを使って解凍する
    gzFile infile = gzopen("./dat/BoardList.gzip", "rb");
    FILE *outfile = fopen("./dat/BoardListSJIS.txt", "wb");

    char buffer[128];
    int num_read = 0;
    while ((num_read = gzread(infile, buffer, sizeof(buffer))) > 0) {
	fwrite(buffer, 1, num_read, outfile);
    }

    // ファイルポインタを閉じる
    gzclose(infile);
    fclose(outfile);

    // 文字列変換処理
    // ファイルポインタの用意
    FILE *fp_sjis;
    int c_sjis;
    FILE *fp_utf8;
    int c_utf8;

    /* 読み込みモードでSJISのファイルをオープン */
    if( fp_sjis != NULL ){
	fp_sjis = fopen( "./dat/BoardListSJIS.txt", "r" );
    } else {
	perror("版一覧リストのShift_JISファイル");
	exit(EXIT_FAILURE);
    }
    /* 書き出しモードでSJISのファイルをオープン */
    if( fp_utf8 != NULL ){
	fp_utf8 = fopen( "./dat/BoardListUTF8.txt", "w" );
    } else {
	perror("版一覧リストのUTF-8ファイル");
	exit(EXIT_FAILURE);
    }

    // 判定用変数の準備
    char SJISHEX[3];
    // 変換テーブルから文字列を読み込むstring
    string lineTrans;

    // UTF-8への変換フラグ
    // flag_su 0:通常時 1:1文字目 2:2文字目
    int flag_su = 0;
    // 変換テーブル探索用文字列
    char search_table[5]; // 88a2\0 ← 最終型

    // 変換ループ
    for(;;) {
	// SJISファイルを一文字ごとに読み出す
	c_sjis = fgetc( fp_sjis );

	// SJISファイルが最終の文字ならばループから抜ける
	if( c_sjis == EOF ) { break;}
	sprintf(SJISHEX, "%02x", (char*)c_sjis);
	SJISHEX[2] = '\x0';

	// 2週目でフラグが1だったら2にしておく
	if ( flag_su == 1 ) { flag_su = 2; }
	// Shift-JISの先頭バイトが見つかればフラグを1に
	if ( (SJIS_CHECK_STR) && flag_su != 1 && flag_su != 2 ){ flag_su = 1; }

	// switch文の開始
	switch (flag_su) {
	// ascii文字だった場合そのまま書きだす
	case 0:
	    // Shift_JIS以外のasciiコードならばそのまま書きだす
	    // SJISからUTF-8に値渡し
	    c_utf8 = c_sjis;
	    fputc(c_utf8, fp_utf8);
	    break;

	    // 1文字目のShift_JISコードを見つけたので保存
	case 1:
	    // 1文字目が一致したので先頭1バイトを格納する
	    char testHex[8];
	    sprintf(testHex, "%02x", (char*)c_sjis);
	    search_table[0] = testHex[0];
	    search_table[1] = testHex[1];
	    search_table[2] = '\x0';

	    // もしも1文字目で変換が必要な半角カナ文字等だったらここで変換する
	    if (HANKAKU_CHECK_STR) {
		printf("convert: %s ",search_table);

		// 変換テーブルは読み込み専用で開く
		ifstream transtable( "./dat/transtable.dat", std::ios::in );

		// 対象となる文字列が見つかるまでループ
		while ( getline(transtable, lineTrans) ) {
		    char scanstr[ 64 ];
		    strcpy( scanstr, lineTrans.c_str() );

		    // スキャンした文字列中のSJISとUTF8文字列へのポインタ
		    char *psjis_kana = scanstr;
		    psjis_kana = psjis_kana + 11;

		    // 探索文字列の大文字化
		    for( int i=0; i<3; i++ ){
			search_table[i] = toupper( search_table[i] );
		    }
		    // scanstr中のSJIS文字列と一致すればループ脱出
		    if ( *psjis_kana == search_table[0] &&
			    *(psjis_kana+1) == search_table[1] ) {

			// UTF-8用のバイナリを作成して書きこむ
			char wb1[3] = { scanstr[21], scanstr[22], '\x0' };
			char wb2[3] = { scanstr[23], scanstr[24], '\x0' };
			char wb3[3] = { scanstr[25], scanstr[26], '\x0' };

			printf("to %s%s%s \n",wb1,wb2,wb3);

			//char型文字列から変換
			long	nValue;
			char*	wbEnd;

			nValue = ::strtol(wb1,&wbEnd,16);
			fputc((unsigned int)nValue, fp_utf8);
			nValue = ::strtol(wb2,&wbEnd,16);
			fputc((unsigned int)nValue, fp_utf8);
			nValue = ::strtol(wb3,&wbEnd,16);
			fputc((unsigned int)nValue, fp_utf8);

		    }
		}
		// フラグをもとに戻す
		flag_su = 0;
		// 変換テーブルは毎回閉じる
		transtable.close();
	    }

	    break;

	    // 2文字目のShift_JISコードを見つけて変換テーブルに探しに行く
	case 2:
	    sprintf(testHex, "%02x", (char*)c_sjis);
	    search_table[2] = testHex[0];
	    search_table[3] = testHex[1];
	    search_table[4] = '\x0';
	    printf("convert: %s ",search_table);

	    // 変換テーブルは読み込み専用で開く
	    ifstream transtable( "./dat/transtable.dat", std::ios::in );

	    // 対象となる文字列が見つかるまでループ
	    while ( getline(transtable, lineTrans) ) {
		char scanstr[ 64 ];
		strcpy( scanstr, lineTrans.c_str() );

		// スキャンした文字列中のSJISとUTF8文字列へのポインタ
		char *psjis = scanstr;
		psjis = psjis + 11;

		// 探索文字列の大文字化
		for( int i=0; i<5; i++ ){
		    search_table[i] = toupper( search_table[i] );
		}
		// scanstr中のSJIS文字列と一致すればループ脱出
		if ( *psjis == search_table[0] &&
			*(psjis+1) == search_table[1] &&
			*(psjis+2) == search_table[2] &&
			*(psjis+3) == search_table[3]   ) {

		    // UTF-8用のバイナリを作成して書きこむ
		    char wb1[3] = { scanstr[21], scanstr[22], '\x0' };
		    char wb2[3] = { scanstr[23], scanstr[24], '\x0' };
		    char wb3[3] = { scanstr[25], scanstr[26], '\x0' };

		    printf("to %s%s%s \n",wb1,wb2,wb3);

		    //char型文字列から変換
		    long	nValue;
		    char*	wbEnd;

		    nValue = ::strtol(wb1,&wbEnd,16);
		    fputc((unsigned int)nValue, fp_utf8);
		    nValue = ::strtol(wb2,&wbEnd,16);
		    fputc((unsigned int)nValue, fp_utf8);
		    nValue = ::strtol(wb3,&wbEnd,16);
		    fputc((unsigned int)nValue, fp_utf8);

		    break;
		}
	    }
	    // フラグをもとに戻す
	    flag_su = 0;
	    // 変換テーブルは毎回閉じる
	    transtable.close();
	    break;
	}// switch文終了
    }
    fclose( fp_sjis );
    fclose( fp_utf8 );
}