标准CSV格式的介绍和分析以及解析算法实例详解

CSV是一种古老的数据传输格式,它的全称是Comma-Separated Values(逗号分隔值)。出生在那个标准缺失的蛮荒年代,CSV的标准一直(到2005年)是NULL——世间存在着N种CSV格式,它们自成体系,相互不兼容。比如我们从名字可以认为CSV至少是一种使用逗号分隔的格式,但是实际上,有的CSV格式却是使用分号(;)去做分隔。假如,不存在一种标准,那么这东西最终会因为碎片化而发展缓慢,甚至没落。本文讨论的CSV格式是基于2005年发布的RFC4180规范。我想,在这个规范发布之后,大家应该会更加自觉的遵从这套规范去开发——虽然这套标准依旧存在着一些致命的缺陷

我们可以从IETF上获得包含了CSV格式定义的文档。当然,如果你觉得看英文文档麻烦,你可以直接看我的下文。

1.在不包含换行符(CRLF即 \r\n)的单条信息时,数据要保持在一行,并且使用\r\n结束。

aaa,bbb,ccc,dddCRLF   合法

aaa,b                            内容中无换行符,而单条信息被换行,不合法

bb.ccc,dddCRLF

2.最后一条信息可以没有换行符(当然有换行符也是合法的)

aaa,bbb,ccc,dddCRLF

eee,fff,ggg,hhh           合法

aaa,bbb,ccc,dddCRLF
eee,fff,ggg,hhhCRLF     合法

3.第一条信息可能是一个头信息。这个头信息和之后信息格式是相同的,并且和之后的信息有相同的模块数(上例中,aaa和bbb和ccc和ddd各被视为一个模块)。(个人认为这是RFC设计这个CSV格式的一个缺陷,因为这个规则将无法让我们从规则的角度去确认第一条信息到底是头信息还是普通信息。当然RFC这么设计肯定有它的原因。)

index,character          合法,从字面意思上我们可以认为这个是头,当然我们也可以认为它不是头

1,aCRLF
2,bCRLF

indexCRLF                 非法,模块数不统一
1,aCRLF

4.每条信息都要使用半角逗号(,)分隔出若干模块。每条信息的模块数要相等。每条信息的最后一个模块之后不可以使用半角逗号。空格符被视为一个模块的内容而不可被忽略。(这条规则包含的信息量相对较多)

aaa,bbbCRLF                合法
ccc,ddd,CRLF                非法,一条信息的最后一个模块不可以使用半角逗号
eee;ffffCRLF                   非法,要使用半角逗号分隔,而不是分号
ggg,       h h h  CRLF     合法,注意hhh模块的若干个空格,它属于模块内容而不可以被忽略
iii,jjj,kkkkCRLF               非法,模块数和上面不统一

5.每个模块首尾可以使用双引号扩住(当然也可以不使用)。如果不使用双引号扩住的模块,模块中不可以出现双引号。(言外之意:如果模块中出现双引号,则这个模块要用双引号将首尾扩住)

“aaa”,bbbCRLF             合法
a"aa,bbbCRLF              不合法,因为a"aa中包含了双引号,而这个模块没有被双引号扩住

6.如果模块中包含双引号、半角逗号或换行符,则模块首尾要用双引号扩住。

"a\r\na"a,bbbCRLF       合法,第一个模块包含了换行符,要用双引号包含
"a,aa",bbbCRLF            合法

7.当双引号出现在模块中,要将模块的首尾用双引号扩住,并且将模块中的一个双引号变成一对双引号。

“a""aa”,bbbCRLF          合法,原始数据为a"aa,bbb

有了以上规则,我们可以编写出相应的提取算法。以下是我在工作中编写的一套从CSV文件中提取信息的核心代码

