2016-08-12 5 views
1

하나의 공백 문자로 구분 된 4 개의 부동 소수점 숫자를 포함하는 여러 C 스타일 문자열 (약 500k)을 구문 분석해야합니다. 하나의 문자열의 예는 다음과 같은 :숫자 문자열 해석

"5879 89042.2576 5879 90292"

내가 두 점을 나타내는 두 개의 구조에서이 번호를 저장해야합니다. 구문 분석하는 동안 문자열을 수정할 수 있으며 그 숫자의 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++ read file space separated float"] (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

space의 위치를 ​​반환하는 atof을 구현할 수 있습니다. 이 방법으로 각 문자열을 한 번만 트래버스해야합니다.

예 :

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

내가 코드 여러 문제를보고 (그리고 당신이 질문 한 것을 실제로 좋은) :

  • 번호 (있을 때 경우에 대한 처리 오류가 없습니다 NB : 논의에 따라, 당신은 따라서이 외출하는 호출 코드를 읽는 사람에 대한 사소한 아니라,
  • 당신은 PointF 당신은 참조로 전달할 그들에게
    • 을 통과 할 수 있도록 두 번 객체 생성)이 경우에는 0을 기대 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 : 네, 'equivalent to'부분이 없습니다 : boost :: optional입니다. C++ 03에서도 마찬가지입니다. 그걸 고치고있어. 'atof'와 디폴트 0에 관해서는 '0x12 AB AF xx'가 ((0,0), (0,0)) 또는 (내가 생각하기에) 에러가되어야한다는 것이 확실하지 않다. 또한 - 기본값이 변경되면 (예 : ((-1, -1), (-1, -1)))? – lorro

    0

    여기에 strlen이없고 strtok_s이없는 버전입니다. 내 컴퓨터에서 1.5sec 대신 1.1sec이 필요합니다.

    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; 
    }