c++:如何将字符串分解(而不是解析)为命令行参数



我正在使用boost::program_options来解析我的命令行。现在,我添加了对批处理执行的支持,通过表示文件的--script参数,每行都包含命令行选项,例如:

--src=""z:devvedssqlexpressRun 1.ved"" --src-kind=bla --yz
--src=z:devvedssqlexpressdb.ebf
--src=z:devvedssqlexpressdb2.mdf
--src=db3
--src=""z:devvedssqliteRun 41 (Run 23).ved""
--src=z:devvedssqlitews_results_db_2012_01_15_18_37_03.db3
--src=z:devvedsmysql10.ved
--src=z:devvedsmysqldb

文件中的每一行都表示我的工具的一次执行,并列出了该特定执行的命令行选项。

问题是,读取脚本文件会产生完整的行,而这些行并没有分解为单独的命令行选项。但是,必须有argcargv才能使用boost::program_options,也就是说,这取决于有人将命令行分解为不同的选项。

我不能简单地用空格分隔,因为有些值包含空格,因此它们用双引号括起来,甚至是嵌套的双引号。

另一方面,我不想在操作系统命令提示符下为每一组命令行选项运行该工具,因为引导程序成本高昂——这也是我首先引入脚本功能的原因。

有没有一种简单的方法可以像操作系统那样将行分解为命令行参数?

谢谢。

好吧,我想好了。这是我的代码:

  string script;
  {
    ifstream file(scriptPath.c_str());
    file.seekg(0, ios::end);
    script.resize(file.tellg());
    file.seekg(0, ios::beg);
    file.read(const_cast<char *>(script.c_str()), script.size());
  }
  boost::replace_all(script, "\", "\\");       // Escape the backslashes
  boost::replace_all(script, "\\"", "\"");   // Except for those escaping the quotes
  boost::trim_right_if(script, is_space_or_zero); // There are extra '' in the string, because the file is read as text, but its length was computed as binary
  vector<string> lines;
  boost::split(lines, script, boost::is_any_of("n"));  // I prefer getting a string line iterator here, the question is how?
  escaped_list_separator<char> sep('\', ' ', '"');
  int res = 0;
  BOOST_FOREACH (const string& line, lines) 
  {
    // reset the command line variables here, since this is like a new execution
    // Tokenize the command line, respecting escapes and quotes  
    tokenizer<escaped_list_separator<char>> tok(line, sep);
    vector<string> args(tok.begin(), tok.end());
    po::variables_map vm;
    po::store(po::command_line_parser(args).options(options).run(), vm);
    res += run(vm);
  }

我正在使用http://www.boost.org/doc/libs/1_48_0/libs/tokenizer/打破界限。效果很好。

Boost文档涵盖了响应文件,包括一个使用它们的简单示例。这将接近你想要的,除了他们还说它"有一些限制",包括空间的解析。

它们还有parse_config_file(),它将从文件中加载选项。在这里,您将放弃在文件中使用与命令行中相同的语法,并且它们所包含的实现只(容易地)支持每个程序调用一个"命令调用"。但我敢打赌,你可以看看他们是如何做到的,并复制一些代码。如果我是你,我可能会调整它以支持.ini语法,如下所示:

[job1]
src=z:devvedssqlexpressRun 1.ved
src-kind=bla
y=
z=
[job2]
src=z:devvedssqlexpressdb.ebf
[another_job]
src=z:devvedssqlexpressdb2.mdf

我敢打赌,这并不是一项可怕的额外工作,而且它还为你运行的每项工作提供了一个明确的名称。Boost自己的parse_config_file()使用部分名称(括号中)作为选项名称的前缀,但这不是必要的,因此为了保持简单的.ini语法,您还可以重新调整它们的用途。

编辑:你想要更简单的吗?好吧。放弃在文件中使用与命令行中相同的引号和空格语法的想法。如果您必须在参数中支持空格,请在选项之间确定一个合适的分隔符,如;。使用std::string::find()或Boost Tokenizer:将类似的东西转换为argc/argv对应该很容易

--src=z:devvedssqlexpressRun 1.ved; --src-kind=bla; --yz
--src=z:devvedssqlexpressdb.ebf
--src=z:devvedssqlexpressdb2.mdf

在第一个示例中,在;上进行拆分以生成argv[1,2,3],并将程序自己的argv[0]复制到"伪"argv[0],然后使用Boost解析选项。

查看以下内容:C++食谱拆分字符串(配方4.6)

示例4-10。拆分分隔字符串

#include <string>
#include <vector>
#include <functional>
#include <iostream>
using namespace std;
void split(const string& s, char c,
           vector<string>& v) {
   string::size_type i = 0;
   string::size_type j = s.find(c);
   while (j != string::npos) {
      v.push_back(s.substr(i, j-i));
      i = ++j;
      j = s.find(c, j);
      if (j == string::npos)
         v.push_back(s.substr(i, s.length( )));
   }
}
int main( ) {
   vector<string> v;
   string s = "Account Name|Address 1|Address 2|City";
   split(s, '|', v);
   for (int i = 0; i < v.size( ); ++i) {
      cout << v[i] << 'n';
   }
}

--

template<typename T>
void split(const basic_string<T>& s, T c,
           vector<basic_string<T> >& v) {
   basic_string<T>::size_type i = 0;
   basic_string<T>::size_type j = s.find(c);
   while (j != basic_string<T>::npos) {
      v.push_back(s.substr(i, j-i));
      i = ++j;
      j = s.find(c, j);
      if (j == basic_string<T>::npos)
         v.push_back(s.substr(i, s.length( )));
   }
}

示例4-11。使用Boost 拆分字符串

#include <iostream>
#include <string>
#include <list>
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace boost;
int main( ) {
   string s = "one,two,three,four";
   list<string> results;
   split(results, s, is_any_of(","));  // Note this is boost::split
   for (list<string>::const_iterator p = results.begin( );
        p != results.end( ); ++p) {
      cout << *p << endl;
   }
}

--

template<typename Seq,
         typename Coll,
         typename Pred>
Seq& split(Seq& s, Coll& c, Pred p,
        token_compress_mode_type e = token_compress_off);

我不能随意分享文本(从书中复制/粘贴是非法的),但这些例子很有解释性。如果你想看课文,你需要参考这本书。

这两个例子取自的配方4.6

C++食谱

Jeff Cogswell,Christopher Diggins,Ryan Stephens,Jonathan Turkanis

出版商:O’Reilly

出版日期:2005年11月

ISBN:0-596-00761-2

最新更新