将 std::string 转换为 char* const* 的数组
Turn std::string into array of char* const*'s
我正在使用 POSIX api 在 C++ 中编写命令 shell,但遇到了障碍。我正在通过 execvp(3) 执行,所以我需要以某种方式将包含命令的 std::string 转换为一个合适的 char* consts* 数组,可以传递给:
int execvp(const char *file, char *const argv[]);
我已经绞尽脑汁好几个小时了,但我想不出任何现实或理智的方法来做到这一点。非常感谢任何有关如何实现此转换的帮助或见解。谢谢你,祝你有美好的一天!
编辑:
根据 Chnossos 的要求,这里有一个例子:
const char *args[] = {"echo", "Hello,", "world!"};
execvp(args[0], args);
假设你有一个包含超过 "one argument" 的字符串,你首先必须拆分字符串(使用 std::vector<std::string>
可以存储单独的字符串),然后对于中的每个元素向量,将该字符串的 .c_str()
存储到 const char args[MAXARGS]
[或 std::vector<const char*> args;
中,如果您不介意使用 C++11,则使用 args.data()
。不要忘记在最后一个元素中存储 0
或 nullptr
。
如果你使用 c_str
,你所基于的字符串不是临时的,这一点很重要:const char* x = str.substr(11, 33).c_str();
不会给你你想要的东西,因为在该行的末尾,临时字符串被销毁,其存储空间被释放。
如果你只有一个实参,
const char* args[2] = { str.c_str(), 0 };
会起作用。
您需要决定 shell 的规则并加以实施。这是制作 shell.
的重要部分
您需要编写这段代码,而且并不简单。在典型的 shell 中,echo "Hello world!"
必须变为 { echo
, Hello world!
},而 echo \"Hello world!\"
必须变为 { echo
, "Hello world!"
}。等等。
"
会在您的 shell 中做什么? '
会做什么?在编写这部分代码之前,您需要做出这些决定。
示例方法:
#include <string>
#include <vector>
#include <cstring>
using namespace std;
int execvp(const char *file, char *const argv[]) {
//doing sth
}
int main() {
string s = "echo Hello world!";
char* cs = strdup(s.c_str());
char* lastbeg = cs;
vector<char *> collection;
for (char *itcs = cs; *itcs; itcs++) {
if (*itcs == ' ') {
*itcs = 0;
collection.push_back(lastbeg);
lastbeg = itcs + 1;
}
}
collection.push_back(lastbeg);
for (auto x: collection) {
printf("%s\n", x);
}
execvp("abc.txt", &collection[0]);
}
请注意,cs
的内存未在此处释放...在您的应用程序中,您需要注意这一点...
数组中的元素个数可以简单地从collection.size()
中提取出来
我用这个:
command_line.hpp:
#pragma once
#include <vector>
#include <string>
namespace wpsc { namespace unittest { namespace mock {
class command_line final
{
public:
explicit command_line(std::vector<std::string> args = {});
explicit command_line(int argc, char const * const * const argv);
int argc() const;
/// @remark altering memory returned by this function results in UB
char** argv() const;
std::string string() const;
private:
std::vector<std::string> args_;
mutable std::vector<char*> c_args_;
};
}}} // wpsc::unittest::mock
command_line.cpp:
#include <wpsc/unittest/mock/command_line.hpp>
#include <algorithm>
#include <sstream>
namespace wpsc { namespace unittest { namespace mock {
command_line::command_line(std::vector<std::string> args)
: args_( std::move(args) ), c_args_( )
{
}
command_line::command_line(int argc, char const * const * const argv)
: command_line{ std::vector<std::string>{ argv, argv + argc } }
{
}
int command_line::argc() const
{
return static_cast<int>(args_.size());
}
char ** command_line::argv() const
{
if(args_.empty())
return nullptr;
if(c_args_.size() != args_.size() + 1)
{
c_args_.clear();
using namespace std;
transform(begin(args_), end(args_), back_inserter(c_args_),
[](const std::string& s) { return const_cast<char*>(s.c_str()); }
);
c_args_.push_back(nullptr);
}
return c_args_.data();
}
std::string command_line::string() const
{
using namespace std;
ostringstream buffer;
copy(begin(args_), end(args_), ostream_iterator<std::string>{ buffer, " " });
return buffer.str();
}
}}} // wpsc::unittest::mock
客户代码:
int main(int argc, char** argv)
{
wpsc::unittest::mock::command_line cmd1{ argc, argv };
// wpsc::unittest::mock::command_line cmd2{ {"app.exe" "-h"} };
some_app_controller c;
return c.run(cmd1.argc(), cmd1.argv());
}
如果解析真的很复杂,我会采用类似的方法:
std::string cmd = "some really complicated command here";
char * const args[] =
{
"sh",
"-c",
cmd.c_str(),
(char *) NULL
};
execvp(args[0], args);
所以问题是将行拆分为单独的参数,并用各自的指针填充参数向量?
假设您想要在行中的空白处拆分,您将字符串中的空白替换为空字节(就地)。然后,您可以使用指向字符串的指针填充参数向量。
您将不得不编写一个循环来遍历字符串。
我正在使用 POSIX api 在 C++ 中编写命令 shell,但遇到了障碍。我正在通过 execvp(3) 执行,所以我需要以某种方式将包含命令的 std::string 转换为一个合适的 char* consts* 数组,可以传递给:
int execvp(const char *file, char *const argv[]);
我已经绞尽脑汁好几个小时了,但我想不出任何现实或理智的方法来做到这一点。非常感谢任何有关如何实现此转换的帮助或见解。谢谢你,祝你有美好的一天!
编辑: 根据 Chnossos 的要求,这里有一个例子:
const char *args[] = {"echo", "Hello,", "world!"};
execvp(args[0], args);
假设你有一个包含超过 "one argument" 的字符串,你首先必须拆分字符串(使用 std::vector<std::string>
可以存储单独的字符串),然后对于中的每个元素向量,将该字符串的 .c_str()
存储到 const char args[MAXARGS]
[或 std::vector<const char*> args;
中,如果您不介意使用 C++11,则使用 args.data()
。不要忘记在最后一个元素中存储 0
或 nullptr
。
如果你使用 c_str
,你所基于的字符串不是临时的,这一点很重要:const char* x = str.substr(11, 33).c_str();
不会给你你想要的东西,因为在该行的末尾,临时字符串被销毁,其存储空间被释放。
如果你只有一个实参,
const char* args[2] = { str.c_str(), 0 };
会起作用。
您需要决定 shell 的规则并加以实施。这是制作 shell.
的重要部分您需要编写这段代码,而且并不简单。在典型的 shell 中,echo "Hello world!"
必须变为 { echo
, Hello world!
},而 echo \"Hello world!\"
必须变为 { echo
, "Hello world!"
}。等等。
"
会在您的 shell 中做什么? '
会做什么?在编写这部分代码之前,您需要做出这些决定。
示例方法:
#include <string>
#include <vector>
#include <cstring>
using namespace std;
int execvp(const char *file, char *const argv[]) {
//doing sth
}
int main() {
string s = "echo Hello world!";
char* cs = strdup(s.c_str());
char* lastbeg = cs;
vector<char *> collection;
for (char *itcs = cs; *itcs; itcs++) {
if (*itcs == ' ') {
*itcs = 0;
collection.push_back(lastbeg);
lastbeg = itcs + 1;
}
}
collection.push_back(lastbeg);
for (auto x: collection) {
printf("%s\n", x);
}
execvp("abc.txt", &collection[0]);
}
请注意,cs
的内存未在此处释放...在您的应用程序中,您需要注意这一点...
数组中的元素个数可以简单地从collection.size()
我用这个:
command_line.hpp:
#pragma once
#include <vector>
#include <string>
namespace wpsc { namespace unittest { namespace mock {
class command_line final
{
public:
explicit command_line(std::vector<std::string> args = {});
explicit command_line(int argc, char const * const * const argv);
int argc() const;
/// @remark altering memory returned by this function results in UB
char** argv() const;
std::string string() const;
private:
std::vector<std::string> args_;
mutable std::vector<char*> c_args_;
};
}}} // wpsc::unittest::mock
command_line.cpp:
#include <wpsc/unittest/mock/command_line.hpp>
#include <algorithm>
#include <sstream>
namespace wpsc { namespace unittest { namespace mock {
command_line::command_line(std::vector<std::string> args)
: args_( std::move(args) ), c_args_( )
{
}
command_line::command_line(int argc, char const * const * const argv)
: command_line{ std::vector<std::string>{ argv, argv + argc } }
{
}
int command_line::argc() const
{
return static_cast<int>(args_.size());
}
char ** command_line::argv() const
{
if(args_.empty())
return nullptr;
if(c_args_.size() != args_.size() + 1)
{
c_args_.clear();
using namespace std;
transform(begin(args_), end(args_), back_inserter(c_args_),
[](const std::string& s) { return const_cast<char*>(s.c_str()); }
);
c_args_.push_back(nullptr);
}
return c_args_.data();
}
std::string command_line::string() const
{
using namespace std;
ostringstream buffer;
copy(begin(args_), end(args_), ostream_iterator<std::string>{ buffer, " " });
return buffer.str();
}
}}} // wpsc::unittest::mock
客户代码:
int main(int argc, char** argv)
{
wpsc::unittest::mock::command_line cmd1{ argc, argv };
// wpsc::unittest::mock::command_line cmd2{ {"app.exe" "-h"} };
some_app_controller c;
return c.run(cmd1.argc(), cmd1.argv());
}
如果解析真的很复杂,我会采用类似的方法:
std::string cmd = "some really complicated command here";
char * const args[] =
{
"sh",
"-c",
cmd.c_str(),
(char *) NULL
};
execvp(args[0], args);
所以问题是将行拆分为单独的参数,并用各自的指针填充参数向量?
假设您想要在行中的空白处拆分,您将字符串中的空白替换为空字节(就地)。然后,您可以使用指向字符串的指针填充参数向量。
您将不得不编写一个循环来遍历字符串。