diff options
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp')
| -rw-r--r-- | SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp | 214 |
1 files changed, 116 insertions, 98 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp b/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp index ce568e9..4c4ed0e 100644 --- a/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp @@ -1,167 +1,193 @@ #include "csvserializer.h" #include <QStringList> +#include <QList> +#include <QDebug> +#include <QTime> -// TODO write unit tests for CsvSerializer +template <class C> +bool isCsvSeparator(QList<C>& ahead, const C& theChar, const QStringList& separators) +{ + for (const QString& sep : separators) + if (isCsvSeparator(ahead, theChar, sep)) + return true; + + return false; +} template <class C> -bool isCsvColumnSeparator(QTextStream& data, const C& theChar, const CsvFormat& format) +bool isCsvSeparator(QList<C>& ahead, const C& theChar, const QString& singleSeparator) +{ + if (singleSeparator[0] != theChar) + return false; + + typename QList<C>::const_iterator aheadIter = ahead.begin(); + int singleSeparatorSize = singleSeparator.size(); + int i = 1; + while (aheadIter != ahead.end() && i < singleSeparatorSize) + { + if (singleSeparator[i++] != *aheadIter++) + return false; + } + + if (i < singleSeparatorSize) + return false; + + for (int i = 1, total = singleSeparator.size(); i < total; ++i) + ahead.removeFirst(); + + return true; +} + +template <class C> +bool isCsvColumnSeparator(QList<C>& ahead, const C& theChar, const CsvFormat& format) { if (!format.strictColumnSeparator) return format.columnSeparator.contains(theChar); // Strict checking (characters in defined order make a separator) - QStringList separators; if (format.multipleColumnSeparators) - separators = format.columnSeparators; - else - separators << format.columnSeparator; + return isCsvSeparator(ahead, theChar, format.columnSeparators); - qint64 origPos = data.pos(); - bool match = true; - for (const QString sep : separators) - { - match = true; - data.seek(origPos - 1); - C nextChar; - for (const QChar& c : sep) - { - data >> nextChar; - if (c != nextChar) - { - data.seek(origPos); - match = false; - break; - } - } - if (match) - break; - } - - return match; + return isCsvSeparator(ahead, theChar, format.columnSeparator); } template <class C> -bool isCsvRowSeparator(QTextStream& data, const C& theChar, const CsvFormat& format) +bool isCsvRowSeparator(QList<C>& ahead, const C& theChar, const CsvFormat& format) { if (!format.strictRowSeparator) return format.rowSeparator.contains(theChar); // Strict checking (characters in defined order make a separator) - QStringList separators; if (format.multipleRowSeparators) - separators = format.rowSeparators; - else - separators << format.rowSeparator; + return isCsvSeparator(ahead, theChar, format.rowSeparators); + + return isCsvSeparator(ahead, theChar, format.rowSeparator); +} - qint64 origPos = data.pos(); - bool match = true; - for (const QString sep : separators) +template <class C> +void readAhead(QTextStream& data, QList<C>& ahead, int desiredSize) +{ + C singleValue; + while (!data.atEnd() && ahead.size() < desiredSize) { - match = true; - data.seek(origPos - 1); - C nextChar; - for (const QChar& c : sep) - { - data >> nextChar; - if (data.atEnd() || c != nextChar) - { - data.seek(origPos); - match = false; - break; - } - } - if (match) - break; + data >> singleValue; + ahead << singleValue; } - - return match; } template <class T, class C> -QList<QList<T>> typedDeserialize(QTextStream& data, const CsvFormat& format, bool oneEntry) +void typedDeserializeInternal(QTextStream& data, const CsvFormat& format, QList<T>* cells, QList<QList<T>>* rows) { - QList<QList<T>> rows; - QList<T> cells; - bool quotes = false; bool sepAsLast = false; + int separatorMaxAhead = qMax(format.maxColumnSeparatorLength, format.maxRowSeparatorLength) - 1; T field = ""; - C c0; - C c1; + field.reserve(3); + C theChar; + QList<C> ahead; - while (!data.atEnd()) + while (!data.atEnd() || !ahead.isEmpty()) { - data >> c0; + if (!ahead.isEmpty()) + theChar = ahead.takeFirst(); + else + data >> theChar; + sepAsLast = false; - if (!quotes && c0 == '"' ) + if (!quotes && theChar == '"' ) { quotes = true; } - else if (quotes && c0 == '"' ) + else if (quotes && theChar == '"' ) { if (!data.atEnd()) { - data >> c1; - if (c1 == '"' ) + readAhead(data, ahead, 1); + if (ahead.isEmpty()) + { + field += theChar; + } + else if (ahead.first() == '"' ) { - field += c0; + field += theChar; + ahead.removeFirst(); } else { quotes = false; - data.seek(data.pos() - 1); } } else { if (field.length() == 0) - cells << field; + *cells << field; quotes = false; } } else if (!quotes) { - if (isCsvColumnSeparator(data, c0, format)) + readAhead(data, ahead, separatorMaxAhead); + if (isCsvColumnSeparator(ahead, theChar, format)) { - cells << field; - field = ""; + *cells << field; + field.truncate(0); sepAsLast = true; } - else if (isCsvRowSeparator(data, c0, format)) + else if (isCsvRowSeparator(ahead, theChar, format)) { - cells << field; - rows << cells; - cells.clear(); - field = ""; - if (oneEntry) + *cells << field; + field.truncate(0); + if (rows) + { + *rows << *cells; + cells->clear(); + } + else + { break; + } } else { - field += c0; + field += theChar; } } else { - field += c0; + field += theChar; } } if (field.size() > 0 || sepAsLast) - cells << field; + *cells << field; - if (cells.size() > 0) - rows << cells; + if (rows && cells->size() > 0) + *rows << *cells; +} +template <class T, class C> +QList<QList<T>> typedDeserialize(QTextStream& data, const CsvFormat& format) +{ + QList<QList<T>> rows; + QList<T> cells; + typedDeserializeInternal<T, C>(data, format, &cells, &rows); return rows; } +template <class T, class C> +QList<T> typedDeserializeOneEntry(QTextStream& data, const CsvFormat& format) +{ + QList<T> cells; + typedDeserializeInternal<T, C>(data, format, &cells, nullptr); + return cells; +} + QString CsvSerializer::serialize(const QList<QStringList>& data, const CsvFormat& format) { QStringList outputRows; - foreach (const QStringList& dataRow, data) + for (const QStringList& dataRow : data) outputRows << serialize(dataRow, format); return outputRows.join(format.rowSeparator); @@ -172,7 +198,7 @@ QString CsvSerializer::serialize(const QStringList& data, const CsvFormat& forma QString value; bool hasQuote; QStringList outputCells; - foreach (const QString& rowValue, data) + for (const QString& rowValue : data) { value = rowValue; @@ -191,27 +217,19 @@ QString CsvSerializer::serialize(const QStringList& data, const CsvFormat& forma QStringList CsvSerializer::deserializeOneEntry(QTextStream& data, const CsvFormat& format) { - QList<QStringList> deserialized = CsvSerializer::deserialize(data, format, true); - if (deserialized.size() > 0) - return deserialized.first(); - - return QStringList(); -} - -QList<QStringList> CsvSerializer::deserialize(QTextStream& data, const CsvFormat& format) -{ - return CsvSerializer::deserialize(data, format, false); + QList<QString> deserialized = typedDeserializeOneEntry<QString, QChar>(data, format); + return QStringList(deserialized); } QList<QList<QByteArray>> CsvSerializer::deserialize(const QByteArray& data, const CsvFormat& format) { QTextStream stream(data, QIODevice::ReadWrite); - return typedDeserialize<QByteArray,char>(stream, format, false); + return typedDeserialize<QByteArray,char>(stream, format); } -QList<QStringList> CsvSerializer::deserialize(QTextStream& data, const CsvFormat& format, bool oneEntry) +QList<QStringList> CsvSerializer::deserialize(QTextStream& data, const CsvFormat& format) { - QList<QList<QString>> deserialized = typedDeserialize<QString, QChar>(data, format, oneEntry); + QList<QList<QString>> deserialized = typedDeserialize<QString, QChar>(data, format); QList<QStringList> finalList; for (const QList<QString>& resPart : deserialized) @@ -224,6 +242,6 @@ QList<QStringList> CsvSerializer::deserialize(const QString& data, const CsvForm { QString dataString = data; QTextStream stream(&dataString, QIODevice::ReadWrite); - return deserialize(stream, format, false); + return deserialize(stream, format); } |
