source: trunk/sample/guessutf8.cpp @ 15

Revision 15, 12.2 KB checked in by murachi, 7 weeks ago (diff)
  • コードが Shift JIS で保存されていたのを UTF-8 で保存し直した。
Line 
1/**
2    @file   guessutf8.cpp
3    @brief  libiconv を用いた文字セット自動認識処理のサンプル (内部コード UCS4 版)
4    @author T.MURACHI (Toshiyuki Murayama) / (C)2010 Harapeko Inc.
5*/
6
7#include <string>
8#include <map>
9#include <vector>
10#include <cstdio>
11#include <cerrno>
12#include <iconv.h>
13#include <exception>
14
15using namespace std;
16
17/// 文字を扱う内部コード型
18typedef uint32_t ucs4_t;
19typedef basic_string<ucs4_t, char_traits<ucs4_t>, allocator<ucs4_t> > ucs4string;
20
21/**
22    @brief  デコード時例外クラス
23*/
24class UcsDecoderException : public exception
25{
26    const string message;
27    UcsDecoderException();
28    UcsDecoderException &operator =(const UcsDecoderException &);
29public:
30    UcsDecoderException(const string &mess) : exception(), message(mess) {}
31    UcsDecoderException(const UcsDecoderException &src) : exception(), message(src.message) {}
32    virtual ~UcsDecoderException() throw () {}
33    virtual const char* what() const throw () { return message.c_str(); }
34};
35
36/**
37    @brief  エンコード時例外クラス
38*/
39class UcsEncoderException : public exception
40{
41    const string message;
42    UcsEncoderException();
43    UcsEncoderException &operator =(const UcsEncoderException &);
44public:
45    UcsEncoderException(const string &mess) : exception(), message(mess) {}
46    UcsEncoderException(const UcsEncoderException &src) : exception(), message(src.message) {}
47    virtual ~UcsEncoderException() throw () {}
48    virtual const char* what() const throw () { return message.c_str(); }
49};
50
51/**
52    @brief デコーダクラス
53   
54    ファイルから読み込んだ文字列データを、内部コードの文字列に変換する。
55    変換時に、元の文字列データの文字セットを自動認識する。
56*/
57class UcsDecoder
58{
59    char *src;
60    ucs4string result;
61    size_t src_size;
62   
63    // デフォルトコンストラクタ、コピーコンストラクタ、代入演算子は使用禁止
64    UcsDecoder();
65    UcsDecoder(const UcsDecoder &);
66    UcsDecoder &operator =(const UcsDecoder &);
67   
68    /// 文字コードセットのリスト
69    static char const* const ENCODE_LIST[];
70   
71   
72    typedef map<string, int> IllSeqCountMap;
73    typedef IllSeqCountMap::value_type IllSeqCountPair;
74    typedef IllSeqCountMap::const_iterator IllSeqCountCIter;
75   
76public:
77    /**
78        @brief コンストラクタ
79        @param bytes    (I) ファイルから読み込んだ文字列データ
80       
81        引数に渡された文字列データの文字セットを解析し、 UCS4 の内部コードを生成する。
82        UCS4 テキストには get() メソッドにてアクセスできる。
83    */
84    UcsDecoder(char const* bytes)
85    {
86        src_size = 0;
87        while (*bytes++) src_size++;
88        src_size++;
89        bytes -= src_size;
90        src = new char[src_size];
91        char *tp = src;
92        while (*tp++ = *bytes++) ;
93       
94        string guess_encode = guessEncodeType();
95        fprintf(stderr, "guess encode type: %s\n", guess_encode.c_str());
96        decodeToUcs4(guess_encode);
97    }
98   
99    /**
100        @brief 内部コードテキストを取得する
101        @return 内部コードに変換されたテキストの文字列オブジェクト。
102    */
103    ucs4string const& get() const { return result; }
104   
105    ~UcsDecoder()
106    {
107        delete[] src;
108    }
109
110private:
111    // 文字セットを解析する。
112    string guessEncodeType() const
113    {
114        IllSeqCountMap ill_seq_count_map;
115        for (char const* const* encode_type = ENCODE_LIST; *encode_type; encode_type++) {
116            iconv_t iconv_handle = iconv_open("UCS-4-INTERNAL", *encode_type);
117            if (iconv_handle == reinterpret_cast<iconv_t>(-1)) {
118                throw UcsDecoderException(string("iconv_open faild: from_code = ") + *encode_type +
119                    ", to_code = UCS-4-INTERNAL");
120            }
121            char const* from_current = src;
122            vector<ucs4_t> buf(src_size);
123            char * to_current = reinterpret_cast<char *>(&buf[0]);
124            size_t from_left = src_size - 1, to_left = (src_size - 1) * sizeof(ucs4_t);
125            int ill_seq_count = 0;
126            for (;;) {
127                size_t ret = iconv(iconv_handle, &from_current, &from_left, &to_current, &to_left);
128                // エラー無し
129                if (ret != static_cast<size_t>(-1))
130                    break;
131               
132                // マルチバイトの下位オクテッドが欠けている場合、
133                // データソースはここで終了しているはずなので、エラーはカウントせず終了。
134                if (errno == EINVAL)
135                    break;
136               
137                switch (errno) {
138                    case E2BIG:
139                        // 出力先バッファのサイズが足りない。
140                        // 変換自体が目的ではないので、続きは上書きする。
141                        to_current = reinterpret_cast<char *>(&buf[0]);
142                        to_left = src_size * sizeof(ucs4_t);
143                        break;
144                    case EILSEQ:
145                        // あり得ないコードが出現。
146                        // エラーをカウントし、続きから変換を継続する。
147                        ill_seq_count++;
148                        from_current++;
149                        from_left--;
150                        break;
151                    case EBADF:
152                        // 何らかの理由により変換に失敗。通常はあり得ないはず。
153                        iconv_close(iconv_handle);
154                        throw UcsDecoderException("iconv faild");
155                    default:
156                        // 未定義のエラー番号
157                        iconv_close(iconv_handle);
158                        throw UcsDecoderException("unknown error");
159                }
160            }
161            iconv_close(iconv_handle);
162           
163            // エラーが一つもなければ確定
164            if (ill_seq_count == 0)
165                return *encode_type;
166           
167            ill_seq_count_map[*encode_type] = ill_seq_count;
168        }
169        // エラーの数が最も少ないものを候補として返す。
170        string guess_code;
171        int ill_seq_count = 0;
172        for (IllSeqCountCIter i = ill_seq_count_map.begin(); i != ill_seq_count_map.end(); i++) {
173            if (ill_seq_count == 0 || i->second < ill_seq_count) {
174                ill_seq_count = i->second;
175                guess_code = i->first;
176            }
177        }
178        return guess_code;
179    }
180   
181    // 元データの文字セットを指定して、 UCS4 内部コードテキストに変換する。
182    void decodeToUcs4(const string &encode_type)
183    {
184        iconv_t iconv_handle = iconv_open("UCS-4-INTERNAL", encode_type.c_str());
185        if (iconv_handle == reinterpret_cast<iconv_t>(-1)) {
186            throw UcsDecoderException(string("iconv_open faild: from_code = ") + encode_type +
187                ", to_code = UCS-4-INTERNAL");
188        }
189        char const* from_current = src;
190        vector<ucs4_t> buf(src_size, 0);
191        char * to_current = reinterpret_cast<char *>(&buf[0]);
192        size_t from_left = src_size - 1, to_left = (src_size - 1) * sizeof(ucs4_t);
193        for (;;) {
194            size_t ret = iconv(iconv_handle, &from_current, &from_left, &to_current, &to_left);
195            // エラー無し
196            if (ret != static_cast<size_t>(-1))
197                break;
198           
199            // マルチバイトの下位オクテッドが欠けている場合、
200            // データソースはここで終了しているはずなので、ここまでで終了。
201            if (errno == EINVAL)
202                break;
203           
204            switch (errno) {
205                case E2BIG:
206                    // 出力先バッファのサイズが足りない。
207                    // バッファを拡張し、継続する。
208                    {
209                        size_t pre_size = buf.size();
210                        buf.resize(pre_size + src_size, 0);
211                        to_current = reinterpret_cast<char *>(&buf[pre_size - 1 - (to_left / sizeof(ucs4_t))]);
212                        to_left += src_size * sizeof(ucs4_t);
213                    }
214                    break;
215                case EILSEQ:
216                    // あり得ないコードが出現。
217                    // エラーを回避し、続きから変換を継続する。
218                    from_current++;
219                    from_left--;
220                    break;
221                case EBADF:
222                    // 何らかの理由により変換に失敗。通常はあり得ないはず。
223                    iconv_close(iconv_handle);
224                    throw UcsDecoderException("iconv faild");
225                default:
226                    // 未定義のエラー番号
227                    iconv_close(iconv_handle);
228                    throw UcsDecoderException("unknown error");
229            }
230        }
231        iconv_close(iconv_handle);
232        result = &buf[0];
233    }
234};
235
236char const* const UcsDecoder::ENCODE_LIST[] = {
237    "UTF-8", "C99",// "JAVA",
238    "EUC-JP",// "EUC-JISX0213",
239    "UCS-2", "UCS-2BE", "UCS-2LE",
240    "UCS-4",
241    "UTF-16", "UTF-16BE", "UTF-16LE",
242    "UTF-32", "UTF-32BE", "UTF-32LE",
243    "ISO-2022-JP", "ISO-2022-JP-2", "ISO-2022-JP-1",// "ISO-2022-JP-3",
244    "UTF-7",
245    "SHIFT_JIS", "CP932",// "SHIFT_JISX0213",
246    "UCS-4BE", "UCS-4LE",
247    NULL
248};
249
250/**
251    @brief エンコーダクラス
252   
253    内部コードの文字列を、UTF-8 形式の文字列データに変換する。
254*/
255class UcsEncoder
256{
257    const ucs4string ucs_text;
258    vector<char> result_bytes;
259   
260    // デフォルトコンストラクタ、コピーコンストラクタ、代入演算子は使用禁止
261    UcsEncoder();
262    UcsEncoder(const UcsEncoder &);
263    UcsEncoder &operator =(const UcsEncoder &);
264   
265public:
266    /**
267        @brief コンストラクタ
268        @param src  (I) UCS4 内部コード文字列
269       
270        引数に渡された UCS4 内部コード文字列を、UTF-8 形式の文字列データに変換する。
271    */
272    UcsEncoder(const ucs4string &src) : ucs_text(src), result_bytes(src.size() + 1, '\0')
273    {
274        encodeFromUcs4();
275    }
276    /**
277        @brief UTF-8 形式の文字列データを取得する
278        @return UTF-8 形式の文字列データへのポインタ
279    */
280    char const* get() const { return &result_bytes[0]; }
281   
282private:
283    // UCS4 内部コード文字列を UTF-8 形式に変換する
284    void encodeFromUcs4()
285    {
286        iconv_t iconv_handle = iconv_open("UTF-8", "UCS-4-INTERNAL");
287        if (iconv_handle == reinterpret_cast<iconv_t>(-1)) {
288            throw UcsEncoderException("iconv_open faild: from_code = UCS-4-INTERNAL, to_code = UTF-8");
289        }
290        char const* from_current = reinterpret_cast<char const*>(ucs_text.c_str());
291        char * to_current = &result_bytes[0];
292        size_t from_left = ucs_text.size() * sizeof(ucs4_t), to_left = ucs_text.size();
293        for (;;) {
294            size_t ret = iconv(iconv_handle, &from_current, &from_left, &to_current, &to_left);
295            // エラー無し
296            if (ret != static_cast<size_t>(-1))
297                break;
298           
299            // マルチバイトの下位オクテッドが欠けている場合、
300            // データソースはここで終了しているはずなので、ここまでで終了。
301            // ※ありえないが…
302            if (errno == EINVAL)
303                break;
304           
305            switch (errno) {
306                case E2BIG:
307                    // 出力先バッファのサイズが足りない。
308                    // バッファを拡張し、継続する。
309                    {
310                        size_t pre_size = result_bytes.size();
311                        result_bytes.resize(pre_size + ucs_text.size(), '\0');
312                        to_current = reinterpret_cast<char *>(&result_bytes[pre_size - 1 - to_left]);
313                        to_left += ucs_text.size();
314                    }
315                    break;
316                case EILSEQ:
317                    // あり得ないコードが出現。
318                    // エラーを回避し、続きから変換を継続する。
319                    from_current++;
320                    from_left--;
321                    break;
322                case EBADF:
323                    // 何らかの理由により変換に失敗。通常はあり得ないはず。
324                    iconv_close(iconv_handle);
325                    throw UcsEncoderException("iconv faild");
326                default:
327                    // 未定義のエラー番号
328                    iconv_close(iconv_handle);
329                    throw UcsEncoderException("unknown error");
330            }
331        }
332        iconv_close(iconv_handle);
333    }
334};
335
336int main()
337{
338    try {
339        // 標準入力を一気読み…
340        vector<char> buf(32768, '\0');
341        char * target = &buf[0];
342        size_t readed_size = 0;
343        for (;;) {
344            readed_size += fread(target, 1, 32767, stdin);
345            if (ferror(stdin))
346                throw "stdin read error";
347            if (feof(stdin)) {
348                buf[readed_size] = '\0';
349                break;
350            }
351            size_t pre_size = buf.size();
352            buf.resize(pre_size + 32767, '\0');
353            target = &buf[pre_size - 1];
354        }
355        // デコード
356        ucs4string text = UcsDecoder(&buf[0]).get();
357        // 「、」を「,」に変換する
358        for (size_t i = text.find_first_of(0x3001ul); i != ucs4string::npos; i = text.find_first_of(0x3001ul, i + 1))
359            text[i] = 0x2cul;
360        // 「。」を「.」に変換する
361        for (size_t i = text.find_first_of(0x3002ul); i != ucs4string::npos; i = text.find_first_of(0x3002ul, i + 1))
362            text[i] = 0x2eul;
363        // エンコードして出力
364        fputs(UcsEncoder(text).get(), stdout);
365    }
366   
367    catch (UcsDecoderException const& ex) {
368        fprintf(stderr, "guessutf8: UCS Decoding error - %s\n", ex.what());
369        return 1;
370    }
371    catch (UcsEncoderException const& ex) {
372        fprintf(stderr, "guessutf8: UCS Encoding error - %s\n", ex.what());
373        return 1;
374    }
375    catch (exception const& ex) {
376        fprintf(stderr, "guessutf8: Unknown error - %s\n", ex.what());
377        return 1;
378    }
379    catch (char const* mess) {
380        fprintf(stderr, "guessutf8: main() error - %s\n", mess);
381        return 1;
382    }
383    catch (...) {
384        throw;
385    }
386   
387    return 0;
388}
Note: See TracBrowser for help on using the repository browser.