使用Regex从CSV中提取数据并将其转换为JSON

时间:2013-06-11 08:01:46

标签: regex json qt csv

想象一下,您在CSV文件中有一个具有这种布局的表格:

name,property1 [unit1],property2 [unit2]
name1,4.5,2.3
name2,3.2,7.4
name3,5.5,6.1

我需要将每一行转换为这种JSON结构(即第1行):

{
    "name1": [
        {
            "properties": [
                {
                    "property_1": "_value_",
                    "unit": "unit1"
                },
                {
                    "property_2": "_value_",
                    "unit": "unit2"
                }
            ]
        }
    ]
}

最重要的是,我必须解释我使用的是Qt 4.7并且无法更新;另外,我无法安装Qxt,所以我依靠qt-json进行JSON解析/编码。更多,我不会创建/维护CSV文件,所以我也无法真正改变它。

所有这一切,我意识到我需要一些东西,所以这是一个多重问题:

  • 如何编写RegEx以读取每列标题中的单元?请注意,单位包含在矩形括号内。
  • 想象一下,我将标题行和其他行都提取到QList<QString>中,将每列分隔为字符串。我如何设法同步所有数据位,以便在QString上创建我需要的JSON结构? (我想我需要在QString中使用它,所以我可以将每一行转储到另一个文件中,但我也可以使用其他选项)

最后一个注意事项 - 我还需要这个可扩展性。将在其中显示的CSV文件在列数中非常不同:有些列有8列,有些有20列。

我知道发布“多项目”并不是一个好习惯,但问题是我对这一切感到太不知所措了,因为我几乎没有Qt的经验,所以我甚至无法定义一个计划攻击这个。希望有人可以分享一些指示。谢谢!

修改 所以,我一直在考虑这个问题,我实际上并不知道这是不是一个好主意/可行,但这就是我的想法:

  • 在浏览标题行时,我会检查每个列字符串是否有RegEx的命中。如果是这样,我会将列索引和单元字符串存储在列表中;
  • 然后,当浏览其他行时,为了将它们解析为JSON,我会检查每一列是否与上一个列表中的索引匹配,如果是,我会添加单元< / em>到地图(如qt-json docs解释)

这有什么意义吗?任何人都可以嘲笑我可以为此工作的骨架吗?

EDIT2

到目前为止,我已经成功完成了一些工作,但仍未按预期工作。现在我已经设法从CSV文件正确读取,但输出不正确。谁能分享一些见解?

注意: processLineFromCSV 函数返回如下所示的QStringList:QStringList cells = line.split(separator_char);

注2:RegEx是从this回答中获得的。

注3:检查下面我输出的输出类型。现在我认为问题更多地与qt-json库的使用相关,而不是其他代码,但欢迎任何帮助! :)

到目前为止的代码:

