web-dev-qa-db-ja.com

Qtでcsvファイルを解析する

Csvファイルを解析して文字列リストに入れる方法を知っている人はいますか。現在、csvファイル全体を取得して、文字列リストに入れています。最初の列のみを取得する方法があるかどうかを把握しようとしています。

#include "searchwindow.h"
#include <QtGui/QApplication>

#include <QApplication>
#include <QStringList>
#include <QLineEdit>
#include <QCompleter>
#include <QHBoxLayout>
#include <QWidget>
#include <QLabel>

#include <qfile.h>
#include <QTextStream>


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QWidget *widget = new QWidget();
    QHBoxLayout *layout = new QHBoxLayout();

    QStringList wordList;

    QFile f("FlightParam.csv");
    if (f.open(QIODevice::ReadOnly))
    {
        //file opened successfully
        QString data;
        data = f.readAll();
        wordList = data.split(',');

        f.close();
    }

    QLabel *label = new QLabel("Select");
    QLineEdit *lineEdit = new QLineEdit;
    label->setBuddy(lineEdit);

    QCompleter *completer = new QCompleter(wordList);
    completer->setCaseSensitivity(Qt::CaseInsensitive); //Make caseInsensitive selection

    lineEdit->setCompleter(completer);

    layout->addWidget(label);
    layout->addWidget(lineEdit);

    widget->setLayout(layout);
    widget->showMaximized();

    return a.exec();
}
11
user3878223

そこに行きます:

FlightParam.csv

1,2,3,
4,5,6,
7,8,9,

main.cpp

#include <QFile>
#include <QStringList>
#include <QDebug>

int main()
{
    QFile file("FlightParam.csv");
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug() << file.errorString();
        return 1;
    }

    QStringList wordList;
    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        wordList.append(line.split(',').first());
    }

    qDebug() << wordList;

    return 0;
}

main.pro

TEMPLATE = app
TARGET = main
QT = core
SOURCES += main.cpp

ビルドして実行

qmake && make && ./main

出力

("1", "4", "7")
18
lpapp

探しているのは QTextStream クラスです。ファイルを読み書きするためのあらゆる種類のインターフェースを提供します。

簡単な例:

QStringList firstColumn;
QFile f1("h:/1.txt");
f1.open(QIODevice::ReadOnly);
QTextStream s1(&f1);
while (!s1.atEnd()){
  QString s=s1.readLine(); // reads line from file
  firstColumn.append(s.split(",").first()); // appends first column to list, ',' is separator
}
f1.close();

あるいは、はい、同じ結果になるこのようなことを行うことができます:

wordList = f.readAll().split(QRegExp("[\r\n]"),QString::SkipEmptyParts); //reading file and splitting it by lines
for (int i=0;i<wordList.count();i++) 
   wordList[i]=wordlist[i].split(",").first(); // replacing whole row with only first value
f.close();    
9
Shf

私が通常使用するコードは次のとおりです。私は著者であり、これを現状のままのパブリックドメインと考えています。 CodeLurkerのコード と同様の機能セットとコンセプトを持っていますが、ステートマシンが異なる方法で表されていることを除き、コードは少し短くなっています。

