質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

1870閲覧

ReadFile funcによる文字列の扱いについて

Weapon

総合スコア106

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/05/30 13:44

編集2018/05/30 16:08

前提・実現したいこと

WinAPIでファイルオープンして文字列を取り出しiostreamを使って吐き出そうとしています。

該当のソースコード

C++

1#define UNICODE 2using namespace std; 3 4#include <iostream> 5#include <Windows.h> 6 7HANDLE file; 8//const wchar_t filename[]=L"15116020_le.csv"; 9const wchar_t filename[] = L"test.txt"; 10LPWSTR lpszbuf; 11DWORD accbytes; 12HGLOBAL hMemory; 13 14 15int main(void) { 16 17 file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 18 19 hMemory = GlobalAlloc(GHND, GetFileSize(file, NULL) + sizeof(wchar_t)); 20 21 if (hMemory == NULL) { 22 cout << L"Failes to get memory" << endl; 23 } 24 lpszbuf = (LPTSTR)GlobalLock(hMemory); 25 26 if (ReadFile(file, lpszbuf, GetFileSize(file, NULL), &accbytes, NULL) != 0) 27 { 28 lpszbuf[accbytes] = '\0'; 29 cout << L"FileRead Successed" << endl; 30 cout << lpszbuf << endl; 31 } 32 33 CloseHandle(file); 34 GlobalUnlock(hMemory); 35 GlobalFree(hMemory); 36 37 return 0; 38}

質問

lpszbufはポインターなのでしょうか?
確保した文字列がメモリ上で断続的だった場合の文字列はどうやって取り出せるのでしょうか?
取り出した値はバイナリになりますか?バイナリならどうやってUNICODE等に変換できるのでしょうか?
あとコード上で使い方がおかしいものがあったらご指摘ください。

【追記】得たバイナリをwcoutで吐き出すためにはどう記述するのが正しいのでしょうか

補足情報

Visual C++
VisualStudio 2017 Community
Windows10 Pro
メモリ:16GB

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ご質問に順番に回答させていただきます。


lpszbuf はポインターなのでしょうか?

ポインターです。LPWSTR は、wchar_t* をtypedef したものです。(実際はもう少しややこしいですが)
Windows Data Types
SDKのヘッダーファイル winnt.h に定義があると思いますので、そちらも見てみましょう。


確保した文字列がメモリ上で断続的だった場合の文字列はどうやって取り出せるのでしょうか?

データ構造によりますが、自分でがんばって探し、取り出すしか無いと思います。


取り出した値はバイナリになりますか?バイナリならどうやってUNICODE等に変換できるのでしょうか?

ReadFileでファイルに書いてある内容そのままで指定のメモリに読み出しますので、その文字列のデータがシフトJISなり、ただのASCIIコードなり、UNICODEでないならば、プログラマー自身が変換する必要があります。

ネイティブのAPIとしてはWideCharToMultiByteMultiByteToWideCharの文字コード変換用APIがありますが、勉強を目的とする以外でこれらを直接使うことは今ではほとんど無いと思います。もっと便利な方法があります。
方法: さまざまな文字列型間で変換する
WideCharToMultiByteらの意味を理解したならば、これらの便利なものを使うことも検討しましょう。


あとコード上で使い方がおかしいものがあったらご指摘ください。

そうですね、気がついたものとしては

  • HANDLE fileLPWSTR lpszbufがグローバル変数である必然性が無い。
  • LPTSTR(TCHAR配列のポインター)とLPWSTR(WCHAR/wchar_t配列のポインター)の意識が混在している。※TCHARはマルチバイト(_MBCS)とUNICODE(_UNICODE)の双方で同じコードで書くためのものです。
  • ReadFile の返り値はBOOL なので、 実行結果の判定として != 0は不適切。
  • GlobalAlloc(GHND...)GlobalLockGlobakUnlockGlobaFree の一連の流れは作法としては正しいのですが、短期間であるならば、Cランタイムのmallocfreeで充分かと思います。GHNDフラグ(GMEM_MOVEABLE|GMEM_ZEROINIT)を指定したGlobalAllocの使用は、昔、PCのメモリが少なかった頃に有用だったもので、現在のPC+普通のアプリケーションで厳密に使い分けるほどに有用なものでもありません。

あと、問題がある部分としては

  • GlobalAllocで取得したhMemoryがNULLであった場合でも、処理を続行している。
  • ReadFile実行後、lpszbuf[accbytes] = '\0'; でNULL文字を埋め込んでいるが、lpszbufwchar_t型のポインターなので、実際に読み込んだバイト長 * 2 の位置に'\0'を挿入してしまっている。(メモリを破壊している) また、L'\0' との表記の方がより適切。
  • coutを使う部分ですが、cout ではLで指定したワイド文字列(UNICODE)は出力できないです。wcout を使う必要があります。

です。

投稿2018/05/30 15:38

編集2018/05/30 15:59
dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Weapon

2018/05/30 16:19 編集

細かい説明ありがとうございます。 参考書を読んでそのまま記述したため理解はしていないのですが lpszbuf[accbytes+2] ?にNULLを突っ込むのはなぜなのでしょうか? あと追記をよろしければお願いします。
dodox86

2018/05/30 16:23

+2ではなく、lpszbuf[accbytes] = '\0'; ですよね? これは、メモリに読み込んだファイルの内容の末尾に'¥0' を入れることで、確実にNULLで終わる文字列にしているのだと思われます。私もよくやります。 (ファイルのデータ中には末端にNULLが無いので) ただ、いずれにせよlpszbu[accbytes]はバグです。ポインタの型を考えると、バイト単位でのaccbytesではインデックスが合いません。
catsforepaw

2018/05/31 03:00

Weapon さん 「参考書」がいつの時代のものか知りませんが、相当古いコードです。`GlobalAlloc`関数は16bit時代の遺物であり、限定的な場面(DDEとかクリップボードとか。DDEもすでに遺物化していますが)でしか使う必要性はありませんし、使う意味もありません。メモリの動的確保ならdodox86さんのご指摘のようにmallocを使ってください。どうしてもWindows APIを使いたいのなら`HeapAlloc`を使ってください。 もう少し新しい参考書をご用意された方がよろしいかと思います。
Weapon

2018/05/31 14:39

mallocで動的確保するときどの型で文字列を確保するのがいいのでしょうか?ANSIかUNICODEかはたまたバイナリか変換は後で処理を追加するとして拡張性を持たせたいです。
dodox86

2018/05/31 14:49

文字列の型は考えず、バイト単位で良いのではないでしょうか。mallocもGlobalAllocも、割り当てるサイズをバイト単位で指定するだけで、メモリ中に入れるデータがどのような型かは意識していません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問