インターネットよりファイルを1行ずつ読み出しEUC文字列をUNICODE又はSJISに変換してファイルへ保存する

icon 項目のみ表示/展開表示の切り替え

概要

指定したURLのファイルをインターネットから1行ずつ取得しEUC文字列をUNICODEでコンパイルした場合はUNICODE、マルチバイトでコンパイルした場合はSJISに変換してファイルに保存します。
EUCはExtended UNIX Code Packed Format for Japaneseの略であり名称の通り昔UnixやLinux等で広く使われた文字コードです。
現在はUTF-8を使用している例が多いようです。
半角文字コードの0x00~0x7fまではANSI文字列と同一であり、0x80~0xffの場合、漢字の1バイト目、2バイト目であることを示します。さらに0x8fの場合は、補助漢字を示し次の2バイト目、3バイト目が文字コードを文字コードとなります。
漢字の文字コードはJISコードをシフトさせたものとなりますのでJISとの親和性は高いものとなります。
EUCで保存されたファイルをメモ帳で開くと文字化けのオンパレードとなります。
本プログラムの使用方法はコマンドプロンプトを実行し、以下のように入力します。
以下の場合、私のホームページのEUC文字列を含むeuc.htmlをファイルtest.htmlに保存します。
wgeteuc http://yamatyuu.net/computer/html/charset/euc.html test.html
インターネットからの読み込みにはWININETを使用しています。
WININETは1行ずつの読み込みに対応していないので、バッファリングと1行読み込みをサポートしたクラスを作成しています。
EUC文字列は一度UNICODEに変換します。MultiByteToWideChar APIはEUC文字列をUNICODEに変換できなかったので、IEの一部であるMLANG.DLL(IE5.5以上が対象なのでWindows 95以上)に含まれるConvertINetMultiByteToUnicodeを用います。
このConvertINetMultiByteToUnicodeは、EUC以外の文字コードをUNICODEに変換することができます。
Visual C++ 2008/2013をインストールした状態では、MLANG.DLL用のライブライファイルが存在しません。ここでは、動的リンクでConvertINetMultiByteToUnicodeを呼び出します。
静的にリンクしたい場合は、DLLファイルからLIBファイルを作成する(_stdcall宣言)を参照してLIBファイルを作成してリンクしてください。

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

プロジェクトの作成

Win32プロジェクト Windowsコンソールアプリケーション

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

プログラムソースの概要

_tmain

UNICODE文字を標準エラー出力に正しく表示させるために_tsetlocale関数によりロケールを設定します。 webEucTextGet関数で使用するConvertINetMultiByteToUnicode APIを動的にリンクして使用するために、LoadLibrary APIを用いてmlang.dllをロードします。
次にGetProcAddress APIを用いてConvertINetMultiByteToUnicode APIのアドレスを取得しグローバル変数に格納します。
InternetOpen APIによりインタネットハンドルを取得します。
APIが0を返した場合は、インタネットハンドルを取得できなかったので 標準エラー出力にエラーメッセージを表示しプログラムを終了させます。
_tfopen_sにより取得したデータを保存するためのファイルを書き込みでバイナリモードで開きファイルハンドルを取得します。
ファイルが開けなかった場合は、InternetCloseHandle APIによりインターネットハンドルを閉じて 標準エラー出力にエラーメッセージを表示しプログラムを終了させます。
webEucTextGet関数によりインタネット上のファイルを読み取り、ファイルに保存します。
InternetCloseHandle APIによりインターネットハンドルを閉じます。
fclose関数によりファイルハンドルを閉じます。

webEucTextGet

行単位での入力が可能なようにINETクラスを作成し、メンバー関数openで指定URLをオープンしハンドルを取得します。
オープンできなかった場合は、関数を終了します。
INETクラスgetsメンバー関数で1行を取得します。
ConvertINetMultiByteToUnicode APIによりEUC文字列をUNICODE文字列に変換します。
マルチバイトでコンパイルされている場合は、WideCharToMultiByte APIによりUNICODE文字列をSJIS文字列に変換します。
_ftprintf関数でファイルに書き込みます。
読み込める行がなくなるまで、繰り返します。

INETクラス

inet.hで定義されています。
WININETは行単位の入力をサポートしていないので、クラス内でバッファリングを行い、 getsメンバー関数により行単位を要求された場合、バッファ内の行部分をコピーして返します。バッファが空になったらバッファサイズ分読み込みを行います。
¥nか¥rが行末であると解釈して処理を行います。

プログラムソース

wgeteuc.cpp

// インターネットより指定したurlを読み出しEUC文字をUNICODE又はSJISに変換して指定したファイルへ保存します。
//      Visual C++ 2008/2013 Unicode/マルチバイト

#include <windows.h>
#include <stdio.h>
#include <wininet.h>
#include <tchar.h>
#include <locale.h>
#include "inet.h"

#pragma comment(lib,"wininet.lib")

typedef HRESULT (WINAPI *ConvertINetMultiByteToUnicode_p)(LPDWORD , DWORD , LPCSTR , LPINT , LPWSTR ,  LPINT ); 

ConvertINetMultiByteToUnicode_p ConvertINetMultiByteToUnicode=0;

//      指定したURLのEUCテキストをUNICODEまたはSJISに変換して指定ファイルに保存する
int webEucTextGet(HINTERNET hInet, TCHAR* url, FILE* fp);

