Как правильно обрабатывать текстовые потоки с \ r?Я бы хотел использовать буферизованный путь, используя Qt - PullRequest
0 голосов
/ 18 декабря 2011

Я использую Qt и QProcess для чтения некоторых данных из других инструментов и печати их в своем приложении. Например, подумайте, что это «терминал».

Я обрабатываю данные, используя QProcess :: canReadLine () и QProcess: readLine (), и это замечательно. Но некоторые инструменты используют \ r для печати индикаторов на экране, и это плохо для моего парсера. Поскольку никогда не нужно читать какую-либо строку, мое приложение просто ждет, пока процесс не завершит печать последней строки: многие строки склеены вместе с \ r вместо \ n.

В любом случае, есть ли какой-нибудь способ сказать QProcess использовать \ r также как перенос строки? Я думал о реализации своего подкласса QIODevice, но мне нужно было бы также переопределить QProcess, так что это, кажется, не оптимальный подход.

Я подумал об использовании среднего буфера и использовал этот буфер для передачи сигнала «hasLine» моей основной программе. Я бы использовал QProcess :: readyRead для заполнения буфера, а затем буфер для заполнения моего основного приложения, но я хотел бы просто сказать Qt, что \ r также в порядке как разрыв строки. Это возможно?

Ответы [ 2 ]

2 голосов
/ 18 декабря 2011

Я не думаю, что можно напрямую указать Qt использовать '\ r' как перевод строки. Я думал, что QTextStream мог бы сделать это, но, глядя на его источники, сейчас мне кажется, что я ошибался.

Один забавный способ сделать это - реализовать пользовательский подкласс QIODevice, который читает из другого QIODevice и просто заменяет все '\ r' на \ n, делегируя все другие методы, за исключением разновидностей read (), на исходное устройство. Тогда readLine () и QTextStream будут работать с результирующим потоком просто отлично, я думаю. Однако вам придется как-то иметь дело с возможной последовательностью '\ r \ n'. Плюс в том, что вам не нужно делать буферизацию в этом классе.

Что-то в этом роде:

class CRFilter: public QIODevice {
    Q_OBJECT
public:
    CRFilter(QIODevice *device);
protected:
    virtual qint64 readData(char *data, qint64 maxSize);
    virtual qint64 writeData(const char *data, qint64 maxSize);
private:
    QIODevice *device;
};

CRFilter::CRFilter(QIODevice *device):
device(device)
{
    // delegate the readyRead() signal to this object
    connect(device, SIGNAL(readyRead()), SIGNAL(readyRead()));
    // and maybe other signals like bytesWritten() too...
}

qint64 CRFilter::readData(char *data, qint64 maxSize)
{
    qint64 res = device->read(data, maxSize);
    for (qint64 i = 0; i < res; i++) {
        if (data[i] == '\r')
            data[i] = '\n';
    }
    return res;
}

qint64 CRFilter::writeData(const char *data, qint64 maxSize)
{
    return device->write(data, maxSize);
}

Тогда вы просто делаете это:

QProcess process; // use QProcess methods on this
CRFilter reader(&p); // use QIODevice methods on this
reader.open(QIODevice::ReadWrite); // need this to convince read()/write() methods to work

Я на самом деле не тестировал его, поэтому, возможно, для его исправления требуется некоторая отладка. Я также думаю, что это немного некрасиво, но не могу придумать ни одного действительно элегантного решения.

1 голос
/ 19 декабря 2011

Поскольку я не использую полиморфизм с этим, нет проблем с публичным наследованием и переопределением некоторых методов и сигналов:

QCLIProcess.h

#ifndef QCLIPROCESS_H
#define QCLIPROCESS_H

#include <QProcess>

class QCLIProcess : public QProcess
{
    Q_OBJECT

public:
    explicit QCLIProcess(QObject *parent = 0);
    bool canReadLine() const;
    QString readLine();

signals:
    void readyRead();

private slots:
    void processLine();

private:
    QByteArray buffer;
    QStringList lines;
};

#endif // QCLIPROCESS_H

QCLIProcess.cpp

#include "QCLIProcess.h"
#include <QtCore>

QCLIProcess::QCLIProcess(QObject *parent) :
    QProcess(parent)
{
    setReadChannelMode(QProcess::MergedChannels);
    connect((QProcess *)this, SIGNAL(readyRead()), this, SLOT(processLine()));
}

void QCLIProcess::processLine(){
    buffer.append(readAll());

    int last = 0;
    for(int i=0; i<buffer.size(); i++){
        if (buffer.at(i) == '\n' || buffer.at(i) == '\r'){
            QString line(buffer.mid(last, i-last));
            line.append('\n');
            if (!line.isEmpty()) lines << line;
            last = i+1;
        }
    }
    buffer.remove(0, last);
    emit readyRead();
}

bool QCLIProcess::canReadLine() const {
    return !lines.isEmpty();
}

QString QCLIProcess::readLine(){
    QString line;

    if (!lines.isEmpty()){
        line = lines.at(0);
        lines.removeFirst();
    }

    return line;
}

UPDATE: Я закончил инкапсуляцию QProcess в новый класс, а не выводил его. Таким образом, я мог контролировать, какие сигналы и какие слоты я хочу показывать.

QLineBufferedCRFilteredProcess.h #ifndef QCLIPROCESS_H #define QCLIPROCESS_H

#include <QProcess>

class QLineBufferedCRFilteredProcess : public QObject
{
    Q_OBJECT

public:
    explicit QLineBufferedCRFilteredProcess(QObject *parent = 0);
    bool canReadLine() const;
    QString readLine();

    void start(const QString &program, const QStringList &arguments);
    void close();

signals:
    void readyRead();
    void finished(int exitCode, QProcess::ExitStatus exitStatus);

private slots:
    void processLine();

private:
    QProcess process;
    QByteArray buffer;
    QStringList lines;
};

#endif // QCLIPROCESS_H

QLineBufferedCRFilteredProcess.cpp #include "QLineBufferedCRFilteredProcess.h" #include

QLineBufferedCRFilteredProcess::QLineBufferedCRFilteredProcess(QObject *parent) :
    QObject(parent)
{
    process.setReadChannelMode(QProcess::MergedChannels);
    connect(&process, SIGNAL(readyRead()), SLOT(processLine()));
    connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus)));
}

void QLineBufferedCRFilteredProcess::processLine()
{
    buffer.append(process.readAll());

    int last = 0;
    for(int i=0; i<buffer.size(); i++){
        if (buffer.at(i) == '\n' || buffer.at(i) == '\r'){
            QString line(buffer.mid(last, i-last));
            line.append('\n');
            if (!line.isEmpty()) lines << line;
            last = i+1;
        }
    }
    buffer.remove(0, last);
    emit readyRead();
}

bool QLineBufferedCRFilteredProcess::canReadLine() const
{
    return !lines.isEmpty();
}

QString QLineBufferedCRFilteredProcess::readLine()
{
    QString line;

    if (!lines.isEmpty()){
        line = lines.at(0);
        lines.removeFirst();
    }

    return line;
}

void QLineBufferedCRFilteredProcess::start(const QString &program, const QStringList &arguments)
{
    process.start(program, arguments);
}

void QLineBufferedCRFilteredProcess::close()
{
    process.close();
}
...