c ++:解析包含表达式“访问多维数组”的字符串
Posted
技术标签:
【中文标题】c ++:解析包含表达式“访问多维数组”的字符串【英文标题】:c++: parse a string that contains an expression "access to the multidimensinal array" 【发布时间】:2019-07-24 13:28:12 【问题描述】:如何为给定的字符串(如foo[0][1][2][3]
)编写一个辅助方法,将其拆分为数组的名称和索引的集合(例如向量)?在上面的示例中,它应该分别产生foo
和0, 1, 2, 3
。
字符串的格式总是像name[index_0][index_1]....[index_n]
。
事先不知道索引的数量 (n
)。都应该是数字。为简单起见,字符串中不允许有空格。数组的名称 (name
) 可以是任意的。如果字符串不符合指定格式,辅助函数会抛出异常。
性能在这里不是问题。我正在寻找最优雅/最简短的解决方案。
更新
嗯,正则表达式是在第一条评论中提出的。我是该领域的新手,并且经历了用 C++ 完成它的麻烦。请随意简化它。与此同时,@MartinYork 和@Frodyne 提出了两个基于非正则表达式的解决方案。乍一看,正则表达式在这里并没有带来什么迷人之处。在我看来,解决方案似乎并没有更短或更优雅。
#include <stdexcept>
#include <iostream>
#include <string>
#include <regex>
#include <tuple>
std::tuple<std::string, std::vector<int>> helper(std::string str)
// used to validate that the incoming string is in format
// array[0][1][2]
const std::regex rx_validate
"([[:alnum:]]+)((?:\\[[[:digit:]]+\\])+)$";
std::match_results<std::string::const_iterator> match_results;
std::regex_search(str, match_results, rx_validate);
// regex_search array[0][1][2] gives
// match_results[0]: array[0][1][2]
// match_results[1]: array
// match_results[2]: [0][1][2]
if (match_results.size() == 3)
std::vector<int> indices;
// used to extract indices, it is guaranteed that
// numbers are between brackets, no extra checks
// needed
const std::regex rx_index"[0-9]+";
const std::string matchmatch_results[2];
auto it = std::sregex_iterator(match.begin(), match.end(), rx_index);
for (; it != std::sregex_iterator(); ++it)
indices.push_back(std::stoi((*it).str()));
return std::make_tuple(match_results[1], indices);
else
throw std::invalid_argument("Invalid format (" + str + ")");
int main()
const std::string str"a[0][1][2][3][4][5]";
const auto tuple = helper(str);
std::cout << "Name: " << std::get<0>(tuple) << std::endl;
for (int index: std::get<1>(tuple))
std::cout << index << std::endl;
更新2
@rici 建议修改使用正则表达式的算法。它更短更简洁。
我对比较这些算法的性能非常感兴趣。
不会提倡数字 :-) 每个人都应该自己决定。
以下程序编译为g++ -std=c++11 -Ofast
并在i7-8550U
上运行给出:
Regex measurements...
min/max/avg 955/154859/1072.88
Stream measurements...
min/max/avg 722/41252/800.402
#include <iostream>
#include <cstdlib>
#include <cstdint>
#include <limits>
#include <string>
#include <vector>
#include <regex>
#include <tuple>
#include <time.h>
inline uint64_t Timestamp()
timespec time_now;
clock_gettime(CLOCK_REALTIME, &time_now);
return static_cast<uint64_t>(time_now.tv_sec) * 1000000000ULL + time_now.tv_nsec;
std::tuple<std::string, std::vector<int>> helper_stream(std::string const& info)
std::stringstream is(info);
std::string name;
std::vector<int> index;
if (std::getline(is, name, '['))
is.putback('[');
name.erase(std::remove(std::begin(name), std::end(name), ' '), std::end(name));
int value;
char b1;
char b2;
while(is >> b1 >> value >> b2 && b1 == '[' && b2 == ']')
index.push_back(value);
return std::make_tuple(name, index);
std::tuple<std::string, std::vector<int>> helper_regex(std::string str)
static const std::regex strip_prefix"^[[:alpha:]][[:alnum:]]*";
static const std::regex index"\\[([[:digit:]]+)\\]|.";
std::match_results<std::string::const_iterator> match;
if (std::regex_search(str, match, strip_prefix))
auto e = match[0].second;
std::vector<int> indices;
for (auto it = std::sregex_iterator(e, str.end(), index), lim = std::sregex_iterator(); it != lim; ++it)
if ((*it)[1].matched)
indices.push_back(std::stoi((*it)[1]));
else throw std::invalid_argument("Invalid format");
return std::make_tuple(std::string(str.cbegin(), e), indices);
else
throw std::invalid_argument("Invalid format (" + str + ")");
std::string make_str(int n)
std::string str"array";
for (int i = 0; i < n; ++i)
str += "[";
str += std::to_string(std::rand());
str += "]";
return str;
template <typename F>
void measurements(F f)
constexpr int kNumRounds = 1000000;
constexpr int kLength = 3;
std::vector<uint64_t> time_diffs(kNumRounds);
for (int i = 0; i < kNumRounds; ++i)
const std::string strmake_str(kLength);
const auto before = Timestamp();
f(str);
const auto after = Timestamp();
time_diffs[i] = after - before;
uint64_t minstd::numeric_limits<uint64_t>::max();
uint64_t maxstd::numeric_limits<uint64_t>::min();
uint64_t sum0;
for (int i = 0; i < kNumRounds; ++i)
const auto time_diff = time_diffs[i];
if (time_diff < min)
min = time_diff;
if (time_diff > max)
max = time_diff;
sum += time_diff;
std::cout << "min/max/avg " << min << "/" << max << "/" << static_cast<double>(sum) / kNumRounds << std::endl;
int main()
std::cout << "Regex measurements..." << std::endl;
measurements(helper_regex);
std::cout << "Stream measurements..." << std::endl;
measurements(helper_stream);
return 0;
【问题讨论】:
使用正则表达式。 @VladfromMoscow: Now they have two problems!:-) 您能否更准确地定义字符串的格式。它会一直有foo
吗?它总是有四个索引吗?索引总是整数吗?字符串的任何部分是否允许/需要空格?输出的类型和格式有哪些?
@MartinYork,感谢您的评论。我会更新原帖。
@TruLa:我认为正则表达式解决方案可以更优雅或至少更短。例如,coliru.stacked-crooked.com/a/7c6aedb1ecec2e36。请注意,调整正则表达式解决方案很简单,例如,接受表达式中的空格。
【参考方案1】:
这是我主张退回到 C 解析函数的少数几次之一。虽然可以通过正则表达式来完成,但对于如此琐碎的事情来说,这似乎有点沉重。
我会使用 C 函数 sscanf()
std::tuple<std::string, std::vector<int>> ck1(std::string const& info)
int functionStartSize = 0;
int functionNameSize = 0;
char check = 'X';
std::vector<int> index;
if (std::sscanf(info.data(), " %n%*[^\[]%n%c", &functionStartSize, &functionNameSize, &check) == 1 && check == '[')
// Format String: " %n%*[^\[]%n%c"
// ' ': Ignore all leading space.
// %n: Save number of characters of space we dropped.
// %*[^\[]: Lets split this up
// %* scan but don't save to a variable.
// [..] Only the letters we find inside the brackets.
// ^\] Everything except ]
// %n: Save the number of characters we have used to here.
// %c: A character This should now be a '['
// We have correctly found the beginning and end of the name.
int size;
int value;
int offset = functionNameSize;
while(std::sscanf(info.data() + offset, "[%d%c%n", &value, &check, &size) == 2 && check == ']')
// We have found another index
index.push_back(value);
offset += size;
return std::make_tuple(info.substr(functionStartSize, (functionNameSize-functionStartSize), index);
当我第一次编写上述代码时,我假设%n
会像任何其他参数一样计算。不幸的是,它不计入返回值。这使得对每个索引的检查更加模糊,因此我认为使用下面的流不是更好。
流并没有那么糟糕: 字符串的完整副本到字符串流中。但对于小字符串来说不是什么大问题。
std::tuple<std::string, std::vector<int>> ck2(std::string const& info)
std::stringstream is(info);
std::string name;
std::vector<int> index;
if (std::getline(is, name, '['))
is.putback('[');
name.erase(std::remove(std::begin(name), std::end(name), ' '), std::end(name));
int value;
char b1;
char b2;
while(is >> b1 >> value >> b2 && b1 == '[' && b2 == ']')
index.push_back(value);
return std::make_tuple(name, index);
【讨论】:
谢谢。我更新了我原来的帖子。我同意你的看法。我看不到使用正则表达式解决这个问题的好处。 那是std::sscanf()
,假设您包含 C++ 标头 <cstdio>
。不建议将兼容性标头 <stdio.h>
用于新代码。
@TobySpeight 是的。有标志可以检查吗?
no way 可以使用 GCC 捕获该错误。唯一的useful suggestion 是在 Solaris 上使用 Oracle 的 Studio 编译器构建的。 (可悲的是,我不再有任何 Solaris 机器,而且我从来没有那个编译器......)【参考方案2】:
我的回答与 Martin York 的回答非常相似,但我改用了 stl。
#include <iostream>
#include <vector>
#include <string>
#include <tuple>
std::tuple<std::string, std::vector<int>> getNameIndices(std::string s)
std::vector<int> indices;
// The name must end at the first '['
size_t pos = s.find("[");
// If we can't find that, then it isn't a valid string - return empty
if (pos == std::string::npos)
return std::make_tuple("", indices);
// Get the name and remove it from the string
std::string name = s.substr(0, pos);
s.erase(0, pos + 1);
size_t begin = 0;
// Keep looping as long as we can find the start of a new index
while ((pos = s.find("]")) != std::string::npos)
// Begin is the position of the '[', pos is the ']': Get the text between them
std::string tmp = s.substr(begin, pos - begin);
indices.push_back(stoi(tmp));
// Remove the characters that were matched, and update 'begin'
s.erase(0, pos + 1);
begin = s.find("[") + 1;
// Return the name and indices in a vector
return std::make_tuple(name, indices);
void main()
std::string s = "foo[500][12][2][13]";
auto b = getNameIndices(s);
std::cout << "Name: " << std::get<0>(b) << std::endl;
for (int i : std::get<1>(b))
std::cout << "\t" << i << std::endl;
【讨论】:
如果你打算使用 STL,你应该使用std::stringstream
和 operator >>
和 std::getline()
来提取部分。
@MartinYork 看过您的 STL 解决方案后,我同意:这种方式看起来更干净、更好。以上是关于c ++:解析包含表达式“访问多维数组”的字符串的主要内容,如果未能解决你的问题,请参考以下文章