/qtcsv

Library for reading and writing csv-files in Qt.

Primary LanguageC++MIT LicenseMIT

qtcsv

Build status

Small easy-to-use library for reading and writing csv-files in Qt.

Qt suppport:

  • Qt6: branch master (you're here)
  • Qt4 and Qt5: branch qt4_qt5

Tested on:

  • Ubuntu with gcc, Qt6
  • Windows with MinGW, Qt6
  • OS X with clang, Qt6

Table of contents

1. Quick Example

#include <QList>
#include <QString>
#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
    QList<QString> strList;
    strList << "one" << "two" << "three";

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

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

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

    return 0;
}

2. Usage

Library could be separated into three parts: Reader, Writer and Containers.

2.1 Containers

qtcsv library can work with standard Qt containers (like QList) and special data containers.

2.1.1 AbstractData

AbstractData is a pure abstract class that provides interface for a class of special data containers.

class AbstractData {
public:
    virtual ~AbstractData() = default;

    virtual void addEmptyRow() = 0;
    virtual void addRow(const QList<QString>& values) = 0;
    virtual void clear() = 0;
    virtual bool isEmpty() const = 0;
    virtual qsizetype rowCount() const = 0;
    virtual QList<QString> rowValues(qsizetype row) const = 0;
};

As you can see, AbstractData declare virtual functions for adding new rows, getting rows values, clearing all information and so on. Basic stuff for a container class.

2.1.2 StringData

StringData inhertis interface of AbstractData class and provides some useful functions for inserting/removing rows and so on. Class uses strings to store data.

2.1.3 VariantData

If you store information in different types - integers, floating point values, strings or (almost) anything else (example: [1, 3.14, "check"]) - and you don't want to manually transform each element to string, then you can use QVariant magic. Wrap your data into QVariants and pass it to VariantData class. It also inherits interface of AbstractData plus has several useful methods.

2.2 Reader

Use Reader class to read csv-files / csv-data. Let's see it's functions.

2.2.1 Reader functions

  1. Read data to QList<QList<QString>>
QList<QList<QString>> readToList(
    const QString& filePath,
    const QString& separator = QString(","),
    const QString& textDelimiter = QString("\""),
    QStringConverter::Encoding codec = QStringConverter::Utf8);
                              
