Могу ли я удалить возврат каретки, когда они входят в `std :: stringstream`? - PullRequest
3 голосов
/ 17 января 2012
struct T
{
   void eat(std::string const& segment)
   {
      buffer << segment;

      std::string sentence;
      while (std::getline(buffer, sentence))
         std::cout << "[" << sentence.size() << "]";
   }

   std::stringstream buffer;
};

int main() {
   T t;
   t.eat("A\r\nB\nC\nD");
//        ^^   ^  ^  ^
}

// Actual output:  [2][1][1][1]
// Desired output: [1][1][1][1]

Я бы хотел, чтобы std::stringstream лишил меня возврата каретки (и предпочел бы не копировать и не изменять segment).

Как я могу пойти по этому поводу? Я бы подумал, что это все равно произойдет, в Linux, для потока в текстовом режиме ... но, возможно, этот механизм заключается в логике file streams.

1 Ответ

2 голосов
/ 17 января 2012

Это общая проблема на Unix-машинах при чтении файлов, созданных на Windows-машине.Я бы посоветовал выполнить очистку на уровне ввода.

Одно из лучших решений, которое я нашел при чтении файлов на основе строк, - создать класс, подобный следующему:

class Line
{
    std::string myText;
public:
    friend std::istream& operator>>( std::istream& source, Line& dest )
    {
        std::getline( source, dest.myText );
        if ( source ) {
            dest.myText.erase( 
                std::remove( dest.myText.begin(), dest.myText.end(), '\015' ),
                dest.myText.end() );
        }
        return source;
    }

    operator std::string() const
    {
        return myText;
    }
};

Вы можете добавлять другие функции по мере необходимости: автоматическое преобразование типов не срабатывает, например, при попытке сопоставления с шаблонами, и я нашел полезным добавить друзей для переноса boost::regex_match.

Я использую это (безудаление '\015'), даже когда мне не нужно беспокоиться о различиях в Windows / Linux;например, он поддерживает чтение строк с использованием std::istream_iterator<Line>.

Другим решением будет использование фильтрующего потока буфера, вставленного во входной поток.Это также очень просто:

class RemoveCRStreambuf : public std::streambuf
{
    std::streambuf* mySource;
    char myBuffer;  //  One char buffer required for input.
protected:
    int underflow()
    {
        int results = mySource->sbumpc();
        while ( results == '\015' ) {
            results = mySource->sbumpc();
        }
        if ( results != EOF ) {
            myBuffer = results;
            setg( &myBuffer, &myBuffer + 1, &myBuffer + 1 );
        }
        return results;
    }

public:
    RemoveCRStreambuf( std::streambuf* source )
        : mySource( source )
    {
    }
};

Чтобы вставить его:

std::streambuf* originalSB = source->rdbuf();
RemoveCRStreambuf newSB( originalSB );
source->rdbuf( &newSB );
//  Do input here...
source->rdbuf( originalSB );    //  Restore...

(Очевидно, что использование какого-либо RAII для восстановления было бы предпочтительнее. Мой собственный фильтрующий streambuf имеет конструкторкоторый принимает std::istream; они также сохраняют указатель на него и восстанавливают потоковый буфер в их деструкторе.)

...