ページ 1 / 1
ファイルパスをディレクトリパスに変換
Posted: 2016年12月22日(木) 16:16
by さいとう
こんにちは。C++で質問があります。
実装したい処理ですが、ファイル(sample.exe)のフルパスを取得し、
最後方にあるファイル名を削除後、ディレクトリ名(sampleDir)を付加したパスを
char型で取得したいと考えています。
コード:
//*****テキストファイルパスの取得
//1 宣言
char fileName[] = "sample.exe";
std::string fullPath = argv[0];
//2 ファイル名削除
int aryCnt = 0;
while (fileName[aryCnt]){
fullPath.pop_back();
aryCnt++;
}
//3 テキストファイルパス追加
aryCnt = 0;
char addPath[] = "sampleDir";
while(addPath[aryCnt]){
fullPath.push_back(addPath[aryCnt]);
aryCnt++;
}
printf("生成した文字列(string):%s", fullPath.c_str());
//4 char型に変換
char fullPathChar[] = fullPath.c_str();
printf("生成した文字列(char):%s", fullPathChar[]);
system("pause");
デバッグを実施したところ、//4 char型に変換の下の行でコンパイルエラーになりました。
出力されたエラーは下の4つです。
error C2440: '初期化中' : 'const char *' から 'char []' に変換できません。
error C2059: 構文エラー : ']'
IntelliSense: 集約オブジェクトには '{...}' での初期化が必要です
IntelliSense: 式が必要です
特に「集約オブジェクトには '{...}' での初期化が必要です」を調べましたが、
ドンピシャの情報がありませんでした。
どなたか原因と修正方法を教えて頂けないでしょうか。
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月22日(木) 18:18
by みけCAT
さいとう さんが書きました:
error C2440: '初期化中' : 'const char *' から 'char []' に変換できません。
そのままの意味ですね。
変数の宣言が間違っています。
さいとう さんが書きました:
error C2059: 構文エラー : ']'
型を書く場所でない式中で[]と書くのはおかしいです。
というわけで、集約オブジェクトとかいうのは私もよくわからないし、他にもやばそうな(入力によってはクラッシュに繋がりそうな)場所もありますが、とりあえず
コード:
char fullPathChar[] = fullPath.c_str();
printf("生成した文字列(char):%s", fullPathChar[]);
を
コード:
const char *fullPathChar = fullPath.c_str();
printf("生成した文字列(char):%s", fullPathChar);
とするといいでしょう。
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月24日(土) 08:45
by metaphor
下記コードはVS2015で正常動作しました。参考にしてください。
コード:
#include <windows.h>
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <fstream> // ifstreamを使うため。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main(int argc, char **argv) {
/*
//int _tmain(int argc, TCHAR** argv) {
// UNICODE文字を標準出力に正しく表示させるためにロケールを設定
_tsetlocale(LC_ALL, _TEXT(""));
TCHAR FullPath[MAX_PATH];
TCHAR* FilePart;
DWORD sz;
sz = GetFullPathName(argv[1], sizeof(FullPath) / sizeof(TCHAR), FullPath, &FilePart);
if (sizeof(FullPath) / sizeof(TCHAR) < sz)
_tprintf(_TEXT("バッファの文字数が足りません %d以上必要¥n"), sz);
else
_tprintf(_TEXT("フルパス¥t%s¥nファイル¥t%s¥n"), FullPath, FilePart);
}
*/
//*****テキストファイルパスの取得 //:::vectorをメタファー(例え)にすれば:::
//1 宣言
char fileName[] = "sample.exe";
std::string fullPath = "d:/dat/bg.png";//argv[0];
//2 ファイル名削除
int aryCnt = 0;
while (fileName[aryCnt]) {
fullPath.pop_back();//:::pop_back
aryCnt++;
}
std::cout << "000::" << fileName << "\n\n";//:::check
//3 テキストファイルパス追加
aryCnt = 0;
char addPath[] = "sampleDir";
while (addPath[aryCnt]) {
fullPath.push_back(addPath[aryCnt]);//:::push_back
aryCnt++;
}
printf("111::生成した文字列(string):%s\n\n", fullPath.c_str());
// ファイルを開く。
//ifstream ifs(fullPath.c_str());
//char fullPathChar[] = "ABCDEF";// fullPath.c_str(); :::可能です
//printf("生成した文字列(char):%s\n\n", fullPathChar);
std::string str("test.exe");// ローカル変数としてstring 型のオブジェクト str の宣言
//コンストラクタ引数に文字列リテラルを指定
std::cout << "AAA::" << str << "\n\n";//:::check
//4 char型に変換
std::string fullPathChar_str = fullPath.c_str();
std::cout << "BBB::" << fullPathChar_str << "\n\n";//:::check
//char fullPathChar[] = fullPath.c_str();//:::不可能
/////////////////////////////////////////////
for (int i = 0; i < 8; ++i)
std::cout << fullPathChar_str[i] << ":";
/////////////////////////////////////////////
//printf("222::生成した文字列(char):%s", fullPathChar);//[]);
system("pause");
}
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月24日(土) 18:07
by かずま
さいとう さんが書きました:こんにちは。C++で質問があります。
実装したい処理ですが、ファイル(sample.exe)のフルパスを取得し、
最後方にあるファイル名を削除後、ディレクトリ名(sampleDir)を付加したパスを
char型で取得したいと考えています。
char型は、1バイトなのでパスは入りません。
パス名という文字列を入れるには、char の配列を用意する必要があります。
さいとう さんが書きました:特に「集約オブジェクトには '{...}' での初期化が必要です」を調べましたが、
ドンピシャの情報がありませんでした。
集約オブジェクトとは、配列や構造体(クラス)などの変数のことです。
初期化には { ... } が必要だということです。例えば、
int a[] = { 3, 1, 4 };
struct Hoge t = { "bob", 24 };
char s[] = { 'a', 'b'. 'c', '\0' };
例外は、文字列リテラルによる初期化です。
char s[] = "abc";
char fullPathChar[] = fullPath.c_str(); の場合、
fullPath.c_str() は文字列リテラルではなくポインタです。
さいとう さんが書きました:どなたか原因と修正方法を教えて頂けないでしょうか。
修正方法の一例
コード:
#include <string> // std::string
#include <stdio.h> // printf
#include <string.h> // strcpy
#include <stdlib.h> // system
int main(int argc, char *argv[])
{
std::string fullPath = argv[0]; // fullPath = "C:\\work\\sample.exe"
size_t n = fullPath.rfind('\\'); // n = 7 (パス名の区切りを探した)
if (n == std::string::npos) return 1; // '\\' が見つからなかった場合
fullPath.resize(n + 1); // fullPath = "C:\\work\\"
fullPath += "sampleDir"; // fullPath = "C:\\work\\sampleDir"
printf("生成した文字列(string):%s\n", fullPath.c_str());
char fullPathChar[1024]; // 十分な大きさの char の配列を用意した
strcpy(fullPathChar, fullPath.c_str());
printf("生成した文字列(char[]):%s\n", fullPathChar);
system("pause");
}
Windows のエクスプローラでアイコンをクリックしてプログラムを起動すると、
argv[0] にはそのプログラムのフルパスが入るようですが、
コマンドラインから起動するとそうなりません。
[迷信] argv[0] はプログラム名
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月25日(日) 00:40
by さいとう
>みけCATさん
ご助言ありがとうございます。
コード:
const char *fullPathChar = fullPath.c_str();
printf("生成した文字列(char):%s", fullPathChar);
別レスのかずまさんも指摘しておりましたが、
c_str()が文字列ではなくポインタということですね。
承知しました!
>かずまさん
ご助言ありがとうございます。
ご提示くださったサンプルは大変参考になります。
さらに私の理解が不足している部分に補足してくださりありがとうございます。
今、該当のソースが手元にないため月曜に早速試したいと思います!
また、argv[0]のリンクを読みました。
コマンドラインから引数[あり]で該当プログラムを起動するとargv[0]にはフルパスが入り、
コマンドラインから引数[なし]で該当プログラムを起動するとargv[0]にはフルパスが
入らないことがあるということでしょうか?
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月25日(日) 15:02
by かずま
さいとう さんが書きました:また、argv[0]のリンクを読みました。
コマンドラインから引数[あり]で該当プログラムを起動するとargv[0]にはフルパスが入り、
コマンドラインから引数[なし]で該当プログラムを起動するとargv[0]にはフルパスが
入らないことがあるということでしょうか?
リンク先で言っているのは、プログラム名にならない場合についてです。
・argc == 0 のとき argv[0] = ""
・プログラムから main を呼び出したとき。例えば、
コード:
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc == 1) {
char *v[] = { "aaa", "bbb", "ccc" };
return main(3, v);
}
printf("argv[0] = %s\n", argv[0]);
return 0;
}
リンク先では、フルパスになるかどうかは言っていません。
わたしが試してみたところ、
argv[0] がプログラム名になって、かつフルバスになるのは、
・Windows のエクスプローラでアイコンをクリックしてプログラムを起動したとき
・コマンドラインから、フルパスでプログラムを起動したとき。例えば
C:\tmp>C:\work\sample.exe (C:\tmp> はプロンプト)
argv[0] がプログラム名になって、かつフルバスにならないのは、
C:\tmp>sample または、C:\tmp>sample.exe で起動したとき。
(いずれの場合も、環境変数PATH に C:\work が入っていると仮定します)
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月25日(日) 16:57
by かずま
かずま さんが書きました:・argc == 0 のとき argv[0] = ""
・プログラムから main を呼び出したとき。例えば、
訂正します。
・argc == 0 のとき argv[0] = NULL
・argv > 0 でも、ホスト環境からプログラム名が得られないとき、argv[0] = ""
・プログラムから main を呼び出したとき。例えば、
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月25日(日) 20:23
by inemaru
オフトピック
(質問内容と若干話題が逸れるのでオフトピックですが、)
filesystemを使用する方法もあります。
起点となるファイルパスさえあれば、parent_pathメソッドでディレクトリパスは簡単に取得できます。
環境:VS2015 / Windows10
コード:
/*
* ・ファイルパスをディレクトリパスに変換
//*/
#include <iostream>
#include <string>
#include <filesystem>
int main(int argc, char **argv)
{
// 名前空間名省略用に宣言
namespace fsys = std::experimental::filesystem::v1;
// カレントパス取得
const fsys::path curPath = fsys::current_path();
// ファイルパス追加
auto fullPath = curPath / "subDir" / "sampleData.dat";
// 表示
std::cout << "カレントパス:" << std::endl
<< curPath << std::endl;
std::cout << "フルパス:" << std::endl
<< fullPath << std::endl;
std::cout << "親ディレクトリ:" << std::endl
<< fullPath.parent_path() << std::endl;
std::cout << "char型で扱う:" << std::endl
<< fullPath.generic_string().c_str() << std::endl;
/*
std::wcout << L"wchar型で扱う:" << std::endl
<< fullPath.generic_wstring().c_str() << std::endl;
//*/
return 0;
}
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月26日(月) 14:47
by さいとう
>かずまさん
返信ありがとうございます。
本題のほうですが、さっそくNo4で提示頂いたコードを試し、
正常にパスを取得したことを確認しました。
また、参考URLの件ですが、かずまさんが検証した内容を
こちらでも検証しました。
かずま さんが書きました:
わたしが試してみたところ、
argv[0] がプログラム名になって、かつフルパスになるのは、
・Windows のエクスプローラでアイコンをクリックしてプログラムを起動したとき
・コマンドラインから、フルパスでプログラムを起動したとき。例えば
C:\tmp>C:\work\sample.exe (C:\tmp> はプロンプト)
argv[0] がプログラム名になって、かつフルパスにならないのは、
C:\tmp>sample または、C:\tmp>sample.exe で起動したとき。
(いずれの場合も、環境変数PATH に C:\work が入っていると仮定します)
こちらの検証結果
◆argv[0] がプログラム名になって、かつフルパス
コード:
C:\usr>C:\usr\SampleApp.exe
argv[0] = C:\usr\SampleApp.exe
続行するには何かキーを押してください . . .
あと、アイコンクリックでも同様プログラム名になって、かつフルパスでした。
◆argv[0] がプログラム名になって、かつフルパス
コード:
C:\usr>SampleApp.exe
argv[0] = SampleApp.exe
続行するには何かキーを押してください . . .
argv[0]からプログラム名を含めたフルパスを取得しようと
する場合はいろいろ気をつけたほうがよさそうですね。
今後の実装では念頭に入れてます。
ご助言ありがとうございます!
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月26日(月) 14:56
by さいとう
>inemaruさん
返信ありがとうございます。
さまざまなやり方と教えて頂き大変勉強になります。
inemaru さんが書きました:オフトピック
(質問内容と若干話題が逸れるのでオフトピックですが、)
filesystemを使用する方法もあります。
起点となるファイルパスさえあれば、parent_pathメソッドでディレクトリパスは簡単に取得できます。
→inemaruさんがご提示くださったコードを実行し、目的のパスが得られたことを確認しました。
今回のケースのであればこちらのコードも読みやすくてよいと思いました。
お二方がご提示くださったコードをしっかりと見比べし、状況に応じてどちらのコードでも書けるように
理解に落とし込みたいと思います。
貴重なアドバイスありがとうございました!
Re: ファイルパスをディレクトリパスに変換
Posted: 2016年12月26日(月) 17:43
by さいとう
>metaphorさん
大変失礼しました。
返答を忘れてしまい、大変申し訳ありませんでした。
また、参考のサンプルソースをご提示くださりありがとうございました。
いろんな書き方があり、勉強になります。
metaphor さんが書きました:下記コードはVS2015で正常動作しました。参考にしてください。
→ご提示くださったソースも含め、どのように実装すれば読みやすく処理効率がよいソースを書けるか
私なりに検討してみたいと思います。
また質問することがあるかもしれませんが、その際はよろしくお願いします。