简体   繁体   English

将std :: string转换为char * const *的数组

[英]Turn std::string into array of char* const*'s

I am writing a command shell in C++ using the POSIX api, and have hit a snag. 我正在使用POSIX api在C ++中编写命令shell,并遇到了麻烦。 I am executing via execvp(3), so I somehow need to turn the std::string that contains the command into a suitable array of char* consts*'s that can be passed to: 我正在通过execvp(3)执行,所以我不知何故需要将包含该命令的std :: string转换为可以传递给的一个合适的char * consts *数组:

int execvp(const char *file, char *const argv[]);

I have been racking my brain for hours but I can't think of any realistic or sane way to do this. 我已经绞尽脑汁待了几个小时,但我想不出任何现实或理智的方式来做这件事。 Any help or insight on how I can achieve this conversion would be greatly appreciated. 任何有关如何实现此转换的帮助或见解将不胜感激。 Thank you and have a good day! 谢谢你,有一个美好的一天!

edit: As per request of Chnossos, here is an example: 编辑:根据Chnossos的要求,这是一个例子:

const char *args[] = {"echo", "Hello,", "world!"};
execvp(args[0], args);

Assuming you have a string that contains more than "one argument", you will first have to split the string (using a std::vector<std::string> would work to store the separate strings), then for each element in the vector, store the .c_str() of that string into a const char args[MAXARGS] [or a std::vector<const char*> args; 假设你有一个包含多于“一个参数”的字符串,你首先必须拆分字符串(使用std::vector<std::string>可以存储单独的字符串),然后对于每个元素vector,将该字符串的.c_str()存储到const char args[MAXARGS] [或std::vector<const char*> args; and use args.data() if you don't mind using C++11]. 如果你不介意使用C ++ 11,请使用args.data() Do not forget to store a 0 or nullptr in the last element. 不要忘记在最后一个元素中存储0nullptr

It is critical if you use c_str that the string you are basing that of is not a temporary: const char* x = str.substr(11, 33).c_str(); 如果你使用c_str ,那么你所基于的字符串不是临时字符串是至关重要的: const char* x = str.substr(11, 33).c_str(); will not give you the thing you want, because at the end of that line, the temporary string is destroyed, and its storage freed. 不会给你你想要的东西,因为在该行的末尾,临时字符串被销毁,其存储空间被释放。

If you have only one actual argument, 如果你只有一个实际的参数,

const char* args[2] = { str.c_str(), 0 }; 

would work. 会工作。

Examplary approach: 示例方法:

#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]);

}

Notice that the memory for the cs isn't freed here... in your application you would need to take care of that... 请注意,此处未释放cs的内存...在您的应用程序中,您需要处理...

The number of elements in array can be simply extracted from collection.size() 可以从collection.size()简单地提取数组中的元素数量

I use this: 我用这个:

command_line.hpp: 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: 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

Client code: 客户代码:

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());
}

If the parsing can actually be really complicated, I'd go with something like that: 如果解析实际上可能真的很复杂,我会选择这样的东西:

std::string cmd = "some really complicated command here";
char * const args[] =
{
    "sh",
    "-c",
    cmd.c_str(),
    (char *) NULL
};
execvp(args[0], args);

So the problem is the splitting of the line into individual arguments, and filling the argument vector with the respective pointers? 所以问题是将行拆分为单个参数,并用相应的指针填充参数向量?

Assuming you want to split at the whitespace in the line, you replace whitespace in the string with null-bytes (in-place). 假设您要在行中的空白处拆分,则使用空字节(就地)替换字符串中的空格。 You can then fill the argument vector with pointers into the string. 然后,您可以使用指向字符串的指针填充参数向量。

You will have to write a single loop to go through the string. 您必须编写一个循环来遍历字符串。

You need to decide what the rules will be for your shell and implement them. 您需要确定shell的规则并实现它们。 That's a significant fraction of the work of making a shell. 这是制作shell的重要部分。

You need to write this code, and it's not simple. 你需要编写这段代码,这并不简单。 In a typical shell, echo "Hello world!" 在典型的shell中, echo "Hello world!" has to become { echo , Hello world! 必须成为{ echoHello world! }, while echo \\"Hello world!\\" has to become { echo , "Hello world!" ,而echo \\"Hello world!\\"必须成为{ echo"Hello world!" }. }。 And so on. 等等。

What will " do in your shell? What will ' do? You need to make these decision before you code this part. 什么会"在你的壳呢?会有什么'吗?你需要做这些决定之前,这部分代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM