- ベストアンサー
C言語でファイルをよんだあとの料理方法
基本的に.NETしか知りませんので、C言語でAPIのReadFile使用して ファイルを一気に全部バッファ読み込んだあと、 バッファから文字を一つずつ取り出すにはどうしたら いいでしょうか? Unicodeを使用することが前提で結構です。 なんか、根本的にわかってませんので。。。 よろしくお願いします。 C#とかなら、文字をインデックスで取得できますので、 C/C++となるとまったく、イメージできません。。。 ポインタを移動することで一バイトずつ操作することくらいは理解してます。
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
うーん。それはC言語の基礎ですが C言語は文字列の終端に0をいれます。 buffer[wReadSize/sizeof(wchar_t)] = 0; と同じことですか。 明示的にUNICODEのNULL文字 L'\0'としています。 別に文字列として扱わないなら必要ありません。 しかし今回は MessageBox関数に使用する為、bufferを文字列として扱う必要がある為 NULL文字をいれました。 mallocでwReadSize + sizeof(wchar_t) としているのも NULL文字をいれるため文字数+1文字しているのです。 どんな言語でもそうですが、文字列を扱うには2通りの方法があって 今回のように文字列の終端に終端を表すNULL文字をいれる仕組みと 最初に文字数があってその後ろに文字列がくる仕組みです。 でなければコンピューターはどこまでが文字列なのか判断出来ません。 >この部分ってUNICODEファイルを読むときで必ず必要でしょうか? >保険のようものでしょうか? つまりUNICODEかどうかは関係ありませんし保険でも無く 文字列として扱うなら必ず必要ということです。
その他の回答 (8)
- sha-girl
- ベストアンサー率52% (430/816)
HANDLE hFile = CreateFile( "text.txt", GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL); DWORD wReadSize = GetFileSize(hFile , NULL); wchar_t* buffer = (wchar_t*)malloc( wReadSize + sizeof(wchar_t) ); DWORD dw; ReadFile( hFile , buffer , wReadSize , &dw , NULL ); buffer[wReadSize/sizeof(wchar_t)] = L'\0'; ::MessageBox( NULL , buffer , L"test" , MB_OK ); free(buffer); CloseHandle(hFile);
お礼
そのまま回答書いてくださいましたね<(_ _)> ファイル読めてますね。ありがとうございます。 > buffer[wReadSize/sizeof(wchar_t)] = L'\0'; この部分ってUNICODEファイルを読むときで必ず必要でしょうか? ほかのコードのファイルでも必要? それとも、余計なものを確保される可能があるので、保険のようものでしょうか?
- xcrOSgS2wY
- ベストアンサー率50% (1006/1985)
開いているファイルのバイト数はいくつでしょうか。可能であれば、ファイル内の各バイトの値も教えてください。 プログラム内で使用している各変数に最終的に設定されている値と、ファイルを読み込んだ後のバッファ内の値はいくつでしょうか。(ハンドルの値、ポインタの値は不要です。)
お礼
補足がもうできませんので、お礼のとこで^^ やっているうちに漢字が取れました。 ありがとうございます。 依然として余計な文字が読み込まれますが。。 各値も漏れてましたね。 aあ 以外の化けている値は以下です。 65021 65021 43947 43947 43947 43947 65262 もしかして、UNICODEの制御文字? 文字コードが理解できてません。。。
補足
メモ帳でUNIOCDE保存したファイル サイズ 6 バイト 内容 aあ wReadSize 3 dw 6 です。 なにおわかりでしょうか? すいません。
- xcrOSgS2wY
- ベストアンサー率50% (1006/1985)
回答No.5の補足にあるコードですが、GetFileSizeが返す値は「バイト数」で、Unicode文字の文字数ではありません。ですので、Unicode文字の文字数を得るにはsizeof(wchat_t)で割ってください。(注:ヘッダ込みの値になります。)
補足
ご回答ありがとうございます。 なんとなくそんな気もしましたので、 テストとして固定で 2 を入れてもやってみたのです。 なんか、とくに結果がかわりませんで、そのまま ほうっておきました。 おっしゃる通りですね。 それでも結果が変わらないのはなんででしょうか。。? それと、余計な文字を読み込み以外にポインタ移動や 配列のインデックス指定で漢字が取れません。。 根本的に間違ってますでしょうか?
- sha-girl
- ベストアンサー率52% (430/816)
何を悩んでいるのかわかりませんが、 例えばテキスト文書が1000文字なら wchar_t buffer[1000]; とでも用意してください。 (wchar_tはワイド文字の型です。U NICODEを宣言しているならTCHARでも同じです。) BOOL ReadFile( HANDLE hFile, // ファイルのハンドル LPVOID lpBuffer, // データバッファ DWORD nNumberOfBytesToRead, // 読み取り対象のバイト数 LPDWORD lpNumberOfBytesRead, // 読み取ったバイト数 LPOVERLAPPED lpOverlapped // オーバーラップ構造体のバッファ ); がReadFileの定義ですが DWORD dw; ReadFile( hFile , buffer , sizeof(wchar_t) * 1000 , &dw , NULL ); これで buffer[1]・・・あ buffer[2]・・・a buffer[3]・・・b buffer[4]・・・い buffer[5]・・・c ・ ・ ・ とはいっているはずです。 ところでUNICODE-UCS2は全ては2バイトです。ですから半角とか全角とかは関係ありません。 まずメモ帳等でUNICODE形式を保存すればわかりますがテキストファイル の最初にFF FE またはFE FFがつきます。これはLittleEndianかBigEndianの指標で Windowsの標準はlittle endianです。 それを考慮してbuffer[0]からではなくbuffer[1]からにしています。
お礼
ご回答ありがとうございます。 > ところでUNICODE-UCS2は全ては2バイトです。ですから半角とか全角とかは関係ありません。 全くおっしゃる通りです。 私のそのように考えていました。 .NETではそのようになってますし。 しかしいざCでやってみるとあれ?なんか。。 え?うん~。。って感じでどうにもできませんでした^^; 今からもう一度やってみます。 また結果を報告いたします。
補足
以下のようにやってみましたが、やはりできません^^; ステップ実行すると、確かにbufferに読み込まれるんですが、 最後に意味不明の文字が入ってしまいます。 ファイルはメモ帳でUNICODEで保存しました。 aあ の2文字が入ってます。 bufferの値は正確に再現できません。化けてますので、おそらく投稿するとさらに違う変な文字になったりしてます。 bufferのポインタを移動するとやはり全角の文字は取れません。。 配列でもやってみましたが、同じ結果でした。 すいません。どんなところがおかしいでしょうか? きっとおかしいでしょうが、発見できませんです。。。 HANDLE hFile = CreateFile( ファイル名, GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL); DWORD wReadSize = GetFileSize(hFile , NULL); wchar_t* buffer = (wchar_t*)malloc( sizeof(wchar_t) * wReadSize ); DWORD dw; ReadFile( hFile , buffer , sizeof(wchar_t) * wReadSize , &dw , NULL ); free(buffer); CloseHandle(hFile);
- xcrOSgS2wY
- ベストアンサー率50% (1006/1985)
読み込むファイルのエンコードはShift-JIS、UCS-2(いわゆるユニコード)、UTF-8、UTF-16のどれでしょうか。 参考: http://www.atmarkit.co.jp/fxml/askxmlexpert/024utf/24utf.html (マイクロソフトのCコンパイラを使うものと仮定します。) Shift-JISの場合、2バイト文字の1バイト目であるかどうかは_ismbblead()関数で判定できます。_istlead()は_UNICODEが定義されている場合常にfalseを返すので、今回の場合おそらく使用できません。(_MBCSが定義されている場合_ismbblead()を返します。) UCS-2(ユニコード)の場合、全角文字でも半角文字でも常に2バイトですから、1バイト目であるかどうかの判定は必要ありません。 UTF-8/16はよく知らないので省略します。
お礼
ご回答ありがとうございます。
- ttyp03
- ベストアンサー率28% (277/960)
#2です。 例で書いたソースはあくまでもバイトごとに読み込むサンプルとして挙げただけですので、あとはご自分で作ってもらえればと思ったのですが・・・ とりあえずUnicodeの細かい処理は省いて(良くわからないので)1文字=4バイトとした場合のソースを書いておきます。 単純に文字で表示させます。 for( i = 0; i < len; i+=4 ){ printf( "%.4s\n", &p[i] ); } 一旦バッファに取り込むなら。 char w[5]; for( i = 0; i < len; i+=4 ){ strncpy( w, &p[i], 4 ); printf( "%s\n", w ); } こんなんでよろしいでしょうか? 的外れなら補足お願いします。
補足
へんな質問ですいません。 全角、半角が混ざっているファイルですので、 forのループごとに同じ量の移動ができないです。 全角かどうかの判定がしたいですが、 ソース内部でUNICODEを#defineしているため _istleadでしたっけ?が使えないようです。 どんな方法でもいいですが、 あabいc のような文字の場合 あ a b い c と取り出したいだけです。
- ttyp03
- ベストアンサー率28% (277/960)
例えば char* p に読み込んだとしたら、p[0] が0バイト目のデータです。 全データを16進で表示するサンプルを書いておきます。 len は ReadFile の4番目の引数の値(読み込んだバイト数)と思ってください。 例: for( i = 0; i < len; i++ ){ printf( "%02x\n", p[i] ); } もしくはポインタを使うならこうなります。 for( i = 0; i < len; i++, p++ ){ printf( "%02x\n", *p ); }
お礼
ご回答ありがとうございます。 すいませんです。いい忘れました。 全角、半角は混ざったファイルで 読み込んだ内容を"文字"として一つずと取り出すには どうしたらいいでしょうか。書いていただいたサンプルがもしすでにそうなっていれば もうすこし解説をいただけないでしょうか? どうしても、一バイトずつ表示しているように見えてしまいます。 これでは2バイト文字は。。。? すいません。
- 6dou_rinne
- ベストアンサー率25% (1361/5264)
ファイルの形式がどうなっているかにもよりますが、一文字ずつ取り出したいのならバッファから1バイトずつ配列にでもコピーすればいいのではないでしょうか。
お礼
ご回答ありがとうございます。
お礼
ありがとうございました。 大変勉強になりました。 文字列の終端のことで気になったのは今回おそらく私のどっかのミスで余計な文字が 読み込まれたので、それをきるために、そのようにされたのかなと思いましたので 質問させていただきました。 解説ですっきりしました。 .NETから入ってnative系の戻るってともてつらいです。。。 .NETが基本的なところ全部やってくれますので。。。スキルが上がりませんね。 これから出てくるPGならもっと不幸かもしれません(ある意味でです)。 #私は.NETが優れていると思います。