解析数字字符串

解析数字字符串

问题描述:

我需要解析包含由单个空格字符分隔的4个浮点数字的几个C样式字符串(大约500k)。下面是一个字符串的例子:解析数字字符串

“90292 5879 89042.2576 5879”

我需要这些数字存储在代表两个点的两个结构。考虑到字符串可以在解析时被修改,并且99.99%的数字只是无符号整数,那么最快的方法是什么?

以下是我目前的执行情况:

#include <iostream> 
#include <cassert> 
#include <chrono> 
#include <algorithm> 
#include <vector> 
#include <string> 
using namespace std; 
using namespace chrono; 



struct PointF 
{ 
    float x; 
    float y; 
}; 


void parse_points(char* points, PointF& p1, PointF& p2) 
{ 
    auto start = points; 
    const auto end = start + strlen(points); 

    // p1.x 
    start = std::find(start, end, ' '); 
    assert(start < end); 
    *start = '\0'; 
    p1.x = static_cast<float>(atof(points)); 
    points = start + 1; 

    // p1.y 
    start = std::find(start, end, ' '); 
    assert(start < end); 
    *start = '\0'; 
    p1.y = static_cast<float>(atof(points)); 
    points = start + 1; 

    // p2.x 
    start = std::find(start, end, ' '); 
    assert(start < end); 
    *start = '\0'; 
    p2.x = static_cast<float>(atof(points)); 
    points = start + 1; 

    // p2.y 
    start = std::find(start, end, ' '); 
    assert(start == end); 
    p2.y = static_cast<float>(atof(points)); 
} 



int main() 
{ 
    const auto n = 500000; 
    char points_str[] = "90292 5879 89042.2576 5879"; 
    PointF p1, p2; 

    vector<string> data(n); 

    for (auto& s : data) 
     s.assign(points_str); 

    const auto t0 = system_clock::now(); 

    for (auto i = 0; i < n; i++) 
     parse_points(const_cast<char*>(data[i].c_str()), p1, p2); 

    const auto t1 = system_clock::now(); 
    const auto elapsed = duration_cast<milliseconds>(t1 - t0).count(); 

    cout << "Elapsed: " << elapsed << " ms" << endl; 

    cin.get(); 
    return 0; 
} 
+0

我猜'boost :: lexical_cast'比'atof'快。 –

+2

@sorosh_sabz实际上慢了8倍以上.... – Nick

+0

