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

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

ただいまの
回答率

90.62%

  • C

    3555questions

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

  • C++

    3323questions

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

  • Win32 API

    215questions

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

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 184

Weapon

score 18

 前提・実現したいこと

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

 該当のソースコード

#define UNICODE
using namespace std;

#include <iostream>
#include <Windows.h>

HANDLE file;
//const wchar_t filename[]=L"15116020_le.csv";
const wchar_t filename[] = L"test.txt";
LPWSTR lpszbuf;
DWORD accbytes;
HGLOBAL hMemory;


int main(void) {

    file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    hMemory = GlobalAlloc(GHND, GetFileSize(file, NULL) + sizeof(wchar_t));

    if (hMemory == NULL) {
        cout << L"Failes to get memory" << endl;
    }
    lpszbuf = (LPTSTR)GlobalLock(hMemory);

    if (ReadFile(file, lpszbuf, GetFileSize(file, NULL), &accbytes, NULL) != 0)
    {
        lpszbuf[accbytes] = '\0';
        cout << L"FileRead Successed" << endl;
        cout << lpszbuf << endl;
    }

    CloseHandle(file);
    GlobalUnlock(hMemory);
    GlobalFree(hMemory);

    return 0;
}

 質問

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

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

 補足情報

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

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


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...)GlobalLock~ GlobakUnlockGlobaFree の一連の流れは作法としては正しいのですが、短期間であるならば、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/31 01:18 編集

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

    キャンセル

  • 2018/05/31 01:23

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

    キャンセル

  • 2018/05/31 12:00

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

    キャンセル

  • 2018/05/31 23:39

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

    キャンセル

  • 2018/05/31 23:49

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

    キャンセル

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

  • ただいまの回答率 90.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C

    3555questions

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

  • C++

    3323questions

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

  • Win32 API

    215questions

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