[英]Parsing a string in C++
我正在尝试为图类创建一个构造器,该构造器接受字符串作为参数并使用它来构建图。
字符串的格式如下: |vertex list|Edges list|
例如|1,2,3,4,15|(1->2),(3->2),(4->15)|
这个想法是构造函数将从字符串中获取值,然后知道执行以下操作(将顶点插入顶点列表,然后将边插入边列表):
addVertex(1)
addVertex(2)
addVertex(3)
addVertex(4)
addVertex(15)
addEdge(1,2)
addEdge(3,2)
addEdge(4,15)
我只是做了几个“ for”循环来扫描字符串,但是我不知道该如何处理双(或更多)位数。 我开始想象各种严重复杂的循环,我想知道这里是否有人可以与我分享更多更聪明的方法来提取和使用此数据。
您似乎对整个事情不知所措。 分解成碎片...任务。 您正在尝试做的似乎是这里的单独功能。
这是5个功能,或多或少。
您想基于管道(|)进行标记化,因此请基于管道获取一个子字符串,并将每一侧传递给适当的解析器,并以逗号进行解析,依此类推。
不会为您做这件事,但希望我能使您朝正确的方向思考。 学习编程并不仅仅是关于某种特定的语言,而是关于改变思维方式。
我以前从未使用过它,但是有一个Boost 令牌生成器类。 您可以轻松地将其分解为组件,而无需进行所有的for循环。
您可以使用stringstream
并使用流提取运算符来获取整数。
string s("12 34");
istringstream ss(s);
int x, y;
ss >> x >> y;
由于这是家庭作业,因此我敦促您探索各种可能性,并为自己找出完整的代码。
无需为您做作业,这将为您提供良好的开端。 我已经为您提供了解析顶点列表的基本工作流程,您应该可以自己创建边缘列表。 我也将错误检查留给您,例如在parseVertex()中,当您遇到无效字符时,您可能希望给出错误。
void skipWhiteSpace(const char*& first , const char* last) {
// do whatever need to be done to skip white space
}
// parse integer only, no error checking is performed
bool parseVertex(const char*& first , const char* last) {
skipWhiteSpace(first, last);
const char* numBegin = first;
for (; first != last && ::isdigit(static_cast<unsigned char>(*first));
++first) {}
if (numBegin != first) {
std::cout << "addVertex(" << std::string(numBegin, first) << ")" << std::endl;
return true;
}
return false;
}
bool parseComma(const char*& first , const char* last) {
skipWhiteSpace(first, last);
if (first != last && ',' == *first) {
++first;
return true;
}
return false;
}
// VL := V (, VL)
// a vertex list (VL) is a vertex (V) followed by a comma than another vertex list
bool parseVertexList(const char*& first, const char* last) {
if (parseVertex(first, last)) {
parseComma(first, last) && parseVertexList(first, last);
return true;
}
return false;
}
}
void test() {
const char* str = "1,2,3,4,15";
parseVertexList(str, str + sizeof("1,2,3,4,15"));
}
使用递归下降技术解析此类事情非常简单(尽管乏味)。 想法是将要解析的语言分为逻辑单元,然后编写一个函数来解析每个这些单元。
如果我们在示例中计算“ | 1,2,3,4,15 |(1-> 2),(3-> 2),(4-> 15)|” 整个字符串是一个“多边形”,我们将编写parsePolygon(),看起来像这样:
void parsePolygon (Buffer& b)
{
parseVertices (b);
parseEdges (b);
}
假设Buffer是贯穿字符串的类。 您将需要两个基本操作:不消耗下一个字符便查看下一个字符,并消耗下一个字符。
parseVertices可能看起来像这样:
void parseVertices (Buffer& b)
{
if (b.peek() != '|') { /* error */ }
b.consume (); // burn the '|'
parseVertexList (b);
if (b.peek() != '|') { /* error */ }
b.consume (); // burn the '|'
}
显然,您想更好地处理错误。 如果流遇到任何错误,则需要将错误代码传递到调用堆栈或引发异常。
另外两个示例... parseVertexList和parseNumber可能看起来像这样:
void parseVertexList (Buffer& b)
{
addVertex (parseNumber (b));
while (b.peek() == ',')
{
b.consume (); // eat the comma
addVertex (parseNumber (b));
}
}
int parseNumber (Buffer& b)
{
char accum[80] = { '0' }; // sensible default in case of failure
int accumPos = 0;
while (isDigit (b.peek())
{
accum[accumPos++] = b.consume();
}
return atoi(accum);
}
这都是非常快速和肮脏的,但是希望它可以使您了解该技术的工作原理。 您可以将处理与解析混合在一起,如上所示,其中parseVertexList函数实际上为您执行addVertex调用。
我认为这确实是手动解析的最简单方法之一。 理想情况下,我们总是能够使用生成的解析器,例如boost spirit或pyparsing或lex / yacc,但是生活并不总是那么好,尤其是对于家庭作业。
另外,我想值得注意的是,在某些解析情况下,上述技术可能会显得过大。
使用stringstream
。 请注意该页面上使用istringstream
读取数字的istringstream
。
我当然会以此为借口来鼓舞精神 ! 为这种小语言编写一些语法应该很有趣。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.