2011-08-16 3 views
0

텍스트 소스를 사용하여 사용자 정의 클래스의 벡터를 초기화 할 수있는 메커니즘을 구현해야합니다. 여기서 소스의 각 행은 클래스의 한 인스턴스를 나타냅니다. 이를 위해 내 수업에는 operator >>을 구현했으며 stringstream을 구현했습니다. 소스를 읽을 때마다 한 줄씩 이동하여 원본 소스의 하위 스트림을 얻은 다음 매번 하위 스트림을 구문 분석합니다. 이것은 나에게 3 가지 이점이있다. 첫째, 이렇게하면 텍스트 소스의 한 줄이 내 수업의 한 인스턴스를 정확히 나타낼 수 있습니다. 둘째, 구문 분석 후 나머지 줄이 무시되므로 내 텍스트 소스의 모든 행에 주석을 안전하게 추가 할 수 있습니다. 파서가 반드시 무시해야합니다. 그리고 세 번째로, 구문 분석 오류가 발생하기 때문에 원본 소스에서 벡터의 길이를 말할 필요가 없습니다. (확인하려면 스트림의 failbad 비트를 확인하십시오.) 벡터 선언 끝났어. 문자열 스트림의 서브 스트림 가져 오기

내가 다음 코드를 사용하고, 줄 단위를 구문 분석하려면이 코드가 잘 작동

std::stringstream  fullStream; 
std::stringstream  lineStream; 
std::string    str; 
bool     isValid; 
myClass     newInstance; 
std::vector <myClass> result; 

// Fill fullStream from external source (codepart omitted) 
isValid = true; 
while (isValid && ! fullStream.eof ()) { 
    std::getline (fullStream, str); 
    lineStream.clear (); 
    lineStream.str (str); 
    lineStream >> newInstance; 
    isValid = ! lineStream.fail (); 
    if (isValid) { 
     result.push_back (newInstance); 
    } 
} 

있지만 동일한 결과를 얻을 수있는 더 좋은 방법이 있다면, 내가 궁금하네요. 특히, fullStream에서 lineStream까지 줄을 추출하는 더 효율적인 방법이 있다면.

감사합니다,
아담

+0

세 개의 다른 스트림이 아닌 서로 다른 스트림을 사용하는 대신 반복기를 사용하여 입력 문자열을 검사하는 것이 훨씬 간단할까요? 귀하의 장소에서 추출 연산자의 논리를 문자열 반복자 쌍을 인수로 사용하여 스트림을 제거하는 일반 함수로 옮깁니다. 나는 당신의 코드가 그렇게 명확하다는 것을 확신합니다. –

+0

이 방법을 사용하면 모든 공백 관리 및 문자열 -> 부동 변환이 자동으로 처리되므로 '연산자'를 사용하고 있습니다. 나는'stringstream'을 사용하지 않고 그것을 어떻게 단순히 달성 할 수 있을지 모른다. –

+0

귀하의 요지를 봅니다. –

답변

2

한 가지 분명한 대안이하는 것입니다 귀하의 operator>> 당신이 외부에서 그렇게 할 필요가 없습니다, 줄 단위 자체를 읽고 수행

class MyClass { 
    // some sort of data to demonstrate the idea: 
    int x; 
    std::string y; 

    friend std::istream &operator>>(std::istream &is, MyClass &m) { 
     std::string temp; 
     std::getline(is, temp); 
     std::istringstream buffer(temp); 
     buffer >> m.x >> m.y; 
     return is; 
    } 
}; 

으로 파일에서 데이터를 읽는 코드는 좀 더 직관적입니다.

std::copy(std::istream_iterator<MyClass>(fullStream), 
      std::istream_iterator<MyClass>(), 
      std::back_inserter(result)); 

편집 : '이 t 구축하려는 행 지향, 또 다른 가능성은 프록시 클래스를 사용하는 것입니다 MyClass에 대한 operator>>에 직접 읽기 : 당신이 라인 중심의 독서를 수행 할 때 다음

class LineReader { 
    MyClass object; 
public: 
    operator MyClass() { return object; } 

    friend std::istream &operator>>(std::istream &is, LineReader &d) { 
     std::string line; 
     std::getline(is, line); 
     std::istringstream buffer(line); 
     buffer >> d; // delegate to the object's own stream-oriented reader. 
    } 
}; 

을, 당신 은의 객체를 읽어 프록시 클래스,하지만 저장 원래 클래스의 객체 : 대신 객체의 선 객체의 흐름을 읽으려면, 사용/

std::vector<MyClass>((std::istream_iterator<LineReader>(some_stream)), 
         std::istream_iterator<LineReader>()); 

그러나, 객체의 직접 자신의 operator>> :

std::vector<MyClass>((std::istream_iterator<MyClass>(stream), 
         std::istream_iterator<MyClass>()); 
+0

줄 바꿈 부분을'operator >>'함수로 옮기는 아이디어는별로 좋지 않습니다. 이렇게하면 줄 바꿈이없는 곳의 스트림을 파싱 할 수 없으므로 (필자는 내 프로젝트의 최신 상태에서 수행). 문제는 제 'MyClass' 클래스가 실제로 여러 차원에서 프로젝트의 여러 부분에서 사용하는 N 차원 점을 구현 한 것이므로 한 줄에서 여러 점을 파싱해야 할 수도 있습니다. –

+0

@ Jerry, 첫 번째 예제는'if (std :: getline (is, temp))'를 읽어야합니다. 확인하지 않고 입력을 사용하지 마십시오! – sbi

+0

@sbi : 예, 꽤 사실입니다. 내가 게시 한 대부분은 잘못된 입력이나 대부분의 다른 오류 검사에 대한 충분한 검사가 부족합니다. 예외 처리가 도움이되지만, 실제 코드는 오류 검사의 혼란에 거의 숨어있는 실제 의도로 끝나는 경우가 종종 있습니다 (사용자 입력을 처리 할 때 특히 그렇습니다). –

3

첫째, 코드가 작동하면 실제로 우연히 만나는 것입니다. 이 처리의 관용적 방법은 다음과 같습니다

std::string line; 
while (std::getline(fullStream, line)) { 
    std::istringstream lineStream(line); 
    lineStream >> newInstance; 
    if (lineStream) { 
     result.push_back(newInstance); 
    } else { 
     fullStream.setstate(std::ios_base::failbit); 
    } 
} 

읽기가 거의 유용하기 전에 eof()을 확인하고 그것을 사용하기 전에 getline의 결과를 확인하지 거의 확실 오류가 발생합니다. stringstream을 다시 사용하려는 경우 보다 간단하고 오류가 발생하기 쉽습니다. 을 재설정 할 필요가없는 모든 종류의 상태가 있습니다. 스트림에는 오류 상태를 기억하는 메커니즘이 있으므로이 코드를 사용하고 싶을 것입니다.(오류가 발생한 후 다른 부분에 대해 fullStream을 계속 사용하려는 경우 문제가 더 많은 경우 입니다. 왜냐하면 이미 실패한 행을 추출 했으므로 은 다시 되돌릴 수 없기 때문입니다.) 그리고 만약 독서 만 사용하는 경우 std::istringstream을 사용해야하며 초과 수하물이 많은 것은 아니며 std::stringstream이 아닙니다. 일반적으로 양방향 스트림을 사용하는 것은 매우 드뭅니다.