将标记转换为 char* const* 时,使用 boost 标记字符串失败

Posted

技术标签:

【中文标题】将标记转换为 char* const* 时,使用 boost 标记字符串失败【英文标题】:tokenizing string with boost fails when casting tokens to char* const* 【发布时间】:2019-09-17 20:48:49 【问题描述】:

我正在使用boost::tokenizer 在 C++ 中标记一个字符串,然后我想将它传递给execv

考虑以下代码 sn-p(可编译):

#include <iostream>
#include <cstdlib>
#include <vector>
#include <boost/tokenizer.hpp>

// I will put every token into this vector
std::vector<const char*> argc;
// this is the command I want to parse
std::string command = "/bin/ls -la -R";


void test_tokenizer() 
  // tokenizer is needed because arguments can be in quotes
  boost::tokenizer<boost::escaped_list_separator<char> > scriptArguments(
              command,
              boost::escaped_list_separator<char>("\\", " ", "\""));
  boost::tokenizer<boost::escaped_list_separator<char> >::iterator argument;
  for(argument = scriptArguments.begin(); 
    argument!=scriptArguments.end(); 
    ++argument) 

    argc.push_back(argument->c_str());
    std::cout << argument->c_str() << std::endl;
  

  argc.push_back(NULL);


void test_raw() 
  argc.push_back("/bin/ls");
  argc.push_back("-l");
  argc.push_back("-R");

  argc.push_back(NULL);


int main() 
  // this works OK
  /*test_raw();
  execv(argc[0], (char* const*)&argc[0]);
  std::cerr << "execv failed";
  _exit(1);
  */

  // this is not working
  test_tokenizer();
  execv(argc[0], (char* const*)&argc[0]);
  std::cerr << "execv failed";
  _exit(2);

当我运行这个调用test_tokenizer()的脚本时,它会打印'execv failed'。 (虽然它很好地打印了参数)。

但是,如果我将 test_tokenizer 更改为 test_raw 它运行良好。

这一定是一些简单的解决方案,但我没有找到它。

PS.:我还把它放到了一个支持 boost 的在线编译器here。

【问题讨论】:

吹毛求疵:argc 的使用令人困惑,因为它通常用于main() 的第二个参数。 _exit() 的使用是不可移植的;更喜欢(标准和功能相同的)exit()。使用execv() 应该是#include &lt;unistd.h&gt;(char* const*)&amp;argc[0] 的构造在很多层面上都是错误的(至少是有问题的),无法在一条评论中解释。是的,在 C++ 程序中避免使用 C 字符串 (char *),它们只会让你头疼。 ;-) 【参考方案1】:

boost::tokenizer 将令牌按值(默认为std::string)保存在令牌迭代器中。

因此argument-&gt;c_str()指向的字符数组在迭代器被修改时可能会被修改或失效,其生命周期最迟会以argument的生命周期结束。

因此,当您尝试使用 argc 时,您的程序会出现未定义的行为。

如果您想继续使用boost::tokenizer,我建议将标记保留在std::vector&lt;std::string&gt; 中,然后将它们转换为指针数组。

【讨论】:

谢谢。如果那里有更合适的东西,我会很乐意放弃标记器。 无论如何将它们存储在向量中 很好,但是我必须为 execv 构造一个 'const char*' 数组。这是相关的 *** 主题:***.com/questions/5797837/…,所以我更喜欢“最近”或更现代的东西。 @Daniel:这里的问题是从 C++ 中的“适当”和方便的转换,以及 execv()(不是 C 而是 C++)的“适当”的转换。前进的最佳方式是什么取决于真实用例... @DevSolar:是的。你是绝对正确的。假设我有一个字符串变量command。我想要的是执行这个命令。它可能包含参数,甚至在引号内。语言为 C++,操作系统 Linux。 @Daniel 如果它基本上是您要执行的任意 shell 命令,您可以简单地 execl 一个带有 -c 选项的 shell,并将整个 command 作为单个参数传递。这样,您就可以将问题委托给 shell。 (如果您不想在 shell 中创建子进程,请将 exec 添加到 command 的前缀。)如果 command 来自不应执行任意代码的不受信任的来源,请不要这样做.

以上是关于将标记转换为 char* const* 时,使用 boost 标记字符串失败的主要内容,如果未能解决你的问题,请参考以下文章

将 Swift 字符串数组转换为 const char * const *

C++ 我将一个const char*的字符串转换为string出错

在Qt中如何将QString转换为const char*

在 iphone 上为 sqlite 将 json 转换为 const char

将 COLORREF 转换为 const char

将 CString 转换为 const char*