QList<QList<QString>> readToList(
    QIODevice& ioDevice,
    const QString& separator = QString(","),
    const QString& textDelimiter = QString("\""),
    QStringConverter::Encoding codec = QStringConverter::Utf8);
  • filePath - string with absolute path to existent csv-file (example: "/home/user/my-file.csv");
  • ioDevice - IO Device that contains csv-formatted data;
  • separator (optional) - delimiter symbol, that separates elements in a row (by default it is comma - ",");
  • textDelimiter (optional) - text delimiter symbol that encloses each element in a row (by default it is double quoute - ");
  • codec (optional) - codec type that will be used to read data from the file (by default it is UTF-8 codec).

As a result function will return QList<QList<QString>> that holds content of the file / IO Device. Size of it will be equal to the number of rows in csv-data source. Each QList<QString> will contain elements of the corresponding row. On error these functions will return empty list.

  1. Read data to AbstractData-based container
bool readToData(
    const QString& filePath,
    AbstractData& data,
    const QString& separator = QString(","),
    const QString& textDelimiter = QString("\""),
    QStringConverter::Encoding codec = QStringConverter::Utf8);
                
bool readToData(
    QIODevice& ioDevice,
    AbstractData& data,
    const QString& separator = QString(","),
    const QString& textDelimiter = QString("\""),
    QStringConverter::Encoding codec = QStringConverter::Utf8);

These functions are little more advanced and, I hope, a little more useful.

  • filePath - string with absolute path to existent csv-file;
  • ioDevice - IO Device that contains csv-formatted data;
  • data - reference to AbstractData-based class object;
  • separator (optional) - delimiter symbol;
  • textDelimiter (optional) - text delimiter symbol;
  • codec (optional) - codec type.

Functions will save content of the file / IO Device in data object using virtual function AbstractData::addRow(QList<QString>). Elements of csv-data will be saved as strings in objects of StringData / VariantData.

If you would like to convert row elements to the target types on-the-fly during file reading, please implement your own AbstractData-based container class.

  1. Read data and process it line-by-line by AbstractProcessor-based processor
bool readToProcessor(
    const QString& filePath,
    AbstractProcessor& processor,
    const QString& separator = QString(","),
    const QString& textDelimiter = QString("\""),
    QStringConverter::Encoding codec = QStringConverter::Utf8);
                     
bool readToProcessor(
    QIODevice& ioDevice,
    AbstractProcessor& processor,
    const QString& separator = QString(","),
    const QString& textDelimiter = QString("\""),
    QStringConverter::Encoding codec = QStringConverter::Utf8);
  • filePath - string with absolute path to existent csv-file;
  • ioDevice - IO Device that contains csv-formatted data;
  • processor - reference to AbstractProcessor-based class object;
  • separator (optional) - delimiter symbol;
  • textDelimiter (optional) - text delimiter symbol;
  • codec (optional) - codec type.

This function will read csv-data from file / IO Device line-by-line and pass data to processor object.

2.2.2 AbstractProcessor

AbstractProcessor is an abstract class with two methods:

class AbstractProcessor
{
public:
    virtual ~AbstractProcessor() = default;
    virtual void preProcessRawLine(QString& /*editable_line*/) {}
    virtual bool processRowElements(const QList<QString>& elements) = 0;
};

When Reader opens a csv-data source (file or IO Device), it starts reading it line by line in a cycle. First of all, Reader passes each new line to processor's method preProcessRawLine(QString&). In this method you can edit the line - replace values, remove sensitive information and so on.

After that Reader parses elements of the row and passes them to processor's method processRowElements(QList<QString>). At that step you can do whatever you want with row elements - convert/edit/save/filter the elements. Please check out ReadToListProcessor class (defined in reader.cpp) as an example of such processor.

2.3 Writer

Use Writer class to write csv-data to files / IO Devices.

bool write(
    const QString& filePath,
    const AbstractData& data,
    const QString& separator = QString(","),
    const QString& textDelimiter = QString("\""),
    WriteMode mode = WriteMode::REWRITE,
    const QList<QString>& header = {},
    const QList<QString>& footer = {},
    QStringConverter::Encoding codec = QStringConverter::Utf8);
                   
bool write(
    QIODevice& ioDevice,
    const AbstractData& data,
    const QString& separator = QString(","),
    const QString& textDelimiter = QString("\""),
    const QList<QString>& header = {},
    const QList<QString>& footer = {},
    QStringConverter::Encoding codec = QStringConverter::Utf8);
  • filePath - string with absolute path to csv-file (new or existent);
  • ioDevice - IO Device;
  • data - object, that contains information that you want to write to csv-file / IO Device;
  • separator (optional) - delimiter symbol (by default it is comma - ",");
  • textDelimiter (optional) - text delimiter symbol that encloses each element in a row (by default it is double quoute - ");
  • mode (optional) - write mode flag. If it set to WriteMode::REWRITE and csv-file exist, then csv-file will be rewritten. If mode set to WriteMode::APPEND and csv-file exist, then new information will be appended to the end of the file. By default mode is set to WriteMode::REWRITE.
  • header (optional) - strings that will be written as the first row;
  • footer (optional) - strings that will be written at the last row;
  • codec (optional) - codec type that will be used in write operations (by default it is UTF-8 codec).

Writer uses CRLF as line ending symbols in accordance with standard. If element of the row contains separator symbol or line ending symbols, such element will be enclosed by text delimiter symbols (or double quoute if you have set empty string as text delimiter symbol).

3. Requirements

Qt6, only core/base modules.

4. Build

4.1 Building on Linux, OS X

4.1.1 Using qmake

cd /path/to/folder/with/qtcsv

# Create build directory
mkdir ./build
cd ./build

# Build library. You can choose build type: release or debug
qmake ../qtcsv.pro CONFIG+=[release|debug]
make

# Create build directory for tests
mkdir ./tests
cd ./tests

# Build tests. Besides of setting build type, we set path where linker could find compiled library file.
qmake ../../tests/tests.pro CONFIG+=[release|debug] LIBS+=-L../
make

4.1.2 Using cmake

cd /path/to/folder/with/qtcsv

# Create build directory
mkdir ./build
cd ./build

# Build library and tests. See CMakeLists.txt for list of additional options that you can set.
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON ..
make

4.2 Building on Windows

4.2.1 Prebuild step on Windows

If you going to build qtcsv library on Windows with MinGW, first of all check that your PATH variable contains paths to Qt and MinGW toolsets.

4.2.2 Using qmake

cd C:\path\to\folder\with\qtcsv

# Create build directory
mkdir .\build
cd .\build

# Build library. You can choose build type: release or debug. Set DESTDIR to current directory.
qmake ..\qtcsv.pro CONFIG+=[release|debug] DESTDIR=%cd%
mingw32-make

# Create build directory for tests
mkdir .\tests
cd .\tests

# Copy library file into 'tests' directory
copy ..\qtcsv.dll .\

# Build tests
qmake ..\..\tests\tests.pro CONFIG+=[release|debug] DESTDIR=%cd%
mingw32-make

4.2.3 Using cmake

cd C:\path\to\folder\with\qtcsv

# Create build directory
mkdir .\build
cd .\build

# Build library and tests. See CMakeLists.txt for list of additional options that you can set.
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON ..
mingw32-make

5. Run tests

To run tests use these commands after build of qtcsv:

5.1 Linux, OS X

cd /path/to/folder/with/qtcsv/build/tests

# Set LD_LIBRARY_PATH variable so test binary will know where to search library file.
# Suppose, that library file is located in "build" directory, up a level from current directory.
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/../

chmod 777 qtcsv_tests
./qtcsv_tests

5.2 Windows

cd /path/to/folder/with/qtcsv/build/tests

# Copy library file into "tests" directory
copy ..\*qtcsv.dll .\

qtcsv_tests.exe

6. Installation

On Unix-like OS you can install qtcsv library using these commands:

sudo make install
sudo ldconfig -n -v /usr/local/lib

These commands will copy all compiled files (libqtcsv.so*) from build folder to "/usr/local/lib". Also all headers files will be copied from "./include" folder to "/usr/local/include/".

All installation settings are defined in qtcsv.pro file. See copy_lib_headers and target variables.

For additional information, see Qt documentation about files installation.

7. Examples

If you would like to try qtcsv, you can download qtcsv-example project. Don't forget to read README.md file!

8. Other

If you want to know more about csv-file format, please read RFC 4180 standard.

Also on this page you can find useful tips about how should look proper csv-file.

9. Creators

Author: Antony Cherepanov (antony.cherepanov@gmail.com)
Contributors: Patrizio "pbek" Bekerle, Furkan "Furkanzmc" Üzümcü, Martin "schulmar" Schulze, cguentherTUChemnitz, David Jung, Nicu Tofan, Florian Apolloner, Michael Pollind, Kuba Ober, Akram Abdeslem Chaima, Bogdan Cristea, Markus Krause