BOOL CCSV2Json::Parse()
{
  BOOL bSuc = FALSE;
  do {
    if ( INVALID_HANDLE_VALUE == m_hFile ) {
      break;
    }
    OVERLAPPED ov;
    memset(&ov, 0, sizeof(OVERLAPPED));
    BYTE lpBuffer[BUFFERSIZE] = {0};
    DWORD dwHaveRead = 0;
    std::string strSingle;
    BOOL bFirstDoubleQuotes = FALSE;  // 第一个字符是否为"
    BOOL bBeforeIsDoubleQuotes = FALSE;
    BOOL bBeforeIsX0D = FALSE;
    ListString Liststr;
    BOOL bPairDoubleQuotes = FALSE;
    while ( ReadFile(m_hFile, lpBuffer, sizeof(lpBuffer), &dwHaveRead, &ov ) ) {
      ov.Offset += dwHaveRead;
      for ( DWORD dwIndex = 0; dwIndex < dwHaveRead; dwIndex++ ) {
        BYTE& by = *(lpBuffer + dwIndex); 

        if ( bFirstDoubleQuotes ) {
          // 有前置"
          if ( IsDoubleQuotes(by) ) {
            bBeforeIsX0D = FALSE;
            if ( bBeforeIsDoubleQuotes ) {
              strSingle.append(1, (char)(by));
              bBeforeIsDoubleQuotes = FALSE;
            }
            else {
              bBeforeIsDoubleQuotes = TRUE;
            }
          }
          else {
            if ( bBeforeIsDoubleQuotes ) {
              bFirstDoubleQuotes = FALSE;
            }
            bBeforeIsDoubleQuotes = FALSE;
            if ( IsCRLF( by ) ){
              if ( bFirstDoubleQuotes ) {
                strSingle.append(1, (char)(by));
              }
              else if (FALSE == bBeforeIsX0D) {
                Liststr.push_back(strSingle);
                m_Listliststr.push_back(Liststr);
                Liststr.clear();
                strSingle.clear();
                bFirstDoubleQuotes = FALSE;
              }
              bBeforeIsX0D = IsX0D(by);
            }
            else if ( IsSep(by) ) {
              bBeforeIsX0D = FALSE;
              if ( bFirstDoubleQuotes ) {
                strSingle.append(1, (char)(by));
              }
              else {
                bBeforeIsX0D = FALSE;
                Liststr.push_back(strSingle);
                strSingle.clear();
              }
            }
            else {
              bBeforeIsX0D = FALSE;
              strSingle.append(1, (char)(by));
            }
          }
        }
        else{
          // 如果无前置"
          if ( IsDoubleQuotes(by) ) {
            bBeforeIsX0D = FALSE;
            if ( strSingle.empty() ) {
              // 空串,第一个是"
              bFirstDoubleQuotes = TRUE;
              bBeforeIsDoubleQuotes = FALSE;
            }
            else {
              strSingle.append(1,(char)(by));
              continue;
            }
          }
          else {
            bBeforeIsDoubleQuotes = FALSE;
            if ( IsCRLF( by ) ){
              if (FALSE == bBeforeIsX0D) {
                Liststr.push_back(strSingle);
                m_Listliststr.push_back(Liststr);
                Liststr.clear();
                strSingle.clear();
                bFirstDoubleQuotes = FALSE;
                bBeforeIsDoubleQuotes = FALSE;
              }
              else {
                // 连续\r\n不考虑设置为新的行
              }
              bBeforeIsX0D = IsX0D(by);
            }
            else if ( IsSep(by) ) {
              bBeforeIsX0D = FALSE;
              Liststr.push_back(strSingle);
              strSingle.clear();
            }
            else {
              bBeforeIsX0D = FALSE;
              strSingle.append(1, (char)(by));
            }
          }
        } 

      }
      memset(lpBuffer, 0, sizeof(lpBuffer));
    } 

    if ( false == strSingle.empty() ) {
//       while ( IsCRLF(strSingle.at(strSingle.length() - 1) ) && strSingle.length() > 0) {
//         strSingle = strSingle.substr(0, strSingle.length() - 1 );
//       }
      Liststr.push_back(strSingle);
      m_Listliststr.push_back(Liststr);
      Liststr.clear();
      strSingle.clear();
    } 

    bSuc = TRUE;
  } while (0); 

  if ( NULL != m_hFile ) {
    CloseHandle(m_hFile);
    m_hFile = NULL;
  } 

  return bSuc;
}

