

C++にて,バイナリファイル中から日本語の文字列を取り出す処理に苦心しています.
具体的にはPDFファイルのXMPの記述部のXMLに日本語が含まれる場合なのですが,日本語(この場合はUTF-8ですね)の部分が含まれる部分をfread()で読み出してchar[]型の配列に入れて,printfで表示する(漢字コードの変換処理はとりあえずnkf等でUTF-8→SJISに変換)と文字化けします.
Javaで同じような処理をしたときは,String の変数にUTF-8で日本語の文字列が挿入され,その文字列を扱うことができたのですが,C++での扱いがわかりません.
ご教授よろしくおねがいします.
No.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 変換して出力する.
これでだいたいいけるのでは?
ソース付きで教えていただき助かりました.
%S による出力で正しく表示されなかったので WideCharToMultiByte() の方で試して,無事日本語が表示されました.
長い間お付き合いして頂きありがとうございました^^.
No.3
- 回答日時:
> 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 で読み込まれている文字列でないと変換できないようですので..
いろいろ質問が多くなってしまいますが,良ければご教授よろしくお願いします.
No.2
- 回答日時:
>「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・」[話?
補足部の補足になってしまいますが,
取り出したタイトルの結果が
75, 6e, 74, 69, 74, 6c, 65, 64
で untitled と表示され,
著者の結果が
fffffffe, ffffffff, 51, ffffff81, 45, ffffffa3, 5b, ffffff98, 62, 3f
でUTF-8の文字列となります.
No.1
- 回答日時:
知りたいのは具体的になんでしょうか?
結果が文字化けしているとの事ですが、UTF-8のバイト列としては
ただしく切り出せているのでしょうか?
変換時に化けたのか、元から化けていたのか切り分ける必要があると思います。
とりあえずやったことというのは
UTF-8データの切り出し → nkfでShiftJISに変換 → printf でコンソールに出力
ということでいいですか?
あとJavaでの「文字列の扱い」と同じことをしたいというのであれば
その旨書いた方がアドバイスもくるのではないでしょうか。
たとえばある特定の部分文字列を検索して置換したいとか。
ShiftJISとしてのマルチバイト文字列で処理するのか、
ワイド文字に変換して処理するのかも明確になっていたほうが良いでしょう。
この回答への補足
ご指摘ありがとうございます.
扱う文字列はMFC等でWindowsアプリケーションに表示を行うのでSJISであった方が良いです.
私が一番知りたい部分は,
「UTF-8のバイト列としてはただしく切り出せているのでしょうか?」
の部分になると思います.
そのあたりが上手くいっているのかが自信がありません.
処理的には, fread() で char[] 型の配列に値を格納していっているのですが,そういった処理で切り出しが上手くいくのかな?という部分が一番の疑問であったりします.
分かりづらい文章になってしまい申し訳ありませんが,よろしくお願いします.
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- UNIX・Linux テキストファイルをページ番号付きでコマンドラインから印刷したい 1 2023/02/22 12:47
- Excel(エクセル) Excelにて、フォルダ内のTextファイルをマクロで統合すると文字化けしてしまう時の解消コード 4 2023/01/01 07:32
- C言語・C++・C# 【C言語】全角文字の配列を、全角のまま1文字ずつ出力する方法 4 2023/05/09 15:08
- Excel(エクセル) エクセルのVBAについて とあるサイトのコードを参考に、CSVの文字化けを直すVBAを作成しているの 7 2022/11/04 14:15
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
- Java VScodeのターミナルの文字化けについて 1 2022/09/27 22:19
- その他(プログラミング・Web制作) awkの文字列比較はPOSIXロケールまたはCロケールにおいてバイナリ値の比較に使えるか gawkな 1 2023/04/22 09:21
- その他(メールソフト・メールサービス) メールソフトを教えてください 1 2023/03/28 23:32
- Visual Basic(VBA) 集めたシートのシート名を変更したい。 下記のコードでサブフォルダにあるファイルのSheet3を集めて 6 2022/08/23 10:38
- その他(プログラミング・Web制作) 変換のプログラムを教えてください。 6 2023/07/01 09:57
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
16進数を2文字ずつ配列に格納し...
-
_tcscpy_s(wcscpy_s)の第二引数...
-
プログラムによく出てくるst...
-
C++で入力した文字列から数字を...
-
char型配列の最大要素数
-
%dなどの違い
-
プログラム問題・・・
-
VB6.0でのバイナリデータの扱い...
-
Shift_JIS(16進)を文字に変換す...
-
C言語のコンパイル時に表示され...
-
文字列操作
-
nullと""、\\0とEOFの違いにつ...
-
int型での文字列の扱いについて
-
VBA-DLLの引数受け渡しについて
-
TCL言語で文字列検索方法を教え...
-
C#でstringをポインタとして渡す
-
[C++]WCHARの1文字目しか表示で...
-
C言語の基礎
-
曜日を格納する配列の大きさ
-
ソースコードの間違い (C言語)
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
TCL言語で文字列検索方法を教え...
-
C#でstringをポインタとして渡す
-
C++で入力した文字列から数字を...
-
nullと""、\\0とEOFの違いにつ...
-
c#で他のアプリの文字入力フォ...
-
WSH(VBS)でJSONの文字列を読み...
-
バイナリファイル中の日本語文...
-
シリアル通信で0x00を送信した...
-
%dなどの違い
-
16進数を2文字ずつ配列に格納し...
-
VB6.0でのバイナリデータの扱い...
-
C++で文字列の右端から特定の文...
-
構造体→文字列→構造体 をする方法
-
_tcscpy_s(wcscpy_s)の第二引数...
-
char型配列の最大要素数
-
VBA-DLLの引数受け渡しについて
-
binaryに対して正規表現を適用...
-
数字の入った配列をファイルへ...
-
C言語の課題で困っています;
-
プログラムによく出てくるst...
おすすめ情報