通过 Qt 中的 csv 文件进行解析
Posted
技术标签:
【中文标题】通过 Qt 中的 csv 文件进行解析【英文标题】:Parsing through a csv file in Qt 【发布时间】:2014-12-05 14:55:54 【问题描述】:是否有人熟悉如何解析 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();
【问题讨论】:
【参考方案1】:你去吧:
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")
【讨论】:
这行得通。唯一的问题是,在我的文件中,第一列是编号的。我尝试用 .second 替换 wordList.append(line.split(',').first() 并且它不起作用。qt 是否有另一个内置函数可以让您获得第二列? @user3878223: at(1). 这不适用于包含包含逗号的值的行。例如,在‘1, "2, 3", 4’中,第二列是‘2,3’。 @Gallaecio:确实。很遗憾,这里的其他几个不错的答案似乎付出了更多努力来正确解析输入,但还没有获得更多投票来超越这个快速-n-dirty 解决方案。 ` wordList.append(line.split(',').first());` 将在第一次出现 "a",2,3 时崩溃。【参考方案2】:您正在寻找的是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();
【讨论】:
我认为QTextStream
是不必要的复杂化,QRegExp
版本相当丑陋。 :-)【参考方案3】:
这是我经常使用的代码。我是作者,请按原样考虑,公共领域。它具有与CodeLurker's code 相似的功能集和概念,只是状态机的表示方式不同,代码更短一些。
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
,一个QTextStream
。
参数:row
,将接收行的QStringList
。
返回:true
如果读取了一行,false
如果 EOF。
如果发生错误,则抛出:std::runtime_error
。
它解析 Excel 样式的 CSV,适当地处理引号和双引号,并允许字段中的换行符。只要使用QFile::Text
打开文件,就可以正确处理 Windows 和 Unix 行尾。我不认为 Qt 支持老式 Mac 行尾,而且它不支持二进制模式未翻译的行尾,但在大多数情况下,这在这些天应该不是问题。
其他说明:
与 CodeLurker 的实现不同,如果 EOF 在引号内被击中,这会故意失败。如果您将状态表中的 -2 更改为 -1,那将是宽容的。 将x"y"z
解析为xyz
,不确定字符串中引号的规则是什么。我不知道这是否正确。
性能和内存特性与 CodeLurker 相同(即非常好)。
不支持 unicode (converts to ISO-5589-1),但更改为 QChar
应该很简单。
例子:
QFile csv(filename);
csv.open(QFile::ReadOnly | QFile::Text);
QTextStream in(&csv);
QStringList row;
while (readCSVRow(in, &row))
qDebug() << row;
【讨论】:
现在有一个懂状态机的人! @MichalLeon 感谢您的编辑。这不是完全正确(特别是当它应该将它包含在字段中时它会忽略引号内的 \r ),但我现在没有时间调整它。 @Jason C,你是绝对正确的,但我不认为控制字符“属于”CSV。没有具体的禁令,但我从未见过包含任何 127。 @MichałLeon 如果您在例如Excel 中的单个单元格或其他内容。 @Jason C“Excel 中的单个单元格或其他内容”会破坏我的编辑。不要犹豫拒绝我的编辑。顺便说一句,“单元格内的新行”在 linux 上工作正常,即使它是 LF (0x0A)【参考方案4】:人们可能更喜欢这样做:
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 outermost 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 文件;让您一次处理一行,而无需先读取内存中的整个文件
简单:只需将此状态机放入您的代码中,右键单击 QtCreator 中的函数名称并选择 Refactor |添加私有声明,然后就可以了。
高性能:准确处理 CSV 行的速度比对每个字符执行 RegEx 预测要快
方便:不需要外部库
易读:代码直观,万一你需要修改它。
编辑:我终于可以用它来修剪字段前后的空格。引号内不修剪空格或逗号。否则,从字段的开头和结尾修剪所有空格。在对此感到困惑一段时间后,我想到了可以在场上留下引号的想法。因此可以修剪所有字段。这样,仅删除引号或文本前后的空格。然后添加了最后一步,去除以引号开头和结尾的字段的引号。
这是一个或多或少具有挑战性的测试用例:
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
其中
它的输出是(其中 qDebug() 用\"
表示字符串中的引号并将内容放在引号和括号中):
("one")
(" two \"", " and a half")
("three")
("four")
您可以观察到引号和额外的空格保留在项目“二”的引号内。在“and a half”的格式错误的情况下,引号之前的空格和最后一个单词之后的空格被删除;但其他人不是。此例程中缺少终端空格可能表示缺少终端引号。字段中没有开始或结束的引号仅被视为字符串的一部分。如果没有开始,则不会从字段末尾删除引号。要在此处检测错误,只需检查以引号开头但不以引号结尾的字段;和/或在最后一个循环中包含引号但不以一个开头和结尾的一个。
我知道,这超出了您的测试用例所需;但是对于 ? 的一个可靠的一般答案 - 也许对于其他找到它的人来说。
改编自: https://github.com/hnaohiro/qt-csv/blob/master/csv.cpp
【讨论】:
先生,我爱你。请嫁给我。这段代码很棒。赞成。【参考方案5】:尝试使用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 文件。
【讨论】:
【参考方案6】:lines = data.split('\n');
然后
for line in lines
column1.add(line.split(',')[0])
我不确定添加函数是否存在以添加到数组中 - 让我们调用第 1 列
【讨论】:
“行”是什么值类型。我假设一个字符串列表。对吗? 是的。它与您的 wordlist 变量相同 - 我只是给出想法 通常对于这些简单的问题,算法(伪代码通常是一个好主意)不是主要问题,而是实现......以上是关于通过 Qt 中的 csv 文件进行解析的主要内容,如果未能解决你的问题,请参考以下文章