c++:如何将字符串分解(而不是解析)为命令行参数
c++: how to break (not parse) a string into command line arguments?
我正在使用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
文件中的每一行都表示我的工具的一次执行,并列出了该特定执行的命令行选项。
问题是,读取脚本文件会产生完整的行,而这些行并没有分解为单独的命令行选项。但是,必须有argc
和argv
才能使用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
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 如何处理linux终端中带有负号(-)的C++中的命令行参数
- 使用 C++ 将命令行参数拆分为参数/向量
- 如何在OMNET++中添加专门的命令行参数?
- 如何在不传递命令行参数的情况下在 c++ 中设置环境变量
- atoi() 在应用于大型命令行参数时会产生不正确的值
- 是否可以在命令行中将输入参数传递给可执行文件
- 命令行参数,cant 或两个变量
- 在 Windows 中使用 boost::p rogram_options 从命令行参数读取 Unicode 字符
- 如何在 Android/NDK 上将命令行参数从 gradlew.bat 传递到 Clang
- 编写一个将 LLVM IR 文件作为命令行参数的程序
- 通过命令行参数获取llvm ir文件时面临问题
- 有没有办法根据命令行参数定义数组大小?运行时与编译时实例化?
- 从命令行获取参数时出现问题
- 如何在不使用文件扩展名的情况下使用命令行参数打开C++中的文本文件?
- 命令行参数在不到 3 个 LOC 中 int?
- 在命令行参数中使用引号
- 使用参数 + stdout + stderr 从命令行调用 MFC 应用程序
- 在VS2013中使用devenv (C++)传递命令行参数argv
- 二进制数据作为命令行参数