QFile file(csvfile);

    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        bool first = true;
        QVariantMap map;
        QVariantMap propertyMap;
        QList<QVariant> generalList, propertiesList;

        while (!file.atEnd())
        {
            QString line = file.readLine();
            if(first == true){
                headerList = processLineFromCSV(line, separator_char);
                first = false;
            }else{

            QStringList cellList = processLineFromCSV(line, separator_char);

            int i=0;

            for(i; i<cellList.size(); i++)
            {
                // check the header cell for "[unit]" string
                // returns -1 if does not have the string
                // if it has the string, it's stored in capturedUnits[1]
                int test = exp.indexIn(headerList.at(i));

                // store the captured units in a QStringList
                QStringList capturedUnits = exp.capturedTexts();

                if(test==-1){ // if header does not have a captured unit - general column
                    QString name = headerList.at(i);
                    QString sanitizeName= name.remove(exp.capturedTexts().at(0), Qt::CaseSensitive);
                    map[sanitizeName] = cellList.at(i);
                }
                else{ // if header string has a captured unit - property column

                    QString propertyName = headerList.at(i); // extract string in header
                    QString sanitizedPropertyName = propertyName.remove(exp); //remove the unit regex from the string
                    sanitizedPropertyName.remove(QChar('\n'), Qt::CaseSensitive); // clear newlines

                    if(sanitizedPropertyName.startsWith('"') && sanitizedPropertyName.endsWith('"'))
                    {
                        sanitizedPropertyName.remove(0,1);
                        sanitizedPropertyName.remove(sanitizedPropertyName.length(),1);
                    }

                    QString value =cellList.at(i); // extract string in value
                    QString sanitizedValue = value.remove(QChar('\n'), Qt::CaseSensitive); // clear newlines

                    if(sanitizedValue.startsWith('"') && sanitizedValue.endsWith('"'))
                    {
                        sanitizedValue.remove(0,1);
                        sanitizedValue.remove(sanitizedValue.length(),1);
                    }

                    propertyMap[sanitizedPropertyName]= sanitizedValue; // map the property: value pair
                    propertyMap["unit"] = capturedUnits.at(1); // map the unit: [unit] value pair

                    QByteArray general = QtJson::serialize(map); // serialize the pair for general column
                    QByteArray properties = QtJson::serialize(propertyMap); // serialize the pair for property column

                    QVariant genVar(general);
                    QVariant propVar(properties);

                    generalList.append(genVar);
                    propertiesList.append(propVar);
                }
            }
        }}
        QByteArray finalGeneral = QtJson::serialize(generalList);
        QByteArray finalProperties = QtJson::serialize(propertiesList);

        qDebug() << finalGeneral;
        qDebug() << finalProperties;


        file.close();
    }

输出:

"[
    "{ \"name\" : \"name1\" }",
    "{ \"name\" : \"name1\" }",
    "{ \"name\" : \"name2\" }",
    "{ \"name\" : \"name2\" }",
    "{ \"name\" : \"name3\" }",
    "{ \"name\" : \"name3\" }"
]" 
"[
    "{ \"property1 \" : \"4.5\", \"unit\" : \"unit1\" }",
    "{ \"property1 \" : \"4.5\", \"property2 \" : \"2.3\", \"unit\" : \"unit2\" }",
    "{ \"property1 \" : \"3.2\", \"property2 \" : \"2.3\", \"unit\" : \"unit1\" }",
    "{ \"property1 \" : \"3.2\", \"property2 \" : \"7.4\", \"unit\" : \"unit2\" }",
    "{ \"property1 \" : \"5.5\", \"property2 \" : \"7.4\", \"unit\" : \"unit1\" }",
    "{ \"property1 \" : \"5.5\", \"property2 \" : \"6.1\", \"unit\" : \"unit2\" }"
]"

3 个答案:

答案 0 :(得分:1)

Joum。

刚刚看到你对我评论的回应。我对QT也没有多少经验,但是快速概述......

一次提取一行数据,并将其“拆分”为一个数组。如果您使用的是CSV,则需要确保没有包含逗号的数据点,否则拆分将导致真正的混乱。检查提取数据的人是否可以使用另一个“不太常见”的分隔符(例如“|”是好的)。如果您的数据都是很好的数字,但要警惕使用逗号作为小数分隔符的位置:(

我希望你的每个文件都有1个'表',如果不是你需要能够“识别”新表以某种方式启动,这可能很有趣/有趣 - 取决于你的前景;)。

最后你会得到一个'字符串数组'(某种表格)的集合,希望第一个是你的标题信息。如果你有多个表格,你将一次处理一个

您现在应该能够以良好的JSON格式“输出”每个表。

从标题行中获取“单位”:如果您事先知道它们所在的位置(即数组中的索引),您可以计划在正确的索引位置提取信息(如果您愿意,使用正则表达式)

最后一点。 如果您的csv文件非常长(数百行),只需将前几个文件放入一个新的测试文件中以便更快地进行调试,然后一旦您满意,请稍微放大并检查输出格式...然后再次使用很高兴没有其他错误...对于整个文件 同样,如果你的文件中有多个表,只从第一个表开始,然后添加第二个的第一部分......测试....添加第三部分....测试等等,直到你感到高兴

大卫。

在阅读关于想要某种形式的“同步”的评论之后,可能是更好的解决方案。 注意:这可能看起来有点复杂,但我认为最终它将是一个更灵活的解决方案。这个数据也不存在于DB的某个地方(谁给了你?),它们能否直接读取对底层数据库和表的读取权限?如果是这样,你可以直接跳到'将每个表输出到JSON'步骤。