解析问题太多,至少你可以做的就是先搜索。试试这个:[“* C++读取文件空间分隔浮动”](https://www.google.com/search?q=*+c%2B%2B+read+file+space+separated+float&ie=utf-8&oe = utf-8) –

鉴于浮点值的字符串,空格分隔:

const std::string example_input = "90292 5879 89042.2576 5879"; 

你就应该剖析,看看有什么更快,阅读浮点:

std::istringstream text_stream(example_input); 
std::vector<double> container; 
double value; 
while (text_stream >> value) 
{ 
    container.push_back(value); 
} 

或读为整数,服用性能损失,如果有浮点的指示:

std::istringstream text_stream(example_input); 
std::vector<double> container; 
double value; 
signed int int_value; 
std::streampos position_before_read = text_stream.tellg(); 
while (text_stream >> int_value) 
{ 
    // check the next character for possible floating point differences. 
    char c; 
    text_stream >> c; 
    switch (c) 
    { 
    case '.': 
    case 'E': case 'e': 
     // Rewind to before the number and read as floating point 
     text_stream.seekg(position_before_read); 
     text_stream >> value; 
     break; 
    default: 
     value = 1.0 * int_value; 
     break; 
    } 
    container.push_back(value); 
    position_before_read = text_stream.tellg(); 
} 

我的猜测是,标准库进行优化,以读取浮点的,比上面的例子中更好占所有浮点格式的差异

注意:或者,您可以将小数和指数读取为整数(如果存在),然后使用全部三部分构建浮点值。

+0

你有没有测量过我的解决方案? – Nick

+0

不,你是否对所有浮标的简单读数测量了您的解决方案? –

您可以实施atof返回space的pos。这样,你只需要遍历每个字符串一次。

例如。

char *atof(char *point, float &num) { 
    num = 0; 
    bool neg = false, dot = false; 
    float decimal = 0, mul = 0.1; 
    if (*point == '-') { 
    neg = true; 
    point++; 
    } else if (*point == '+') { 
    point++; 
    } 
    while (*point != ' ' && *point) { 
    if (*point == '.') { 
     dot = true; 
    } else { 
     if (dot) { 
     decimal += (*point - '0') * mul; 
     mul *= 0.1; 
     } else { 
     num = num * 10 + *point - '0'; 
     } 
    } 
    point++; 
    } 
    if (dot) { 
    num += decimal; 
    } 
    if (neg) { 
    num = -num; 
    } 
    return point; 
} 
+0

使用strtod;不要重新发明*。 – rici

我看到多个问题的代码(和它的实际好,你已经问):

  • 没有错误的情况下,操作时有号码(注:按讨论,你在这种情况下,预计0)
  • 您创建的PointF对象两次,能够通过他们
    • 您将它们作为参考,因此它不是平凡的人读取调用代码,这些都出来了PARAMS。
  • 您所创建的解析器在C语言中(虽然你可能会测量,如果它的速度更快或更慢)

我建议这样的:(注意:std::experimental::optional<>相当于这里boost::optional<>

#include <iostream> 
#include <cstring> 
#include <utility> 
#include <experimental/optional> 

struct PointF 
{ 
    float x; 
    float y; 
}; 

std::experimental::optional<std::pair<PointF, PointF>> parse_points(char* pch) 
{ 
    pch = strtok (pch, " "); 
    if (pch != NULL) 
    { 
     float x0 = atof(pch); 
     pch = strtok (NULL, " "); 
     if (pch != NULL) 
     { 
      float y0 = atof(pch); 
      pch = strtok (NULL, " "); 
      if (pch != NULL) 
      { 
       float x1 = atof(pch); 
       pch = strtok (NULL, " "); 
       if (pch != NULL) 
       { 
        float y1 = atof(pch); 
        PointF p0{x0, y0}, p1{x1, y1}; 
        return std::make_pair(p0, p1); 
       } 
      } 
     } 
    } 
    return std::experimental::nullopt; 
} 

int main() { 
    const char str[] ="90292 5879 89042.2576 5879"; 
    char* pch0 = new char[sizeof(str)], *pch = pch0; 
    memcpy(pch0, str, sizeof(str)); 

    std::experimental::optional<std::pair<PointF, PointF>> pOpt(parse_points(pch0)); 
    if(pOpt) 
     std::cout << pOpt->first.x << " " << pOpt->first.y << " " 
        << pOpt->second.x << " " << pOpt->second.y << " " << std::endl; 
    delete pch; 
} 
+0

不幸的是'std :: optional'在C++ 11中不可用(我甚至不能测试它)。你有没有测试过我的解决方案?你得到了什么样的改善? – Nick

+0

atof'在出错的情况下返回0,这很好,因为它应该是默认值。除非在这种情况下不影响性能,否则始终接受清晰度。 – Nick

+0

@Nick:是的,'等于'部分丢失了:它是'boost :: optional',你也可以用它作为C++ 03。解决这个问题。至于'atof'和默认值0,不清楚'0x12 AB AF xx'应该导致((0,0),(0,0))还是(我认为)这是一个错误。另外 - 如果默认值改变(例如((-1,-1),(-1,-1)))? – lorro

这是我的版本,没有strlen,但使用strtok_s。 在我的机器上,它需要1.1sec而不是1.5sec

void parse_points(char* points, PointF& p1, PointF& p2) 
{ 
    char *next_token1 = nullptr; 

    // p1.x 
    points = strtok_s(points, " ", &next_token1); 
    p1.x = points ? static_cast<float>(atof(points)) : 0.0f; 

    // p1.y 
    points = strtok_s(nullptr, " ", &next_token1); 
    p1.y = points ? static_cast<float>(atof(points)) : 0.0f; 

    // p2.x 
    points = strtok_s(nullptr, " ", &next_token1); 
    p2.x = points ? static_cast<float>(atof(points)) : 0.0f; 

    // p2.y 
    points = strtok_s(nullptr, " ", &next_token1); 
    p2.y = points ? static_cast<float>(atof(points)) : 0.0f; 
} 



int main() 
{ 
    const auto n = 500000; 
    char points_str[] = "90292 5879 89042.2576 5879"; 
    PointF p1, p2; 

    vector<string> data(n); 

    for (auto& s : data) 
     s.assign(points_str); 

    const auto t0 = system_clock::now(); 

    for (auto i = 0; i < n; i++) 
     parse_points(const_cast<char*>(data[i].c_str()), p1, p2); 

    const auto t1 = system_clock::now(); 
    const auto elapsed = duration_cast<milliseconds>(t1 - t0).count(); 

    cout << "Elapsed: " << elapsed << " ms" << endl; 

    //cin.get(); 
    return 0; 
}