C ++高效解析

时间:2015-02-27 20:24:49

标签: c++ regex parsing

我正在编写一些自动测试设备(ATE),并且我试图从ATE的示例响应中提取以下值:

DCRE? 1, 
DCRE P, 10.3, (pin1)
DCRE F, 200.1, (pin2)
DCRE P, 20.4, (pin3)

从每一行开始,我只关心引脚和测量结果值。因此,对于上述情况,我希望将以下信息存储在map<std::string, double> results;

results["pin1"] = 50.3;
results["pin2"] = 30.8;
results["pin3"] = 70.3;

我制作了以下代码来解析响应:

void parseResultData(map<Pin*, double> &pinnametoresult, string &datatoparse) {
    char *p = strtok((char*) datatoparse.c_str(), " \n");
    string lastread;
    string current;
    while (p) {
        current = p;
        if(current.find('(') != string::npos) {
            string substring = lastread.substr(1); 
            const char* last = substring.c_str();
            double value = strtod(last, NULL);
            unsigned short number = atoi(current.substr(4, current.size()-2).c_str());
            pinnametoresult[&pinlookupmap[number]] = value;
        }
        lastread = p;
        p = strtok(NULL, " \n");
    }
}

它有效,但效率不高。有没有办法让这个特定案例的功能更有效?我不关心每一行的DCRE或P / F值。我想过使用Boost正则表达式库,但不确定它是否会更有效。

3 个答案:

答案 0 :(得分:2)

为了提高效率,请尽量避免复制。特别是,对子字符串,赋值等的调用可能会对性能造成严重破坏。如果您查看代码,就会发现datatoparse的内容会重复分配到lastreadcurrent,每次开头都会少一行。因此,平均而言,您将原始字符串的一半复制到行数,使该部分成为O(n ^ 2)算法。如果您有三行或四行(甚至不是100行!),这是不相关的,但如果您还有更多行,则性能会迅速下降。

请尝试使用此方法:

string::size_type p0 = 0;
string::size_type p1 = input.find('\n', p0);
while (p1 != string::npos) {
    // extract the line
    string line = input.substr(p0, p1 - p0);

    // move to the next line
    p0 = p1 + 1;
    p1 = input.find('\n', p0);
}

注意:

  • 请注意,算法仍会复制所有输入一次,但每行只复制一次,使其成为O(n)。
  • 由于您拥有该行的副本,因此可以将'\0'作为人工分隔符插入,以便为例如子字符串提供子字符串。 atoi()strtod()
  • 我不是100%确定string::find()的参数顺序而且懒得查找它,但想法是开始在某个位置搜索。查看类似find的函数的各种重载。
  • 处理一条线时,搜索所需部件的索引,然后提取并解析它们。
  • 如果最后有线段(即没有换行的部分线),则必须略微修改循环。创建测试!

答案 1 :(得分:1)

这就是我所做的:

#include <cstdlib>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <iostream>

using namespace std;

struct Pin {
    string something; 
    Pin() {}
};

vector<Pin*> pins = { new Pin(), new Pin(), new Pin() };
typedef unordered_map<Pin*, double> CONT_T;

inline bool OfInterest(const string& line) {
    return line.find("(") != string::npos;
}

void parseResultData(CONT_T& pinnametoresult, const string& datatoparse)
{
    istringstream is(datatoparse);
    string line;
    while (getline(is, line)) {
        if (OfInterest(line)) {
            double d = 0.0;
            unsigned int pinid;
            size_t firstComma = line.find(",")+2; // skip space
            size_t secondComma = line.find(",", firstComma);                        
            istringstream is2(line.substr(firstComma, secondComma-firstComma));            
            is2 >> d;
            size_t paren = line.find("(")+4; // skip pin            
            istringstream is3(line.substr(paren, (line.length()-paren)-1));
            is3 >> pinid;
            --pinid;
            Pin* pin = pins[pinid];
            pinnametoresult[pin] = d;            
        }
    }
}



/*
 * 
 */
int main(int argc, char** argv) {
    string datatoparse = "DCRE? 1, \n"
            "DCRE P, 10.3, (pin1)\n"
            "DCRE F, 200.1, (pin2)\n"
            "DCRE P, 20.4, (pin3)\n";


    CONT_T results;
    parseResultData(results, datatoparse);

    return 0;
}

答案 2 :(得分:0)

这是我的最终结果。不涉及任何复制,但它会破坏字符串。

void parseResultData3(map<std::string, double> &pinnametoresult, std::string &datatoparse) {
    char* str = (char*) datatoparse.c_str();
    int length = datatoparse.size();
    double lastdouble = 0.0;
    char* startmarker = NULL; //beginning of next pin to parse

    for(int pos = 0; pos < length; pos++, str++) {
        if(str[0] == '(') {
            startmarker = str + 1;
            //get previous value
            bool triggered = false;
            for(char* lookback = str - 1; ; lookback--) {
                if(!triggered && (isdigit(lookback[0]) || lookback[0] == '.')) {
                    triggered = true;
                    *(lookback + 1) = '\0';
                }
                else if(triggered && (!isdigit(lookback[0]) && lookback[0] != '.')) {
                    lastdouble = strtod(lookback, NULL);
                    break;
                }
            }
        }
        else if(startmarker != NULL) {
            if(str[0] == ')') {
                str[0] = '\0';
                pinnametoresult[startmarker] = lastdouble;
                startmarker = NULL;
            }
            if(str[0] == ',') {
                str[0] = '\0';
                pinnametoresult[startmarker] = lastdouble;
                startmarker = str + 1;
            }
        }
    }
}