• R/O
  • SSH
  • HTTPS

cadencii: コミット


コミットメタ情報

リビジョン1911 (tree)
日時2012-01-07 17:39:52
作者kbinani

ログメッセージ

[vConnect/trunk/stand2.0] add: TextReader

変更サマリ

差分

--- vConnect/trunk/stand2.0/stand/TextReader.h (nonexistent)
+++ vConnect/trunk/stand2.0/stand/TextReader.h (revision 1911)
@@ -0,0 +1,255 @@
1+#ifndef __TextReader_h__
2+#define __TextReader_h__
3+
4+#include <stdio.h>
5+#include "EncodingConverter.h"
6+
7+namespace vconnect
8+{
9+ /**
10+ * テキストファイルを読み込むためのクラス
11+ * @todo 1 行が BUFFER_SIZE を超えるテキストを読む場合動作がデタラメ
12+ */
13+ class TextReader
14+ {
15+ private:
16+ /**
17+ * 1行の最大文字数
18+ */
19+ static const int BUFFER_SIZE = 1024;
20+
21+ /**
22+ * テキストエンコーディングのコンバータ
23+ */
24+ EncodingConverter *converter;
25+
26+ /**
27+ * ファイルハンドル
28+ */
29+ FILE *fileHandle;
30+
31+ /**
32+ * 指定されたエンコーディングの読み込み単位バイト数
33+ * たとえば UTF8 なら 1 バイト、UTF16 なら 2 バイト単位で読み込む
34+ */
35+ int bytesPerWord;
36+
37+ /**
38+ * bytesPerWord バイト分のバッファー
39+ */
40+ char *unitBuffer;
41+
42+ /**
43+ * ファイルからデータを読み込むときに使うバッファー
44+ */
45+ char *buffer;
46+
47+ public:
48+ /**
49+ * テキストエンコーディングを指定して、テキストファイルを開く
50+ * @param path 開くファイルのパス
51+ * @param encoding テキストエンコーディング
52+ */
53+ TextReader( string path, string encoding )
54+ {
55+ this->converter = new EncodingConverter( encoding, EncodingConverter::getInternalEncoding() );
56+ this->fileHandle = fopen( path.c_str(), "rb" );
57+ this->bytesPerWord = EncodingConverter::getBytesPerWord( encoding );
58+ this->unitBuffer = new char[this->bytesPerWord];
59+
60+ int bufferBytes = this->bytesPerWord * BUFFER_SIZE;
61+ this->buffer = new char[bufferBytes];
62+ }
63+
64+ /**
65+ * デストラクタ
66+ */
67+ ~TextReader()
68+ {
69+ this->close();
70+ }
71+
72+ /**
73+ * テキストファイルから 1 行読み込む
74+ * @return 行データ
75+ */
76+ string readLine()
77+ {
78+ FILE *fp = this->fileHandle;
79+ int unit_buflen = this->bytesPerWord;
80+ int unit_bufbytes = sizeof( char ) * unit_buflen;
81+ int i;
82+ int bufbytes = this->bytesPerWord * BUFFER_SIZE;
83+
84+ string result = "";
85+ bool isEndOfLine = false;
86+ while( false == isEndOfLine ){
87+ memset( this->buffer, 0, bufbytes );
88+ int offset = -unit_buflen;
89+ for( i = 0; i < bufbytes - 1; i++ ){
90+ // このループ中でbuf[offset]からbuf[offset+buflen]までを埋めます
91+ offset += unit_buflen;
92+ int j;
93+
94+ // 1文字分読み込む
95+ int len = fillUnitBuffer( this->unitBuffer, unit_buflen, fp );
96+
97+ if( len != unit_buflen ){
98+ // EOFまで読んだ場合
99+ for( j = 0; j < unit_buflen; j++ ){
100+ this->buffer[j + offset] = '\0';
101+ }
102+ isEndOfLine = true;
103+ break;
104+ }else if( isCR( this->unitBuffer, unit_buflen ) ){
105+ // 読んだのがCRだった場合
106+ // 次の文字がLFかどうかを調べる
107+ len = fillUnitBuffer( this->unitBuffer, unit_buflen, fp );
108+ if( len == unit_buflen ){
109+ if( isLF( this->unitBuffer, unit_buflen ) ){
110+ // LFのようだ
111+ }else{
112+ // LFでないので、ファイルポインタを戻す
113+ fseek( fp, -unit_bufbytes, SEEK_CUR );
114+ }
115+ }
116+ isEndOfLine = true;
117+ break;
118+ }else if( isLF( this->unitBuffer, unit_buflen ) ){
119+ // 読んだのがLFだった場合
120+ // 次の文字がCRかどうかを調べる
121+ len = fillUnitBuffer( this->unitBuffer, unit_buflen, fp );
122+ if( len == unit_buflen ){
123+ if( isCR( this->unitBuffer, unit_buflen ) ){
124+ // CRのようだ
125+ // LF-CRという改行方法があるかどうかは知らないけれどサポートしとこう
126+ }else{
127+ // CRでないので、ファイルポインタを戻す
128+ fseek( fp, -unit_bufbytes, SEEK_CUR );
129+ }
130+ }
131+ isEndOfLine = true;
132+ break;
133+ }else{
134+ // 通常の処理
135+ for( j = 0; j < unit_buflen; j++ ){
136+ this->buffer[offset + j] = this->unitBuffer[j];
137+ }
138+ }
139+ }
140+
141+ string source = this->buffer;
142+ result += this->converter->convert( source );
143+ }
144+
145+ return result;
146+ }
147+
148+ /**
149+ * ファイル読み込みがファイル末尾に達したかどうか
150+ * @return ファイル末尾に達している場合 true を、そうでなければ false を返す。
151+ */
152+ bool isEOF()
153+ {
154+ return feof( this->fileHandle ) ? true : false;
155+ }
156+
157+ /**
158+ * ファイルを閉じる
159+ */
160+ void close()
161+ {
162+ fclose( this->fileHandle );
163+ delete this->converter;
164+ delete [] this->unitBuffer;
165+ delete [] this->buffer;
166+ }
167+
168+ private:
169+ /**
170+ * マルチバイトの一文字分のデータをファイルから読み込む
171+ * @param buffer 読み込んだデータの格納先
172+ * @param length buffer のバイト数
173+ * @param fileHandle 読み込み対象のファイル
174+ * @return 読み込んだデータのバイト数
175+ */
176+ static int fillUnitBuffer( char *buffer, int length, FILE *fileHandle )
177+ {
178+ int ret = 0;
179+ memset( buffer, 0, length * sizeof( char ) );
180+ for( int i = 0; i < length; i++ ){
181+ int c = fgetc( fileHandle );
182+ if( c == EOF ){
183+ return ret;
184+ }
185+ buffer[i] = (char)c;
186+ ret++;
187+ }
188+ return ret;
189+ }
190+
191+ /**
192+ * 指定したマルチバイトの一文字が、指定した制御文字を表すかどうかを調べる
193+ * @param buffer マルチバイトの一文字
194+ * @param length buffer のバイト数
195+ * @param expected 期待値
196+ * @return 一致したら ture を、そうでなければ false を返す
197+ */
198+ static bool isExpectedCode( char *buffer, int length, char expected )
199+ {
200+ if( length < 1 ){
201+ return false;
202+ }
203+ int i;
204+
205+ // LEを仮定
206+ if( buffer[0] == expected ){
207+ for( i = 1; i < length; i++ ){
208+ if( buffer[i] != 0x00 ){
209+ return false;
210+ }
211+ }
212+ return true;
213+ }
214+
215+ // BEを仮定
216+ if( buffer[length - 1] == expected ){
217+ for( i = 0; i < length - 1; i++ ){
218+ if( buffer[i] != 0x00 ){
219+ return false;
220+ }
221+ }
222+ return true;
223+ }
224+
225+ return false;
226+ }
227+
228+ /**
229+ * マルチバイト文字列が、改行制御文字(キャリッジリターン, CR)を表すかどうかを調べます
230+ * @param buf 調査対象のマルチバイト文字列のバッファ
231+ * @param len バッファbufの長さ
232+ * @param check_char チェックする制御文字コード
233+ * @return バッファの文字列がLFを表すならtrue、そうでなければfalse
234+ */
235+ static bool isCR( char *buffer, int length )
236+ {
237+ return isExpectedCode( buffer, length, 0x0D );
238+ }
239+
240+ /**
241+ * マルチバイト文字列が、改行制御文字(ラインフィード, LF)を表すかどうかを調べます
242+ * @param buf 調査対象のマルチバイト文字列のバッファ
243+ * @param len バッファbufの長さ
244+ * @param check_char チェックする制御文字コード
245+ * @return バッファの文字列がLFを表すならtrue、そうでなければfalse
246+ */
247+ static bool isLF( char *buffer, int length )
248+ {
249+ return isExpectedCode( buffer, length, 0x0A );
250+ }
251+ };
252+}
253+
254+#endif
255+
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
--- vConnect/trunk/stand2.0/stand/tests/fixture/TextReader/utf8_lf.txt (nonexistent)
+++ vConnect/trunk/stand2.0/stand/tests/fixture/TextReader/utf8_lf.txt (revision 1911)
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
--- vConnect/trunk/stand2.0/stand/tests/fixture/TextReader/shift_jis_crlf.txt (nonexistent)
+++ vConnect/trunk/stand2.0/stand/tests/fixture/TextReader/shift_jis_crlf.txt (revision 1911)
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
--- vConnect/trunk/stand2.0/stand/tests/AllTests.cpp (revision 1910)
+++ vConnect/trunk/stand2.0/stand/tests/AllTests.cpp (revision 1911)
@@ -1,10 +1,12 @@
11 #include <cppunit/TestRunner.h>
22 #include <cppunit/TestResult.h>
33 #include <cppunit/TestResultCollector.h>
4+#include <cppunit/CompilerOutputter.h>
45 #include <cppunit/extensions/HelperMacros.h>
56 #include <cppunit/BriefTestProgressListener.h>
67 #include <cppunit/extensions/TestFactoryRegistry.h>
78
9+#include "TextReaderTest.h"
810 #include "EncodingConverterTest.h"
911
1012 int main( int argc, char* argv[] )
@@ -20,5 +22,8 @@
2022 runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );
2123 runner.run( controller );
2224
25+ CppUnit::CompilerOutputter outputter( &results, CppUnit::stdCOut() );
26+ outputter.write();
27+
2328 return results.wasSuccessful() ? 0 : 1;
2429 }
--- vConnect/trunk/stand2.0/stand/tests/Makefile (revision 1910)
+++ vConnect/trunk/stand2.0/stand/tests/Makefile (revision 1911)
@@ -1,3 +1,3 @@
11 all: *.cpp *.h
2- g++ AllTests.cpp ../../libiconv-1.13/lib/*.o -lcppunit -o run
2+ g++ -finput-charset=UTF-8 AllTests.cpp ../../libiconv-1.13/lib/*.o -lcppunit -o run
33 ./run
--- vConnect/trunk/stand2.0/stand/tests/TextReaderTest.h (nonexistent)
+++ vConnect/trunk/stand2.0/stand/tests/TextReaderTest.h (revision 1911)
@@ -0,0 +1,61 @@
1+#ifndef TEST_TextReaderTest
2+#define TEST_TextReaderTest
3+#include <cppunit/extensions/HelperMacros.h>
4+#include "../TextReader.h"
5+
6+using namespace vconnect;
7+
8+class TextReaderTest : public CppUnit::TestFixture
9+{
10+public:
11+ void testTextReaderShiftJIS()
12+ {
13+ TextReader reader( "fixture/TextReader/shift_jis_crlf.txt", "Shift_JIS" );
14+
15+ CPPUNIT_ASSERT( false == reader.isEOF() );
16+
17+ string actual;
18+ string expected;
19+ actual = reader.readLine();
20+ expected = "だ・い・じ・け・ん";
21+ CPPUNIT_ASSERT_EQUAL( expected, actual );
22+
23+ CPPUNIT_ASSERT( false == reader.isEOF() );
24+
25+ actual = reader.readLine();
26+ expected = "社会復帰できなくなっちゃうよ";
27+ CPPUNIT_ASSERT_EQUAL( expected, actual );
28+
29+ CPPUNIT_ASSERT( true == reader.isEOF() );
30+ }
31+
32+ void testTextReaderUTF8()
33+ {
34+ TextReader reader( "fixture/TextReader/utf8_lf.txt", "UTF-8" );
35+
36+ CPPUNIT_ASSERT( false == reader.isEOF() );
37+
38+ string actual;
39+ string expected;
40+
41+ CPPUNIT_ASSERT( false == reader.isEOF() );
42+ actual = reader.readLine();
43+ expected = "吾輩は猫である。名前はまだ無い。";
44+ CPPUNIT_ASSERT_EQUAL( expected, actual );
45+
46+ CPPUNIT_ASSERT( false == reader.isEOF() );
47+ actual = reader.readLine();
48+ expected = "どこで生れたかとんと見当がつかぬ。";
49+ CPPUNIT_ASSERT_EQUAL( expected, actual );
50+
51+ CPPUNIT_ASSERT( true == reader.isEOF() );
52+ }
53+
54+ CPPUNIT_TEST_SUITE( TextReaderTest );
55+ CPPUNIT_TEST( testTextReaderShiftJIS );
56+ CPPUNIT_TEST( testTextReaderUTF8 );
57+ CPPUNIT_TEST_SUITE_END();
58+};
59+
60+CPPUNIT_TEST_SUITE_REGISTRATION( TextReaderTest );
61+#endif
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
--- vConnect/trunk/stand2.0/stand/EncodingConverter.h (revision 1910)
+++ vConnect/trunk/stand2.0/stand/EncodingConverter.h (revision 1911)
@@ -1,18 +1,38 @@
1-#include "stand.h"
1+#ifndef __EncodingConverter_h__
2+#define __EncodingConverter_h__
23
4+#include <iconv.h>
5+
36 using namespace std;
47
58 namespace vconnect
69 {
10+ /**
11+ * マルチバイト文字列のテキストエンコーディングを変換するためのコンバータ
12+ */
713 class EncodingConverter
814 {
915 private:
16+ /**
17+ * システム内部のマルチバイト文字列が使用しているテキストエンコーディング名
18+ */
19+ string internalEncoding;
20+
21+ /**
22+ * libiconv のコンバータ
23+ */
1024 iconv_t converter;
1125
1226 public:
13- EncodingConverter( const char *from, const char *to )
27+ /**
28+ * 変換元と変換先のエンコーディングを指定し、コンバータを初期化する
29+ * @param from 変換前のエンコーディング名
30+ * @param to 変換後のエンコーディング名
31+ */
32+ EncodingConverter( string from, string to )
1433 {
15- this->converter = iconv_open( to, from );
34+ this->converter = iconv_open( to.c_str(), from.c_str() );
35+ this->internalEncoding = getInternalEncoding();
1636 }
1737
1838 ~EncodingConverter()
@@ -20,6 +40,11 @@
2040 iconv_close( this->converter );
2141 }
2242
43+ /**
44+ * 文字列のテキストエンコーディングを変換する
45+ * @param source 変換する文字列
46+ * @return エンコーディングを変換した文字列
47+ */
2348 string convert( string source )
2449 {
2550 string result;
@@ -71,11 +96,110 @@
7196 return result;
7297 }
7398
99+ /**
100+ * char の内部のエンコーディングを調べる
101+ * @return
102+ */
103+ static string getInternalEncoding()
104+ {
105+ unsigned int result = 0;
106+ char *localeNameRaw = std::setlocale( LC_CTYPE, "" );
107+#ifdef __APPLE__
108+ // Macは決め打ち
109+ result = 1208;
110+#else
111+ if( NULL != localeNameRaw ){
112+ // localeName = "ja_JP.UTF-8" (openSUSE, g++)
113+ // localeName = "Japanese_Japan.932" (Windows XP, g++)
114+ string localeName = localeNameRaw;
115+ int index = localeName.rfind( "." );
116+ if( index != string::npos ){
117+ string encodingName = localeName.substr( index + 1 );
118+ // snum部分が数値に変換できるかどうかを調べる
119+ if( encodingName.size() == 0 ){
120+ result = 1208;
121+ }else{
122+ bool allnum = true;
123+ for( int i = 0; i < encodingName.size(); i++ ){
124+ char c = encodingName[i];
125+ if( isdigit( c ) == 0 ){
126+ allnum = false;
127+ break;
128+ }
129+ }
130+ if( allnum ){
131+ int encodingNumber = atoi( encodingName.c_str() );
132+ result = encodingNumber;
133+ }else{
134+ result = 1208;
135+ }
136+ }
137+ }else{
138+ result = 1208;
139+ }
140+ }else{
141+ result = 1208;
142+ }
143+#endif
144+ return getCharsetFromCodepage( result );
145+ }
146+
147+ /**
148+ * コードページの名称から、読込み時の読込単位(バイト)を調べます
149+ * @return テキストファイルからの読み込み単位
150+ */
151+ static int getBytesPerWord( string encoding ){
152+ encoding = toLower( encoding );
153+ if( encoding.compare( "shift_jis" ) == 0 ){
154+ return 1;
155+ }else if( encoding.compare( "euc-jp" ) == 0 ){
156+ return 1;
157+ }else if( encoding.compare( "iso-2022-jp" ) == 0 ){
158+ return 1;
159+ }else if( encoding.compare( "utf-8" ) == 0 ){
160+ return 1;
161+ }else if( encoding.compare( "utf-16le" ) == 0 ){
162+ return 2;
163+ }else if( encoding.compare( "utf-16be" ) == 0 ){
164+ return 2;
165+ }else if( encoding.compare( "utf-16" ) == 0 ){
166+ return 2;
167+ }else if( encoding.compare( "utf-32le" ) == 0 ){
168+ return 4;
169+ }else if( encoding.compare( "utf-32be" ) == 0 ){
170+ return 4;
171+ }else if( encoding.compare( "utf-32" ) == 0 ){
172+ return 4;
173+ }else{
174+ return 1;
175+ }
176+ }
177+
74178 private:
75179 EncodingConverter();
76180
77- static const char *getCharsetFromCodepage( unsigned int codepage )
181+ /**
182+ * 小文字に変換する
183+ * @param text 変換元の文字列
184+ * @return 小文字に変換後の文字列
185+ */
186+ static string toLower( string text )
78187 {
188+ int length = text.length();
189+ int i;
190+ for( i = 0; i < length; i++ ){
191+ text[i] = tolower( text[i] );
192+ }
193+ return text;
194+ }
195+
196+ /**
197+ * コードページ番号から、エンコーディング名を取得する
198+ * @param codepage 調べるコードページ番号
199+ * @return エンコーディング名
200+ */
201+ static string getCharsetFromCodepage( unsigned int codepage )
202+ {
79203 switch( codepage ){
80204 case 932:{
81205 return "Shift_JIS";
@@ -118,6 +242,44 @@
118242 return "";
119243 }
120244
245+ /**
246+ * コードページの名称から、コードページ番号を取得する
247+ * @param name コードページ名称
248+ * @return コードページ番号
249+ */
250+ static unsigned int getCodepageFromCharset( string sname )
251+ {
252+ sname = toLower( sname );
253+ if( sname.compare( "shift_jis") == 0 ){
254+ return 932;
255+ }else if( sname.compare( "euc-jp" ) == 0 ){
256+ return 51932;
257+ }else if( sname.compare( "iso-2022-jp" ) == 0 ){
258+ return 50220;
259+ }else if( sname.compare( "utf-8" ) == 0 || sname.compare( "utf8" ) == 0 ){
260+ return 1208;
261+ }else if( sname.compare( "utf-16le" ) == 0 || sname.compare( "utf16le" ) == 0 ){
262+ return 1202;
263+ }else if( sname.compare( "utf-16be" ) == 0 || sname.compare( "utf16be" ) == 0 ){
264+ return 1200;
265+ }else if( sname.compare( "utf-16" ) == 0 || sname.compare( "utf16" ) == 0 ){
266+ return 1204;
267+ }else if( sname.compare( "utf-32le" ) == 0 || sname.compare( "utf32le" ) == 0 ){
268+ return 1234;
269+ }else if( sname.compare( "utf-32be" ) == 0 || sname.compare( "utf32be" ) == 0 ){
270+ return 1232;
271+ }else if( sname.compare( "utf-32" ) == 0 || sname.compare( "utf32" ) == 0 ){
272+ return 1236;
273+ }else{
274+ return 0;
275+ }
276+ }
277+
278+ /**
279+ * 有効なコンバータかどうかを調べる
280+ * @param converter 調べる対象のコンバータ
281+ * @return コンバータが有効であれば true を、そうでなければ false を返す
282+ */
121283 static bool isValidConverter( iconv_t converter )
122284 {
123285 iconv_t invalid = (iconv_t) - 1;
@@ -124,6 +286,11 @@
124286 return (converter == invalid) ? false : true;
125287 }
126288
289+ /**
290+ * 有効なエンコーディングかどうかを取得する
291+ * @param codepage エンコーディング番号
292+ * @return 有効なエンコーディングであれば true を、そうでなければ false を返す
293+ */
127294 static bool isValidEncoding( unsigned int codepage )
128295 {
129296 // まずUTF-8が有効かどうか
@@ -134,8 +301,8 @@
134301 }
135302 iconv_close( cnv );
136303
137- const char *charset_name = getCharsetFromCodepage( codepage );
138- iconv_t cnv2 = iconv_open( "UTF-8", charset_name );
304+ string charset_name = getCharsetFromCodepage( codepage );
305+ iconv_t cnv2 = iconv_open( "UTF-8", charset_name.c_str() );
139306 if( false == isValidConverter( cnv2 ) ){
140307 iconv_close( cnv2 );
141308 return false;
@@ -142,7 +309,7 @@
142309 }
143310 iconv_close( cnv2 );
144311
145- iconv_t cnv3 = iconv_open( charset_name, "UTF-8" );
312+ iconv_t cnv3 = iconv_open( charset_name.c_str(), "UTF-8" );
146313 if( false == isValidConverter( cnv3 ) ){
147314 iconv_close( cnv3 );
148315 return false;
@@ -152,3 +319,5 @@
152319 }
153320 };
154321 }
322+
323+#endif
旧リポジトリブラウザで表示