基于状态机的 CSV 文件数据解析

对话框/属性页 同时被 3 个专栏收录
27 篇文章 0 订阅
31 篇文章 0 订阅
62 篇文章 0 订阅
#include <tchar.h>
#include <afxtempl.h>
#include <stdio.h>
#include <string.h>
#ifndef CStringA
#define CStringA CString
#endif //CStringA

//解析CSV行数据核心代码
//chLine 输入行
//szDataArray 子项解析输出
//返回值:子项个数
int AnalysCSVDataLine(LPCSTR chLine, CStringArray &szDataArray)
{
  ASSERT(chLine != NULL);

  //先清理输出项
  szDataArray.RemoveAll();

  //子项状态
  enum _tagItemType_t
  {
    _Invalid_Item, //无效项
      _Comma_Item, //逗号分隔项
      _Quota_Item, //引号分隔项
  };
  
  //子项状态初始化为无效
  _tagItemType_t eItem = _Invalid_Item;
  //子项数据字符缓存区
  CStringA szSubData;
  //行长度
  int iLineLen = (int)strlen(chLine);
    
  //解析子项
  for(int iPos=0; iPos<iLineLen; )      
  {
    //读取两个字符
    char c0 = chLine[iPos+0];
    char c1 = (iPos+1 <iLineLen)? chLine[iPos+1]:0;

    //插入子项?
    int iInsertFlag = 0;
   
    //依据子项状态处理
    switch(eItem)
    {
    case(_Invalid_Item): //无效
      {
        szSubData.Empty(); //先清空子项
        
        if(c0 == '\"') //引号起始
        {
          eItem = _Quota_Item; //转为引号分隔
          iPos += 1; //跳过"
        }
        else
        {
          eItem = _Comma_Item; //逗号分割符
        }
        
        break;
      }
    case(_Quota_Item): //引号分隔
      {
        if(c0 == '\"' && c1 == '\"') //"" 遇两个引号
        {
          szSubData += "\""; //数据添加一个"
          iPos += 2; //跳过""
        }
        else if(c0 == '\"' && (c1==',' || c1=='\n' || c1=='\0')) //",或 "\0 遇行结束符
        {
          iInsertFlag = 2; //插入子项
        }
        else //正常数据
        {
          szSubData += c0; //追加数据
          iPos++; //跳到下一字符
        }
        
        break;
      }
    case(_Comma_Item): //逗号风格符
      {
        if(c0==',' || c0=='\n' || c0=='\0') //逗号或行结束
        {
          iInsertFlag = 1; //插入子项
        }
        else //正常数据
        {
          szSubData += c0; //追加数据
          iPos++; //跳到下一字符
        }
      }
    }
    
    //插入子项
    if(iInsertFlag)
    {
      szDataArray.Add(szSubData); //添加子项
      eItem = _Invalid_Item; //等待下一项
      iPos += iInsertFlag; //跳过长度
    }
    
  }

  //返回子项个数
  return (INT)szDataArray.GetSize();
}


int AnalsysCSVDataFile(LPCTSTR szFile, 
                       CTypedPtrArray <CPtrArray, CStringArray *> &szDataArray)
{
  FILE *fp = NULL;
  if((fp = _tfopen(szFile, _T("rt"))) != NULL)
  { 
    while(!feof(fp))
    {
      //行字符缓存区
      CHAR chLine[8192];
      memset(chLine, 0, sizeof(chLine));

      //读行
      if(fgets(chLine, sizeof(chLine), fp) == NULL)
        break;
      
      //解析行
      CStringArray szDataLines;
      if(AnalysCSVDataLine(chLine, szDataLines) > 0)
      {
        //追加行
        CStringArray *pStrA = new CStringArray;
        pStrA->Copy(szDataLines);
        szDataArray.Add(pStrA);
      }
    }
    
    //关闭文件
    fclose(fp);
  }  

  return (INT)szDataArray.GetSize();
}


 

//调用示例
void CDlg3Dlg::OnButton1()
{
  //打开文件
  CFileDialog dlg(TRUE, _T(".csv"), NULL, 0, 
    _T("csv files(*.csv)|*.csv||"), this);
  if(dlg.DoModal() == IDOK)
  {
    //解析文件
    CTypedPtrArray <CPtrArray, CStringArray *>sData;
    AnalsysCSVDataFile(dlg.GetPathName(), sData);

    //输出内容
    for(INT_PTR iLine=0; iLine<sData.GetSize(); iLine++)
    {
      CStringArray *sLine = sData.GetAt(iLine);
      INT iMaxItem = (INT)sLine->GetSize();
      TRACE(_T("Line%d==>(%d)==>"), iLine, iMaxItem);
      for(INT iItem=0; iItem<iMaxItem; iItem++)
      {
        CString sItem = sLine->GetAt(iItem);
        TRACE(_T("[%s]"), sItem);
      }
      TRACE(_T("\n"));
    }

    //结束清理
    while(sData.GetSize() > 0)
    {
      delete sData.GetAt(0);
      sData.RemoveAt(0);
    }
  }
}

 

//测试用csv文件内容

1,"""2""","3,ABC",4,"""""",",",""",""",,"aaa"",""bbb"
1,"""2""","3,ABC",4,"""""",",",""",""",,"aaa""??bbb"""
1,"""2""","3,ABC",4,"""""",",",""",""",,"aaa""??bbb"""

//调试输出

Line0==>(9)==>[1]["2"][3,ABC][4][""][,][","][][aaa","bbb]
Line1==>(9)==>[1]["2"][3,ABC][4][""][,][","][][aaa"??bbb"]
Line2==>(9)==>[1]["2"][3,ABC][4][""][,][","][][aaa"??bbb"]

 

  • 1
    点赞
  • 3
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值