ファイルパスをディレクトリパスに変換

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
さいとう

ファイルパスをディレクトリパスに変換

#1

投稿記事 by さいとう » 3年前

こんにちは。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: 式が必要です

特に「集約オブジェクトには '{...}' での初期化が必要です」を調べましたが、
ドンピシャの情報がありませんでした。

どなたか原因と修正方法を教えて頂けないでしょうか。

アバター
みけCAT
記事: 6246
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: ファイルパスをディレクトリパスに変換

#2

投稿記事 by みけCAT » 3年前

さいとう さんが書きました: 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);
とするといいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

metaphor

Re: ファイルパスをディレクトリパスに変換

#3

投稿記事 by metaphor » 3年前

下記コードは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: ファイルパスをディレクトリパスに変換

#4

投稿記事 by かずま » 3年前

さいとう さんが書きました:こんにちは。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: ファイルパスをディレクトリパスに変換

#5

投稿記事 by さいとう » 3年前

>みけCATさん
ご助言ありがとうございます。

コード:

const char *fullPathChar = fullPath.c_str();
printf("生成した文字列(char):%s", fullPathChar);
別レスのかずまさんも指摘しておりましたが、
c_str()が文字列ではなくポインタということですね。
承知しました!


>かずまさん
ご助言ありがとうございます。
ご提示くださったサンプルは大変参考になります。
さらに私の理解が不足している部分に補足してくださりありがとうございます。
今、該当のソースが手元にないため月曜に早速試したいと思います!

また、argv[0]のリンクを読みました。
コマンドラインから引数[あり]で該当プログラムを起動するとargv[0]にはフルパスが入り、
コマンドラインから引数[なし]で該当プログラムを起動するとargv[0]にはフルパスが
入らないことがあるということでしょうか?

かずま

Re: ファイルパスをディレクトリパスに変換

#6

投稿記事 by かずま » 3年前

さいとう さんが書きました:また、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: ファイルパスをディレクトリパスに変換

#7

投稿記事 by かずま » 3年前

かずま さんが書きました:・argc == 0 のとき argv[0] = ""
・プログラムから main を呼び出したとき。例えば、
訂正します。
・argc == 0 のとき argv[0] = NULL
・argv > 0 でも、ホスト環境からプログラム名が得られないとき、argv[0] = ""
・プログラムから main を呼び出したとき。例えば、

inemaru
記事: 108
登録日時: 3年前

Re: ファイルパスをディレクトリパスに変換

#8

投稿記事 by inemaru » 3年前

オフトピック
(質問内容と若干話題が逸れるのでオフトピックですが、)
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: ファイルパスをディレクトリパスに変換

#9

投稿記事 by さいとう » 3年前

>かずまさん
返信ありがとうございます。
本題のほうですが、さっそく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: ファイルパスをディレクトリパスに変換

#10

投稿記事 by さいとう » 3年前

>inemaruさん
返信ありがとうございます。
さまざまなやり方と教えて頂き大変勉強になります。
inemaru さんが書きました:
オフトピック
(質問内容と若干話題が逸れるのでオフトピックですが、)
filesystemを使用する方法もあります。
起点となるファイルパスさえあれば、parent_pathメソッドでディレクトリパスは簡単に取得できます。
→inemaruさんがご提示くださったコードを実行し、目的のパスが得られたことを確認しました。
今回のケースのであればこちらのコードも読みやすくてよいと思いました。

お二方がご提示くださったコードをしっかりと見比べし、状況に応じてどちらのコードでも書けるように
理解に落とし込みたいと思います。

貴重なアドバイスありがとうございました!

さいとう

Re: ファイルパスをディレクトリパスに変換

#11

投稿記事 by さいとう » 3年前

>metaphorさん
大変失礼しました。
返答を忘れてしまい、大変申し訳ありませんでした。
また、参考のサンプルソースをご提示くださりありがとうございました。
いろんな書き方があり、勉強になります。
metaphor さんが書きました:下記コードはVS2015で正常動作しました。参考にしてください。
→ご提示くださったソースも含め、どのように実装すれば読みやすく処理効率がよいソースを書けるか
私なりに検討してみたいと思います。
また質問することがあるかもしれませんが、その際はよろしくお願いします。

閉鎖

“C言語何でも質問掲示板” へ戻る