bool readCSVRow (QTextStream &in, QStringList *row) {

    static const int delta[][5] = {
        //  ,    "   \n    ?  eof
        {   1,   2,  -1,   0,  -1  }, // 0: parsing (store char)
        {   1,   2,  -1,   0,  -1  }, // 1: parsing (store column)
        {   3,   4,   3,   3,  -2  }, // 2: quote entered (no-op)
        {   3,   4,   3,   3,  -2  }, // 3: parsing inside quotes (store char)
        {   1,   3,  -1,   0,  -1  }, // 4: quote exited (no-op)
        // -1: end of row, store column, success
        // -2: eof inside quotes
    };

    row->clear();

    if (in.atEnd())
        return false;

    int state = 0, t;
    char ch;
    QString cell;

    while (state >= 0) {

        if (in.atEnd())
            t = 4;
        else {
            in >> ch;
            if (ch == ',') t = 0;
            else if (ch == '\"') t = 1;
            else if (ch == '\n') t = 2;
            else t = 3;
        }

        state = delta[state][t];

        switch (state) {
        case 0:
        case 3:
            cell += ch;
            break;
        case -1:
        case 1:
            row->append(cell);
            cell = "";
            break;
        }

    }

    if (state == -2)
        throw runtime_error("End-of-file found while inside quotes.");

    return true;

}
  • パラメーター:in、a QTextStream
  • パラメーター:row、行を受け取るQStringList
  • 戻り値:行が読み取られた場合はtrue、EOFの場合はfalse
  • 例外:std::runtime_errorエラーが発生した場合。

ExcelスタイルのCSVを解析し、引用符と二重引用符を適切に処理し、フィールドに改行を追加します。ファイルがQFile::Textで開かれている限り、WindowsおよびUnixの行末を適切に処理します。 Qtは昔ながらのMacの行末をサポートしておらず、これはバイナリモードの未翻訳の行末をサポートしていませんが、ほとんどの場合、これは最近では問題になりません。

その他の注意事項:

  • CodeLurkerの実装とは異なり、EOFが引用符で囲まれている場合、これは意図的に失敗します。状態テーブルで-2を-1に変更すると、許容されます。
  • x"y"zxyzとして解析しますが、中央文字列引用符のルールが何であるかわかりませんでした。これが正しいかどうかはわかりません。
  • CodeLurkerと同じパフォーマンスとメモリ特性(つまり非常に良い)。
  • Unicodeをサポートしていません( ISO-5589-1に変換 )が、QCharへの変更は簡単です。

例:

QFile csv(filename);
csv.open(QFile::ReadOnly | QFile::Text);

QTextStream in(&csv);
QStringList row;
while (readCSVRow(in, &row))
    qDebug() << row;
7
Jason C

次の方法でそれを行うことを好むかもしれません:

QStringList MainWindow::parseCSV(const QString &string)
{
    enum State {Normal, Quote} state = Normal;
    QStringList fields;
    QString value;

    for (int i = 0; i < string.size(); i++)
    {
        const QChar current = string.at(i);

        // Normal state
        if (state == Normal)
        {
            // Comma
            if (current == ',')
            {
                // Save field
                fields.append(value.trimmed());
                value.clear();
            }

            // Double-quote
            else if (current == '"')
            {
                state = Quote;
                value += current;
            }

            // Other character
            else
                value += current;
        }

        // In-quote state
        else if (state == Quote)
        {
            // Another double-quote
            if (current == '"')
            {
                if (i < string.size())
                {
                    // A double double-quote?
                    if (i+1 < string.size() && string.at(i+1) == '"')
                    {
                        value += '"';

                        // Skip a second quote character in a row
                        i++;
                    }
                    else
                    {
                        state = Normal;
                        value += '"';
                    }
                }
            }

            // Other character
            else
                value += current;
        }
    }

    if (!value.isEmpty())
        fields.append(value.trimmed());

    // Quotes are left in until here; so when fields are trimmed, only whitespace outside of
    // quotes is removed.  The quotes are removed here.
    for (int i=0; i<fields.size(); ++i)
        if (fields[i].length()>=1 && fields[i].left(1)=='"')
        {
            fields[i]=fields[i].mid(1);
            if (fields[i].length()>=1 && fields[i].right(1)=='"')
                fields[i]=fields[i].left(fields[i].length()-1);
        }

    return fields;
}
  • 強力:カンマ、二重二重引用符(二重引用符文字を意味する)、および空白スペースを使用して、引用符で囲まれた素材を処理します
  • 柔軟性:最後の文字列の最後の引用が忘れられても失敗せず、より複雑なCSVファイルを処理します。最初にメモリ内のファイル全体を読み取る必要なく、一度に1行ずつ処理できます。
  • シンプル:このステートマシンをヤーコードにドロップし、QtCreatorで関数名を右クリックして、[リファクタリング]を選択します。プライベートな宣言を追加し、ヤーグッド2ゴー。
  • パフォーマンス:各文字でRegEx先読みを行うよりも速くCSV行を正確に処理します
  • 便利:外部ライブラリは不要
  • 読みやすい:コードは直感的で、Uが2を修正する必要がある場合に備えています。