使用嵌入式DB(即SQLite)。 提取第一个“标题”行,并在数据库中创建一个跟随信息的表(您应该能够将有关单位的信息添加到'元数据',即描述)。如果所有文件都相同,您甚至可以将所有数据导入到同一个表中,或者使用相同的create table语句为每个新文件自动创建一个新表(假设格式相同)。

我确信SQLite中有一个'csvimport'(我还没有检查过文档,并且暂时还没有这样做)或者有人编写了一个可以执行此操作的库。

将每个表格输出为JSON格式,我再次确定有人为此编写了一个库。

答案 1 :(得分:1)

这应该是一个很好的开始:

QString csv = "name,property1 [unit1],property2 [unit2],property3 [unit3]\n"
              "name1,4.5,2.3\n"
              "name2,3.2,7.4\n"
              "name3,5.5,6.1,4.3\n";

QStringList csvRows = csv.split('\n', QString::SkipEmptyParts);
QStringList csvHeader = csvRows.takeFirst().split(',');
csvHeader.removeFirst();

foreach(QString row, csvRows) {
    QStringList values = row.split(',');
    QString rowName = values.takeFirst();

    QVariantList properties;
    for(int i = 0; i < values.size(); i++) {
        QString value = values[i];
        QStringList propParts = csvHeader[i].split(' ');
        QString propName = propParts[0];
        QString propType = propParts[1].mid(1, propParts[1].size() - 2);

        QVariantMap property;
        property[propName] = value;
        property["unit"] = propType;

        properties.append(property);
    }

    QVariantMap propertyObj;
    propertyObj["properties"] = properties;
    QVariantList propList;
    propList.append(propertyObj);

    QVariantMap root;
    root[rowName] = propList;

    QByteArray json = QtJson::serialize(root);
    qDebug() << json;

    // Now you can save json to a file
}

答案 2 :(得分:1)

使用ExplodingRat的答案这是最终代码:(最后没有创建文件)

QString csvfile = ui->lineEditCSVfile->text();
    QString separator_char = ui->lineEditSeparator->text();
    QRegExp exp("\\[([^\\]]+)\\]");

    QFile file(csvfile);
     if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
         return;

    QString csv = file.readAll();

    QStringList csvRows = csv.split('\n', QString::SkipEmptyParts);

    QStringList csvHeader = csvRows.takeFirst().split(separator_char);

    csvHeader.removeFirst();

    foreach(QString row, csvRows) {
        QStringList values = row.split(separator_char);

        QString rowName = values.takeFirst();

        QVariantList general;
        QVariantList properties;
        for(int i = 0; i < values.size(); i++) {
            QString value = values[i];

            int test = exp.indexIn(csvHeader[i]);
            //qDebug() << test;

            //qDebug() << csvHeader;
            QStringList capturedUnits = exp.capturedTexts();
            QString propName = csvHeader[i];


            if(test==-1){
                //QString propName = csvHeader[i].remove(exp);
                //qDebug() <<"property name" << propName;

                QVariantMap property;
                property[propName] = value;
                general.append(property);
            }else{
                propName.remove(exp);
                //QStringList propParts = csvHeader[i].split(' ');
                //QString propName = csvHeader[i].remove(exp);
                QString propType = capturedUnits[1];

                QVariantMap property;
                property[propName] = value;
                property["unit"] = propType;

                properties.append(property);
            }
        }

        QVariantMap propertyObj;
        propertyObj["properties"] = properties;
        QVariantList propList;
        propList.append(propertyObj);

        QVariantMap generalObj;
        generalObj["general"] = general;
        QVariantList generalList;
        generalList.append(generalObj);

        QVariantList fullList;
        fullList.append(generalObj);
        fullList.append(propertyObj);

        QVariantMap root;
        root[rowName] = fullList;

        QByteArray json = QtJson::serialize(root);

        json.prepend('[');
        json.append(']');

        qDebug() << json;

        // Now you can save json to a file