web-dev-qa-db-fra.com

Analyser un fichier csv dans Qt

Quelqu'un sait-il comment analyser un fichier csv et le placer dans une liste de chaînes? En ce moment, je prends le fichier csv entier et je le mets dans la liste des chaînes. J'essaie de comprendre s'il existe un moyen d'obtenir uniquement la première colonne.

#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();
}
13
user3878223

Voilà:

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

Construire et exécuter

qmake && make && ./main

Production

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

Ce que vous recherchez est une classe QTextStream . Il fournit toutes sortes d'interfaces pour lire et écrire des fichiers.

Un exemple simple:

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();

Alternativement oui, vous pouvez faire quelque chose comme ça qui aurait le même résultat:

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();    
11
Shf

Voici le code que j'utilise habituellement. Je suis l'auteur, considérez-le comme tel, dans le domaine public. Il a un ensemble de fonctionnalités et un concept similaires à code de CodeLurker sauf que la machine d'état est représentée différemment, le code est un peu plus court.

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 if (ch == '\r') continue;
            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;

}
  • Paramètre: in, a QTextStream.
  • Paramètre: row, un QStringList qui recevra la ligne.
  • Renvoie: true si une ligne a été lue, false si EOF.
  • Lance: std::runtime_error si une erreur se produit.

Il analyse les fichiers CSV de style Excel, gère les guillemets et les guillemets de manière appropriée et autorise les retours à la ligne dans les champs. Gère correctement les fins de ligne Windows et Unix tant que votre fichier est ouvert avec QFile::Text. Je ne pense pas que Qt prend en charge les terminaisons de ligne Mac à l'ancienne, et cela ne prend pas en charge les terminaisons de ligne non traduites en mode binaire, mais pour la plupart, cela ne devrait pas être un problème de nos jours.

Autres notes:

  • Contrairement à l'implémentation de CodeLurker, cela échoue intentionnellement si EOF est frappé entre guillemets. Si vous changez les -2 en -1 dans la table des états, cela sera indulgent.
  • Analyse x"y"z as xyz, ne savait pas quelle était la règle pour les guillemets intermédiaires. Je n'ai aucune idée si c'est correct.
  • Performances et caractéristiques de mémoire identiques à celles de CodeLurker (c'est-à-dire très bonnes).
  • Ne prend pas en charge unicode ( converti en ISO-5589-1 ) mais le passage à QChar devrait être trivial.

Exemple:

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

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

On pourrait préférer le faire de cette façon:

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;
}
  • Puissant: gère le matériel cité avec des virgules, des doubles guillemets doubles (qui signifient un caractère guillemet double) et des espaces à droite
  • Flexible: n'échoue pas si la dernière citation de la dernière chaîne est oubliée et gère les fichiers CSV plus compliqués; vous permet de traiter une ligne à la fois sans avoir à lire d'abord le fichier entier en mémoire
  • Simple: déposez simplement cette machine d'état dans votre code, faites un clic droit sur le nom de la fonction dans QtCreator et choisissez Refactor | Ajoutez la déclaration privée, et bon 2 allez.
  • Performant: traite avec précision les lignes CSV plus rapidement que les anticipations RegEx sur chaque personnage
  • Pratique: ne nécessite aucune bibliothèque externe
  • Facile à lire: le code est intuitif, au cas où vous en auriez besoin de 2.

Edit: j'ai enfin réussi à obtenir ceci pour couper les espaces avant et après les champs. Aucun espace ni virgule n'est coupé à l'intérieur des guillemets. Sinon, tous les espaces sont coupés au début et à la fin d'un champ. Après avoir été perplexe à ce sujet pendant un certain temps, j'ai trouvé l'idée que les citations pourraient être laissées sur le terrain; et ainsi tous les champs pourraient être coupés. De cette façon, seuls les espaces avant et après les guillemets ou le texte sont supprimés. Une dernière étape a ensuite été ajoutée, pour supprimer les citations des champs qui commencent et se terminent par des citations.

Voici un cas de test plus ou moins difficile:

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

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

Cela correspond au fichier

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

où <TAB> représente le caractère de tabulation; et chaque ligne est introduite dans parseCSV () à son tour. N'écrivez PAS de fichiers .csv comme celui-ci!

Sa sortie est (où qDebug () représente des guillemets dans la chaîne avec \" et mettre les choses entre guillemets et parens):

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

Vous pouvez observer que le devis et les espaces supplémentaires ont été conservés à l'intérieur du devis pour l'élément "deux". Dans le cas mal formé pour "et demi", l'espace avant la citation et ceux après le dernier mot ont été supprimés; mais les autres ne l'étaient pas. Les espaces terminaux manquants dans cette routine pourraient être une indication d'une citation de terminal manquante. Les citations dans un champ qui ne commencent ni ne se terminent sont simplement traitées comme faisant partie d'une chaîne. Un devis n'est pas supprimé à la fin d'un champ si l'on ne le démarre pas. Pour détecter une erreur ici, vérifiez simplement un champ qui commence par un devis, mais ne se termine pas par un; et/ou celui qui contient des guillemets mais ne commence et ne se termine pas par un, dans la boucle finale.

Plus que ce qui était nécessaire pour votre cas de test, je sais; mais une réponse générale solide au?, néanmoins - peut-être pour d'autres qui l'ont trouvé.

Adapté de: https://github.com/hnaohiro/qt-csv/blob/master/csv.cpp

8
CodeLurker

Essayez qtcsv bibliothèque pour lire et écrire des fichiers csv. Exemple:

#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;
}

J'ai essayé de le rendre petit et facile à utiliser. Voir le fichier Lisezmoi pour la documentation de la bibliothèque et d'autres exemples de code.

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

puis

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

Je ne suis pas sûr que la fonction d'ajout existe ou ne pas ajouter à un tableau - appelons la colonne 1

1
Dieu Linh