編集:私はようやくこれを取得して、フィールドの前後のスペースをトリミングすることになりました。引用符内の空白やカンマは削除されません。それ以外の場合、すべての空白はフィールドの先頭と末尾から削除されます。これについてしばらく困惑した後、引用符がフィールドの周りに残る可能性があるという考えに思いつきました。そのため、すべてのフィールドをトリミングできます。これにより、引用符の前後の空白のみが削除されます。その後、最後のステップが追加され、引用符で始まるフィールドと引用符で終わるフィールドの引用符が取り除かれました。

多少難易度の高いテストケースを次に示します。

QStringList sl=
{
    "\"one\"",
    "  \" two \"\"\"  , \" and a half  ",
    "three  ",
    "\t  four"
};

for (int i=0; i < sl.size(); ++i)
    qDebug() << parseCSV(sl[i]);

これはファイルに対応します

"one"
 " two """  , " and a half  
three  
<TAB>  four

ここで、<TAB>はタブ文字を表します。そして、各行はparseCSV()に順番に送られます。このような.csvファイルを書き込まないでください!

出力は次のとおりです(qDebug()は文字列内の引用符を\"で表し、引用符と括弧で囲みます):

("one")
(" two \"", " and a half")
("three")
("four")

引用符と余分なスペースが、アイテム「two」の引用符内に保持されていることがわかります。 「と半分」の不正なケースでは、引用の前のスペースと最後のワードの後のスペースが削除されました。しかし、他はそうではありませんでした。このルーチンで端末スペースが欠落している場合、端末引用符が欠落している可能性があります。開始または終了しないフィールド内の引用符は、文字列の一部として扱われます。引用符は、フィールドを開始しない場合、フィールドの末尾から削除されません。ここでエラーを検出するには、引用符で始まり、引用符で終わらないフィールドをチェックするだけです。および/または、最後のループで引用符を含むもので始まり、引用符で始まり終わらないもの。

Yerテストケースに必要だった以上のものを知っています。しかし、?に対する堅実な一般的な答えは、それでも-それを見つけた他の人にとってはおそらくでしょう。

適応元: https://github.com/hnaohiro/qt-csv/blob/master/csv.cpp

5
CodeLurker

qtcsv csvファイルの読み書き用のライブラリを試してください。例:

#include <QList>
#include <QStringList>
#include <QDir>
#include <QDebug>

#include "qtcsv/stringdata.h"
#include "qtcsv/reader.h"
#include "qtcsv/writer.h"

int main()
{
    // prepare data that you want to save to csv-file
    QStringList strList;
    strList << "one" << "two" << "three";

    QtCSV::StringData strData;
    strData.addRow(strList);
    strData.addEmptyRow();
    strData << strList << "this is the last row";

    // write to file
    QString filePath = QDir::currentPath() + "/test.csv";
    QtCSV::Writer::write(filePath, strData);

    // read data from file
    QList<QStringList> readData = QtCSV::Reader::readToList(filePath);
    for ( int i = 0; i < readData.size(); ++i )
    {
        qDebug() << readData.at(i).join(",");
    }

    return 0;
}

私はそれを小さくて使いやすいものにしようとしました。ライブラリのドキュメントおよびその他のコード例については、 Readme ファイルを参照してください。

4
iamantony
lines = data.split('\n');

それから

for line in lines
   column1.add(line.split(',')[0])

関数が存在するかどうか、配列に追加しないかどうかはわかりません-列1を呼び出してください

1
Dieu Linh