- ベストアンサー
バイナリファイル中の日本語文字列の取り出し
- C++でバイナリファイル中から日本語の文字列を取り出す処理に苦心しています。
- 日本語の文字列をfread()関数を使って配列に格納し、printfで表示すると文字化けします。
- JavaではString変数にUTF-8で日本語文字列を挿入することができましたが、C++ではどのように扱えばよいですか?
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
> FE FF 51 81 45 A3 5B 98 62 3F > unsigned char* へのキャストで出力した結果です. > title: fe, ff, 65, e5, 67, 2c, 8a, 9e, : e虍,萱 同じ PDF ファイルですか? unsigned char* にキャストしただけで値が変わるはずはないんですが.(謎) まあ,とりあえずそれは置いといて….(いいのか?) > ところで,wchar_t という型でワイド文字列が扱えるようですが, > このまま char 型で読み出すのが良いのか, > wchar_t 型で読み出すのが良いのかも気になります. Windows ということなので,wchar_t は UTF-16LE です. しかしファイル内の文字コードは UT-16BE なので, wchar_t で読み出してもエンディアン反転しなければなりません. そもそも,既に文字列は char[] バッファ内に読み出しているわけですから, そこから1バイトずつ読み出して UTF-16LE に変換していくのがいいでしょう. ただしここでちょっと問題が….文字列の最後をどうやって判定するか, 理解していますか? たぶん XMP か XML の文法で決まっているんだと思いますが. とりあえず,次に UTF-16BE → UTF-16LE 変換のサンプルコードを示しますが, 特定の文字 endChar が現れたときに文字列が終了するものと仮定します. #include <windows.h> #include <wchar.h> #include <stdio.h> #include <locale.h> #define BOM ((wchar_t)0xFEFF) // UTF-16 BOM // 切り出した文字列 (UTF-16BE) が格納されているバッファ char buf[BUFSIZE]; // UTF-16LE 文字列用バッファ wchar_t string[MAXSTRING]; const BYTE *src = (const BYTE*)buf; // UTF16-BE 文字列の読み出し位置 wchar_t *dest = string; // UTF16-LE 文字列の書き込み位置 wchar_t endChar = L'?'; // 終端文字 (不明) wchar_t wc; for(;;) { // src から1文字読む. wc = ((wchar_t)src[0] << 8) | (wchar_t)src[1]; src += 2; if(wc == endChar) break; // 文字列終端 // BOM は読み飛ばす.本当は文字列先頭に限定すべきで,それ以外の // 場所に BOM (本当の名前は ZWNBSP (Zero-Width Non-Breaking Space)) // が現れるとマズイが,今回それはないだろう (たぶん).← 手抜き if(wc != BOM) *dest++ = wc; } *dest = L'\0'; // string を UTF-16LE → SJIS 変換して出力する. // 一番安直な方法は,printf("%S") ('S' は大文字) を使う方法. // (ただし文字列内に変換できない文字があると,それ以後の部分が出力されない.) // それでダメなら WideCharToMultiByte() を使う. // デフォルトのロケール (文字コード:SJIS) を設定する. setlocale(LC_ALL, ""); printf("%S\n", string); // UTF-16LE → SJIS 変換して出力する. これでだいたいいけるのでは?
その他の回答 (3)
- noocyte
- ベストアンサー率58% (171/291)
> author: fffffffe, ffffffff, 51, ffffff81, 45, ffffffa3, 5b, ffffff98, 62, 3f, : Q・」[話? えーとまず ffffff (6桁) が付いているのは,バイトデータを char (符号付) のまま printf("%02x") したからです.char[] を unsigned char* にキャストしてやれば 次のようになります. FE FF 51 81 45 A3 5B 98 62 3F 最初の2バイトが FE FF なのは,UTF-16 (Big Endian) の BOM と思われるので, そのつもりで解読すると, U+FEFF = BOM (UTF-16BE) U+5181 = 冁 U+45A3 = 䖣 U+5B98 = 官 U+623F = 房 途中が文字化けしていますが,最後の「官房」に心当たりありますか? もし仮に,最初の FE の前に FF があるのに読み飛ばされていたとすると, UTF-16 (Little Endian) の可能性があるので, U+FEFF = BOM (UTF-16LE) U+51FF = 凿 U+4581 = 䖁 U+5BA3 = 宣 U+6298 = 折 0x3F これもダメっぽいですね. 最後に,UTF-8 だと仮定すると, FE = (禁止バイト) FF = (禁止バイト) 51 = Q 81 = (この位置では禁止) 45 = E A3 = (この位置では禁止) 5B = [ 98 = (この位置では禁止) 62 = b 3F = ? やっぱり全然ダメダメですね. で,元の (PDF ファイル内の) 文字列は何ですか?
お礼
unsigned char* へのキャストで出力した結果です. title: fe, ff, 65, e5, 67, 2c, 8a, 9e, : e虍,萱 65e5: 日 672c: 本 8a9e: 語 で見てみるとUTF-16になっているようですね.UTF-8だと思っていたのですが,勘違いしていたようですね. ところで,wchar_t という型でワイド文字列が扱えるようですが,このまま char 型で読み出すのが良いのか,wchar_t 型で読み出すのが良いのかも気になります. utf16 → sjis への変換を行う関数等はありますが,もともと wchar_t で読み込まれている文字列でないと変換できないようですので.. いろいろ質問が多くなってしまいますが,良ければご教授よろしくお願いします.
補足
詳しい解説等ありがとうございます^^. 元の日本語はそのまま”日本語”になります. 期待している文字列とは違いますね. こちらでも再度見直してみます.
- noocyte
- ベストアンサー率58% (171/291)
>「UTF-8のバイト列としてはただしく切り出せているのでしょうか?」 切り出した部分を (文字列として) printf() で表示するのではなく, 1バイトずつ16進ダンプするか, または別のバイナリファイルを作って fwrite() で書き出して バイナリエディタで見てみるといいと思います. もし自分で16進ダンプを読めないのならば,PDF 内の切り出したい部分と 一緒に投稿してください.
お礼
補足部の補足になってしまいますが, 取り出したタイトルの結果が 75, 6e, 74, 69, 74, 6c, 65, 64 で untitled と表示され, 著者の結果が fffffffe, ffffffff, 51, ffffff81, 45, ffffffa3, 5b, ffffff98, 62, 3f でUTF-8の文字列となります.
補足
コマンドプロンプトにて,必要な文字列のみを16進で表示した結果は以下のようになりました. title: 75, 6e, 74, 69, 74, 6c, 65, 64, : untitled author: fffffffe, ffffffff, 51, ffffff81, 45, ffffffa3, 5b, ffffff98, 62, 3f, : Q・」[話?
- sakusaker7
- ベストアンサー率62% (800/1280)
知りたいのは具体的になんでしょうか? 結果が文字化けしているとの事ですが、UTF-8のバイト列としては ただしく切り出せているのでしょうか? 変換時に化けたのか、元から化けていたのか切り分ける必要があると思います。 とりあえずやったことというのは UTF-8データの切り出し → nkfでShiftJISに変換 → printf でコンソールに出力 ということでいいですか? あとJavaでの「文字列の扱い」と同じことをしたいというのであれば その旨書いた方がアドバイスもくるのではないでしょうか。 たとえばある特定の部分文字列を検索して置換したいとか。 ShiftJISとしてのマルチバイト文字列で処理するのか、 ワイド文字に変換して処理するのかも明確になっていたほうが良いでしょう。
補足
ご指摘ありがとうございます. 扱う文字列はMFC等でWindowsアプリケーションに表示を行うのでSJISであった方が良いです. 私が一番知りたい部分は, 「UTF-8のバイト列としてはただしく切り出せているのでしょうか?」 の部分になると思います. そのあたりが上手くいっているのかが自信がありません. 処理的には, fread() で char[] 型の配列に値を格納していっているのですが,そういった処理で切り出しが上手くいくのかな?という部分が一番の疑問であったりします. 分かりづらい文章になってしまい申し訳ありませんが,よろしくお願いします.
お礼
ソース付きで教えていただき助かりました. %S による出力で正しく表示されなかったので WideCharToMultiByte() の方で試して,無事日本語が表示されました. 長い間お付き合いして頂きありがとうございました^^.