2010-01-18 3 views
8

좋아, 다음은 내가하려고하는 것을 개략적으로 설명하는 코드이다.하나의 std 스트림을 다른 스트림으로 효율적으로 복사하기

#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/fcntl.h> 

#include <iostream> 
#include <sstream> 

int main(int c, char *v[]) 
{ 
    int fd = open("data.out", O_RDONLY | O_NONBLOCK); 
    std::cout << "fd = " << fd << std::endl; 

    char buffer[ 1024000 ]; 
    ssize_t nread; 

    std::stringstream ss; 

    while(true) 
    { 
     if ((nread = read(fd, buffer, sizeof(buffer) - 1)) < 0) 
      break; 

     ss.write(buffer, nread); 

     while(true) 
     { 
      std::stringstream s2; 

      std::cout << "pre-get : " << 
       (((ss.rdstate() & std::ios::badbit) == std::ios::badbit) ? "bad" : "") << " " << 
       (((ss.rdstate() & std::ios::eofbit) == std::ios::eofbit) ? "eof" : "") << " " << 
       (((ss.rdstate() & std::ios::failbit) == std::ios::failbit) ? "fail" : "") << " " << 
       std::endl; 

      ss.get(*s2.rdbuf()); 

      std::cout << "post-get : " << 
       (((ss.rdstate() & std::ios::badbit) == std::ios::badbit) ? "bad" : "") << " " << 
       (((ss.rdstate() & std::ios::eofbit) == std::ios::eofbit) ? "eof" : "") << " " << 
       (((ss.rdstate() & std::ios::failbit) == std::ios::failbit) ? "fail" : "") << " " << 
       std::endl; 

      unsigned int linelen = ss.gcount() - 1; 

      if (ss.eof()) 
      { 
       ss.str(s2.str()); 
       break; 
      } 
      else if (ss.fail()) 
      { 
       ss.str(""); 
       break; 
      } 
      else 
      { 
       std::cout << s2.str() << std::endl; 
      } 
     } 
    } 
} 

먼저 큰 데이터 청크를 데이터 버퍼에 읽습니다. 이 부분을 수행하는 더 나은 C++ 방식이 있다는 것을 알고 있지만, 실제 응용 프로그램에서는 char [] 버퍼와 길이가 전달됩니다.

그런 다음 버퍼를 std :: stringstream 개체에 기록하여 한 번에 한 줄씩 제거 할 수 있습니다.

나는 stringstream에 get (streambuf &) 메서드를 사용하여 한 줄을 다른 stringstream에 쓰고 출력 할 수 있다고 생각했습니다.

내가 읽은 버퍼에서 한 번에 한 줄을 추출하는 가장 좋은 방법이 아닐 수 있다는 사실을 무시한다. (내가 여기에 올린 사람에게 더 나은 대안을 제시하고 싶지만) , 처음으로 이 ss이라고 불리는 순간 실패 상태에 있으며 나는 그 이유를 알 수 없습니다. 입력 파일에 많은 데이터가 있으므로 ss에는 반드시 두 줄 이상의 입력이 있어야합니다.

아이디어가 있으십니까?

답변

0

나는 이것을 Windows에서 테스트 했으므로 이것을 검증 할 수 있습니다.

data.out이 개행 문자로 시작하면 다른 문제가 발생해도 ss.get (* s2.rdbuf())는 첫 번째 호출에서 정상적으로 작동합니다.

두 번째 호출 할 때 스트림의 현재 위치가 EOL을 지나지 않습니다. 따라서 두 번째 호출 할 때 즉시 EOL을 읽으려고 시도하고 다른 문자는 복사되지 않았으므로 오류 비트를 설정합니다.

빠른 어쩌면 더러운 수정 :

ss.get(*s2.rdbuf()); 
// Get rid of EOL (may need an extra if file contains both \r and \n) 
ss.get(); 
1

이 데이터를 복사 최소화하는 첫 번째 (그리고 아마도 가장 큰) 단계는 괜찮은 효율을 얻기 위해 나에게 보인다. 길이가있는 char []에 데이터가 주어지기 때문에 첫 번째 경향은 해당 버퍼를 사용하여 strstream을 작성하는 것입니다. 그런 다음 한 번에 한 문자열을 다른 strstream (또는 stringstream)에 복사하는 대신 한 번에 하나씩 문자열을 출력에 쓰는 데 사용할 문자열로 복사합니다.

버퍼의 내용을 수정할 수있는 경우 다른 '\ n'을 '\ 0'으로 바꾸면 버퍼를 줄 바꿀 수 있습니다. 만약 당신이 그렇게하려고한다면, 일반적으로 각 라인의 시작 부분에 대한 포인터의 벡터 (deque 등)를 생성하고 싶을 것입니다 (즉, 첫 번째 '\ r'또는 '\ n'을 찾습니다. 그것을 '\ 0'으로 바꾸십시오. '\ r'또는 '\ n'이외의 다음 문자는 다음 줄의 시작 부분이므로 벡터의 주소가됩니다.

줄 단위 출력을 피할 수 있는지 여부에 대해서도 열심히 생각하고 있습니다. 줄 바꿈을 찾기 위해 큰 버퍼를 읽는 것은 상대적으로 느립니다. 어쨌든 한 라인 씩 작성하려고한다면, 전체 버퍼에 출력 스트림을 작성하고 그 스트림으로 처리하는 것만으로 모든 것을 피할 수 있습니다.

관련 문제