int _tmain(int argc,TCHAR** argv){
        HMODULE hMod;
        
        //      UNICODE文字を標準エラー出力に正しく表示させるためにロケールを設定
        _tsetlocale(LC_ALL, _TEXT(""));

        if (argc != 3){
                _tprintf(_TEXT("使用方法¥nwgeteuc [url] [保存ファイル名]¥n"));
                return 1;
        }

        hMod = LoadLibrary(_TEXT("mlang.dll"));       //      文字コード変換用のDLLをロード
        if (hMod==0){
                perror("MLANG.DLLがロードできません¥n");
                return 1;
        }
        ::ConvertINetMultiByteToUnicode = (ConvertINetMultiByteToUnicode_p)GetProcAddress(hMod, "ConvertINetMultiByteToUnicode");

        HINTERNET hInet;
        //インターネット(WinInet)開始
        hInet = InternetOpen(TEXT("wxz123"),
                INTERNET_OPEN_TYPE_PRECONFIG,   
                NULL, NULL, 0);
        if (hInet == NULL) {
                FreeLibrary(hMod);
                perror("オープンエラー¥n");
                return 2;
        }
        FILE* fp;
#ifdef _UNICODE
        _tfopen_s(&fp, argv[2], _TEXT("w,ccs=UNICODE"));
#else
        _tfopen_s(&fp, argv[2], _TEXT("w"));
#endif
        webEucTextGet(hInet,argv[1],fp);

        fclose(fp);
        InternetCloseHandle(hInet);
        FreeLibrary(hMod);
}

//      指定したURLのEUCテキストをUNICODEまたはSJISに変換して指定ファイルに保存する

int webEucTextGet(HINTERNET hInet, TCHAR* url, FILE* fp){
        HINTERNET h;
        INET inet(hInet);
        h = inet.open(url);
        if (h == 0)
                return -1;
        char src[1024];
        int size = 0;
        while (inet.gets(src, sizeof(src))){
                WCHAR utf16[1024];
                DWORD mode;
                TCHAR* dtc;
                int len = sizeof(utf16) / sizeof(WCHAR);
                //      EUCをUNICODEに変換
                ConvertINetMultiByteToUnicode(&mode, 51932, src, 0, utf16, &len);
                utf16[len] = 0;
#ifdef _UNICODE
                dtc = utf16;
#else
                char sjis[1024];        //      UNICODEをSJISに変換
                WideCharToMultiByte(932, 0, utf16, -1, sjis, sizeof(sjis), NULL, NULL);
                dtc = sjis;
#endif
                size += len;
                _ftprintf(fp, _TEXT("%s¥n"), dtc);
        }
        return size;
}

inet.h

#ifndef INET_H

//      インターネットから1行ずつ読み取るクラス

#define INET_H 0x3000
#define INET_BUF_SIZE_DEF 65536

#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <wininet.h>
#include <assert.h>
#include <TCHAR.H>

class INET{
        HINTERNET hInet;
        HINTERNET hUrl;
        char*   buf;            //      読み取りバッファ(文字コード変換なし)
        unsigned                p;              //      バッファ内の位置
        DWORD   len;    //      バッファの有効データー数
        int wpos;
        int n;  //      バッファ内の読み出し可能先頭位置
        int INET_BUF_SIZE;
        bool new_mem_f;
public:
        UINT64  total_read;
        void init(HINTERNET h,void* bp,int sz){
                hInet=h;
                hUrl=0;
                p=len=0;
                INET_BUF_SIZE=sz;
                buf=(char*)bp;
                new_mem_f=false;
        }
        INET(){
                hInet=0;
                hUrl=0;
                p=len=0;
                INET_BUF_SIZE=0;
                buf=0;
                new_mem_f=false;
                total_read=0;
        }
        INET(HINTERNET h){
                hInet=h;
                hUrl=0;
                p=len=0;
                INET_BUF_SIZE=INET_BUF_SIZE_DEF;
                buf=new char[INET_BUF_SIZE+1];
                new_mem_f=true;
        }
        INET(HINTERNET h,int sz){
                hInet=h;
                hUrl=0;
                p=len=0;
                INET_BUF_SIZE=sz;
                buf=new char[INET_BUF_SIZE];
                new_mem_f=true;
        }
        HINTERNET open(TCHAR* url){
                wpos=0;
                n=0;
                return hUrl=InternetOpenUrl(hInet, url, NULL, 0, INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, 0);
        }
        int read(char* buf,int max){
                int len;
                InternetReadFile(hUrl, buf, max - 1, (LPDWORD)&len); 
                total_read += len;
                return len;
        }
        char* gets(char* b,int max){    // 1行分取得 改行コードを除去する
                int i=0;
                for(;;){
                        if(p==0 || len==0){             //      バッファ内に有効データーがないのでサイトから読み取る
                                len=read(buf,INET_BUF_SIZE);
                        }
                        if(len==0){
                                return  0;
                        }
                        for(;p<len;p++){
                                if(i==max-1){
                                        b[i]='¥0';
                                        return b;
                                }
                                if(buf[p]=='¥n' || buf[p]=='¥r'){
                                        b[i]='¥0';
                                        ++p;
                                        return b;
                                }else{
                                        b[i++]=buf[p];
                                }
                        }
                        p=0;
                }

        }
        void close(void){
                if(hUrl)
                        InternetCloseHandle(hUrl);
                hUrl=0;
        }
        ~INET(){
                if(hUrl)
                        InternetCloseHandle(hUrl);
                if(new_mem_f==true && buf)
                        delete buf;
                INET_BUF_SIZE=0;
        }
};

#endif

ソースファイルと実行ファイルのダウンロード

ダウンロード wgeteuc.zip(65.6kByte)
ZIPファイルに含まれるファイル
wgeteuc.cpp
inet.h
wgeteuc.exe