这段代码将CSV文件提取出来一个std::list<std::list<std::string>>结构。如上面名字所示,我这个功能是要将CSV文件转换为json格式,相应的我也编写了从json格式转换为CSV格式文件的代码。这些代码都在工程中。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Mysql 导入导出csv 中文乱码问题的解决方法

    导入csv: 复制代码 代码如下: load data infile '/test.csv' into table table_name fields terminated by ','  optionally enclosed by '"' escaped by '"' lines terminated by '\r\n' ignore 1 lines; 导csv: 复制代码 代码如下: SELECT * INTO OUTFILE '/test.csv'  FIELDS TERMIN

  • Java生成CSV文件实例详解

    本文实例主要讲述了Java生成CSV文件的方法,具体实现步骤如下: 1.新建CSVUtils.java文件: package com.saicfc.pmpf.internal.manage.utils; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputS

  • c语言读取csv文件和c++读取csv文件示例分享

    C读取csv文件 复制代码 代码如下: #include <stdio.h>#include <string.h> char *trim(char *str){    char *p = str;     while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')        p ++;    str = p;     p = str + strlen(str) - 1;     while (*p == ' ' ||

  • python读取csv文件示例(python操作csv)

    复制代码 代码如下: import csvfor line in open("test.csv"):name,age,birthday = line.split(",")name = name.strip(' \t\r\n');age = age.strip(' \t\r\n');birthday = birthday.strip(' \t\r\n'); print (name + '\t' + age + '\t' + birthday) csv文件 复制代码 代

  • python处理csv数据的方法

    本文实例讲述了python处理csv数据的方法.分享给大家供大家参考.具体如下: Python代码: 复制代码 代码如下: #coding=utf-8 __author__ = 'dehua.li' from datetime import * import datetime import csv import sys import time import string import os import os.path import pylab as plt rootdir='/nethome/

  • C#读取csv格式文件的方法

    本文实例讲述了C#读取csv格式文件的方法.分享给大家供大家参考.具体实现方法如下: 一.CSV文件规则   1 开头是不留空,以行为单位. 2 可含或不含列名,含列名则居文件第一行. 3 一行数据不跨行,无空行. 4 以半角逗号(即,)作分隔符,列为空也要表达其存在. 5 列内容如存在半角逗号(即,)则用半角引号(即',')将该字段值包含起来. 6 列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来.

  • php使用fgetcsv读取csv文件出现乱码的解决方法

    本文实例讲述了php使用fgetcsv读取csv文件出现乱码的解决方法.分享给大家供大家参考.具体分析如下: 一般来说在php中碰到乱码多半是编码问题,在这里我们实例分析了fgetcsv读取csv文件乱码原因所在与解决方法. 例子如下: 复制代码 代码如下: function get_csv_contents( $file_target ){  $handle  = fopen( $file_target, 'r');  while ($data = fgetcsv($handle, 1000,

  • JS兼容浏览器的导出Excel(CSV)文件的方法

    Js导出表格为Excel文件 的常见一种办法是调用:ActiveXObject("Excel.Application") ,但是这种方法有局限性,只能在IE系列下的浏览器里实现,兼容性方面不理想. 经测试,采用本文推荐的方法能兼容性较好的导出表格内容到Excel文件. 复制代码 代码如下: var str = "博客, 域名\nBlog, 2\njb51.net, 3";var uri = 'data:text/csv;charset=utf-8,' + str;

  • php导入csv文件碰到乱码问题的解决方法

    今天主要是想写一个php导入csv文件的方法,其实网上一搜一大把.都是可以实现怎么去导入的.但是我导入的时候遇到了两个问题,一个是在windows上写代码的时候测试发生了乱码问题,然后解决了.第二个是提交到linux系统上的时候又发生了乱码.我开始还不清楚是乱码的原因,一开始我还以为是代码svn提交发生的错误,到最后我在我的一个群里提问了一下,一朋友是做phpcms的,他说他遇到从Windows提交到Linux的时候刚开始也总是发生错误,后来排查原因就是乱码导致成的.下面切入正题看怎么解决两个问

  • Python写入CSV文件的方法

    本文实例讲述了Python写入CSV文件的方法.分享给大家供大家参考.具体如下: # _*_ coding:utf-8 _*_ #xiaohei.python.seo.call.me:) #win+python2.7.x import csv csvfile = file('csvtest.csv', 'wb') writer = csv.writer(csvfile) writer.writerow(['id', 'url', 'keywords']) data = [ ('1', 'http

  • MySQL如何导入csv格式数据文件解决方案

    给自己做备份的,高手们请忽略. 数据太大,用数据库客户端软件直接导入非常卡,还是直接执行SQL吧. 1.指定文件路径. 2.字段之间以逗号分隔,数据行之间以\r\n分隔(我这里文件是以\n分隔的). 3.字符串以半角双引号包围,字符串本身的双引号用两个双引号表示. Sql代码 复制代码 代码如下: load data infile 'D:\\top-1m.csv' into table `site` fields terminated by ',' optionally enclosed by

  • 基于php导出到Excel或CSV的详解(附utf8、gbk 编码转换)

    php导入到excel乱码是因为utf8编码在xp系统不支持所有utf8编码转码一下就完美解决了utf-8编码案例Php代码 复制代码 代码如下: <?php header("Content-Type: application/vnd.ms-excel; charset=UTF-8"); header("Pragma: public"); header("Expires: 0"); header("Cache-Control: m

  • java读取csv文件示例分享(java解析csv文件)

    复制代码 代码如下: import java.io.*;import java.util.*;public class HandleCsv {public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(   new InputStreamReader(    new FileInputStream("test.csv")   )); String li

随机推荐