アプリ版:「スタンプのみでお礼する」機能のリリースについて

C++にて,バイナリファイル中から日本語の文字列を取り出す処理に苦心しています.
具体的にはPDFファイルのXMPの記述部のXMLに日本語が含まれる場合なのですが,日本語(この場合はUTF-8ですね)の部分が含まれる部分をfread()で読み出してchar[]型の配列に入れて,printfで表示する(漢字コードの変換処理はとりあえずnkf等でUTF-8→SJISに変換)と文字化けします.

Javaで同じような処理をしたときは,String の変数にUTF-8で日本語の文字列が挿入され,その文字列を扱うことができたのですが,C++での扱いがわかりません.

ご教授よろしくおねがいします.

A 回答 (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 変換して出力する.

これでだいたいいけるのでは?
    • good
    • 0
この回答へのお礼

ソース付きで教えていただき助かりました.
%S による出力で正しく表示されなかったので WideCharToMultiByte() の方で試して,無事日本語が表示されました.

長い間お付き合いして頂きありがとうございました^^.

お礼日時:2007/08/14 15:34

> 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 ファイル内の) 文字列は何ですか?

この回答への補足

詳しい解説等ありがとうございます^^.
元の日本語はそのまま”日本語”になります.
期待している文字列とは違いますね.
こちらでも再度見直してみます.

補足日時:2007/08/14 12:16
    • good
    • 0
この回答へのお礼

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 で読み込まれている文字列でないと変換できないようですので..

いろいろ質問が多くなってしまいますが,良ければご教授よろしくお願いします.

お礼日時:2007/08/14 13:06

>「UTF-8のバイト列としてはただしく切り出せているのでしょうか?」



切り出した部分を (文字列として) printf() で表示するのではなく,
1バイトずつ16進ダンプするか,
または別のバイナリファイルを作って fwrite() で書き出して
バイナリエディタで見てみるといいと思います.

もし自分で16進ダンプを読めないのならば,PDF 内の切り出したい部分と
一緒に投稿してください.

この回答への補足

コマンドプロンプトにて,必要な文字列のみを16進で表示した結果は以下のようになりました.

title: 75, 6e, 74, 69, 74, 6c, 65, 64, : untitled
author: fffffffe, ffffffff, 51, ffffff81, 45, ffffffa3, 5b, ffffff98, 62, 3f, : Q・」[話?

補足日時:2007/08/14 10:59
    • good
    • 0
この回答へのお礼

補足部の補足になってしまいますが,
取り出したタイトルの結果が
75, 6e, 74, 69, 74, 6c, 65, 64
で untitled と表示され,
著者の結果が
fffffffe, ffffffff, 51, ffffff81, 45, ffffffa3, 5b, ffffff98, 62, 3f
でUTF-8の文字列となります.

お礼日時:2007/08/14 11:02

知りたいのは具体的になんでしょうか?



結果が文字化けしているとの事ですが、UTF-8のバイト列としては
ただしく切り出せているのでしょうか?
変換時に化けたのか、元から化けていたのか切り分ける必要があると思います。
とりあえずやったことというのは

UTF-8データの切り出し → nkfでShiftJISに変換 → printf でコンソールに出力

ということでいいですか?

あとJavaでの「文字列の扱い」と同じことをしたいというのであれば
その旨書いた方がアドバイスもくるのではないでしょうか。
たとえばある特定の部分文字列を検索して置換したいとか。

ShiftJISとしてのマルチバイト文字列で処理するのか、
ワイド文字に変換して処理するのかも明確になっていたほうが良いでしょう。

この回答への補足

ご指摘ありがとうございます.

扱う文字列はMFC等でWindowsアプリケーションに表示を行うのでSJISであった方が良いです.
私が一番知りたい部分は,
「UTF-8のバイト列としてはただしく切り出せているのでしょうか?」
の部分になると思います.
そのあたりが上手くいっているのかが自信がありません.
処理的には, fread() で char[] 型の配列に値を格納していっているのですが,そういった処理で切り出しが上手くいくのかな?という部分が一番の疑問であったりします.

分かりづらい文章になってしまい申し訳ありませんが,よろしくお願いします.

補足日時:2007/08/14 09:12
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!