2016-08-12 336 views
1

我需要解析包含由單個空格字符分隔的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

解析問題太多,至少你可以做的就是先搜索。試試這個:[「stackoverflow C++讀取文件空間分隔浮動」](https://www.google.com/search?q=stackoverflow+c%2B%2B+read+file+space+separated+float&ie=utf-8&oe = utf-8) –

回答

0

鑑於浮點值的字符串,空格分隔:

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

不,你是否對所有浮標的簡單讀數測量了您的解決方案? –

-1

您可以實施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

我看到多個問題的代碼(和它的實際好,你已經問):

  • 沒有錯誤的情況下,操作時有號碼(注:按討論,你在這種情況下,預計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

0

這是我的版本,沒有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; 
}