8 #include "InputStream.h"
10 #include "smart_ptr.h"
13 using namespace utakata::utf8;
15 CUTF8InputStream::CUTF8InputStream() : EOF_(0xff), strm_()
19 CUTF8InputStream::CUTF8InputStream(const smart_ptr<std::istream>& strm) : EOF_(0xff), strm_(strm)
23 bool CUTF8InputStream::open(const smart_ptr<std::istream>& strm)
25 //現在保持しているストリームと切り替える。
26 // 基本的にただスワップするだけで問題ない。
28 // NULLポインタではなく、問題無く開かれている場合には、次のようにして開始する。
29 if (strm.isNull() != false && !strm->good()) {
37 std::vector<unsigned char> CUTF8InputStream::read()
40 // UTF-8に該当しない場合、空のvectorを返す。
42 throw CStreamException("not ready input stream");
45 // 最初に一文字だけ読みだして、チェックをかける。
48 if (c != std::istream::traits_type::eof())
50 // 末尾でない場合のみ、以降のチェックに入る。
52 // 先頭1バイトが正常でなかった場合はそのまま抜ける
53 if (is_utf8_first_byte(static_cast<unsigned char>(c), size))
57 // sizeが0より大きい場合には、この時複数バイトで文字が構成
58 // されていると考えられるため、明示的に複数文字を取得する。
59 std::vector<char> tmp(size, 0);
60 strm_->read(&tmp[0], size);
64 // 読み取りきれなかった場合には、ストリームに一応読出せた
66 std::for_each(tmp.rbegin(), tmp.rend(), PutBack(strm_));
67 return std::vector<unsigned char>(0);
69 std::vector<unsigned char> rtn;
70 rtn.insert(rtn.begin(), tmp.begin(), tmp.end());
77 return std::vector<unsigned char>(EOF_);
80 std::vector<unsigned char> CUTF8InputStream::read(int num)
83 // 途中で終了した場合、その文字の分だけunsigned charが減少すること
85 // numが0の場合、必ず空のvectorが返される。
89 return std::vector<unsigned char>();
92 // eofの場合なら、この時点でeofが返るので、それで問題はない。
93 std::vector<unsigned char> rtn = this->read();
94 for (int i = 1; i < num && !strm_->eof(); ++i)
96 // 個数に到達するか、もしくはeofとなるまでは追加しつづける。
97 std::vector<unsigned char> tmp = this->read();
98 rtn.insert(rtn.end(), tmp.begin(), tmp.end());
104 std::vector<unsigned char> CUTF8InputStream::peek()
106 // 一文字分だけ先読みする。先読みした場合、文字は戻す。
107 std::vector<unsigned char> tmp = this->read();
108 // 一応戻すサイズが存在する場合だけ、これを実行させることにする。
111 // 複雑な繰り返しを表現する場合には、積極的にalgorithmを利用するようにする。
113 std::for_each(tmp.rbegin(), tmp.rend(),
114 utakata::utf8::PutBack(strm_));
119 void CUTF8InputStream::unget(const std::vector<unsigned char>& ch)
121 // 渡されたバイト列をストリームに差し戻す。
123 if (is_utf8_one(ch, t))
125 std::for_each(ch.rbegin(), ch.rend(), PutBack(strm_));
129 bool utakata::utf8::CUTF8InputStream::isEOF() const
133 return strm_->eof() ? true : false;
141 //================================================================================
143 long utakata::utf8::generateUTF8Code(const std::vector<unsigned char>& bytes)
145 // 1文字分のUTF8のバイト列を受け取って、コードに変換して返す。
146 // 先頭の値によって、次のように値を決定することができる。
148 // y1〜yN = utf8の先頭バイト以降のバイト
149 // N = utf8の先頭バイトを含むバイト数
150 // code = (y1 & ((1 << 7) - 1)) << (6 * n-1) + (y2 & ((1 << 7) -1)) << (6 * (n - 1))...+ x & ((1 << N) -1) << (6 * N-1)
151 // 先頭バイト以外は、全て先頭に10とうビットが設定されている。このビットを除いた6ビットをする。
152 // つまり、末尾のバイトから順次やっていけばよい。
154 std::vector<unsigned char> tmp(bytes);
155 const unsigned char max_c = (1 << (sizeof(unsigned char) * 8 - 1)) - 1;
165 // asciiコードは7bitなのでそこだけ切り取って返す。
166 code = tmp[0] & max_c;
173 unsigned char operator()(unsigned char c, int s) {
178 // サイズが1以外の場合、ここからがちと違う。
179 std::vector<unsigned char>::reverse_iterator beg = tmp.rbegin(),
180 end = tmp.rend() - 1;
181 const unsigned char char_bit = (1 << 6) - 1;
182 for (int i = 0; beg != end; ++i,++beg)
184 code += Lambda()((*beg & char_bit), i);
188 const unsigned char first_byte = (1 << ((sizeof(unsigned char) + 1) - tmp.size())) - 1;
189 code += Lambda()(first_byte,tmp.size() - 1);
197 long utakata::utf8::generateUTF8Code(const std::string& bytes)
199 // UTF8である一文字のstringを受け取って、先頭1文字の値を返す。
201 std::string str = bytes;
202 std::vector<unsigned char> tmp;
203 tmp.insert(tmp.end(), str.begin(), str.end());
205 // vectorにしなおしたら後は元々の関数に任せる。
206 return generateUTF8Code(tmp);
209 bool utakata::utf8::is_utf8_one(const std::vector<unsigned char>& bytes, size_t& size)
211 //渡したバイト列がUTF8の一文字に該当するかどうかを返す。
214 if (bytes.size() == 0)
222 if (!is_utf8_first_byte(bytes[0], num))
228 // そもそもbytesのサイズが足りない場合にも失敗とする。
229 if (num > bytes.size())
235 // 先頭要素以外が正しければそれで問題ないとする。
239 const CheckUTF8Byte& checker = for_each(bytes.begin() + 1, bytes.begin() + num,
250 // sizeが0の場合には、この時点で1を設定するようにする。
258 bool utakata::utf8::is_utf8_all(const std::vector<unsigned char>& bytes)
260 // 与えられたバイト列全てがUTF-8であるかどうかを返す。
262 std::vector<unsigned char>::const_iterator it = bytes.begin();
263 while (is_utf8_one(std::vector<unsigned char>(it, bytes.end()), size)) {
268 if (it == bytes.end()) {
275 bool utakata::utf8::is_utf8_first_byte(unsigned char c, size_t& size)
277 // UTf-8の先頭バイトであるかどうかを返す。
278 // 先頭バイトである場合には、その先頭バイトを含む、一文字のサイズを返す。
280 const unsigned char max_c = 1 << (sizeof(unsigned char) * 8 - 1);
284 // 最上位ビットが0である場合、これはasciiコードを指す。
291 unsigned char first = c << 1;
293 while (first & max_c) {
298 // ここまできたとき、最上位ビットは0であるはず。
299 // numが5未満である場合、とりあえず正常としておくこととする。
300 const unsigned char max_utf8_sequence = 5;
301 if (num < max_utf8_sequence) {
307 // numが1の場合、何らかの理由で先頭が欠落したと見られる。
308 // この場合、スキップするべきバイト数を返す。