代码设计:C++ 一个CSV功能类(源码)

        CSV格式是逗号分隔的文本文件,几十年前挺常用,现在几乎没人用,但是因为很多工具都支持,可以作为各种数据迁移的中介,所以始终没有退出历史舞台。

        CSV格式非常简单,就是一行一条记录、逗号分隔字段而已。

        下面是个代码,主要是为了用excel编辑数据而做的,不保证和非execl的兼容性。

        主要的细节是对不同语言编码的处理和文本转换。因为用的只是UTF-8,所以只检测了UTF-8的BOM。

//csv格式文件处理

#include 

	//基本csv功能
	class CCSV
	{
	private:
		CStdOSFile m_file;
		vector m_Heads;//头标,如果no_head则没有头标
	public:
		//根据路径名创建所有目录,不包括最后的文件名
		static bool CreateDir(char const* filename)
		{
			string dirname;
			char const* p = filename;
			for (; '\0' != *p; ++p)
			{
				dirname += *p;
				if ('/' == *p)
				{
					//thelog<::const_iterator it;
			for (it = m_Heads.begin(); it != m_Heads.end(); ++it)
			{
				if (*it == field)return it - m_Heads.begin();
			}
			return -1;
		}
		//获得字段数
		long GetFieldCount()const
		{
			return m_Heads.size();
		}
		//获得字段名
		string GetFieldName(long field)const
		{
			return m_Heads[field];
		}
		//读取字段,或者是一个字段,或者是行结束
		bool _ReadField(bool& isLineEnd, string& ret)
		{
			bool tmp = __ReadField(isLineEnd, ret);
			TrimAll(ret);
			return tmp;
		}
		bool __ReadField(bool& isLineEnd, string& ret)
		{
			isLineEnd = false;
			ret = "";

			char m_c;
			string str;
			bool isFirstChar = true;
			bool isInStr = false;
			bool isInStrQuote = false;//是否在字符串内出现了一个双引号,这意味着字符串结束,或者一个内嵌双引号的开始
			while (1 == m_file.Read(&m_c, 1))
			{
				if (isFirstChar && ('\r' == m_c || '\n' == m_c))
				{//仅当第一个字符就是行结束才认为是行结束而没有获得数据
					isLineEnd = true;
					if ('\r' == m_c)
					{
						if (1 == m_file.Read(&m_c, 1))
						{
							if ('\n' != m_c)
							{
								m_file.SeekCur(-1);//不是连续\r\n,吐回去
							}
						}
					}
					return true;
				}
				else
				{
					isFirstChar = false;
				}

				if (isInStr && isInStrQuote && '\"' == m_c)
				{//内嵌双引号
					ret = +m_c;
					isInStrQuote = false;
					continue;
				}
				if (isInStr && isInStrQuote && ',' == m_c)
				{//字符串结束,逗号标记字段结束
					return true;
				}
				if (isInStr && isInStrQuote && ('\r' == m_c || '\n' == m_c))
				{//字符串结束,行结束
					m_file.SeekCur(-1);//吐回行结束,否则永远不能发现单独的行结束
					return true;
				}
				if (isInStr && isInStrQuote)
				{//字符串结束
					isInStr = false;
					continue;
				}
				if (isInStr && !isInStrQuote && '\"' == m_c)
				{//遇到内部第一个双引号
					isInStrQuote = true;
					continue;
				}
				if (isInStr && !isInStrQuote)
				{//字符串内容,除了双引号都不用特殊判断
					ret += m_c;
					continue;
				}

				if (!isInStr && '\"' == m_c)
				{//字符串开始
					isInStr = true;
					continue;
				}
				if (!isInStr && ',' == m_c)
				{//字段结束
					return true;
				}
				if (!isInStr && ('\r' == m_c || '\n' == m_c))
				{//行结束
					m_file.SeekCur(-1);//吐回行结束,否则永远不能发现单独的行结束
					return true;
				}
				if (!isInStr)
				{//非字符串的其余
					ret += m_c;
					continue;
				}
			}

			return false;
		}
		bool ReadField(string& ret)
		{
			bool isLineEnd;
			while (_ReadField(isLineEnd, ret))
			{
				if (!isLineEnd)return true;
			}
			return false;
		}
		//关闭文件
		bool CloseCSV()
		{
			return m_file.Close();
		}
		//文件到表格
		bool LoadFromFile(char const* file, CHtmlDoc::CHtmlTable2 & table)
		{
			table.Clear();

			CCSV csv;
			if (!csv.OpenCSV(file))
			{
				return false;
			}
			long i;
			for (i = 0; i < csv.GetFieldCount(); ++i)
			{
				table.AddCol(csv.GetFieldName(i));
			}
			bool isLineEnd;
			string str;
			bool need_new_line = false;
			table.AddLine();
			while (csv._ReadField(isLineEnd, str))
			{
				if (isLineEnd)
				{
					need_new_line = true;
				}
				else
				{
					if (need_new_line)
					{
						table.AddLine();
						need_new_line = false;
					}
					table.AddData(str);
				}
			}
			table.Fill();
			return csv.CloseCSV();
		}
	};
	class CText
	{
	public:
		//删除两端的空白,连续回车换行缩减为一个换行
		static string FormatText(char const* text)
		{
			string ret = text;
			TrimAll(ret);

			string::size_type pos = 0;
			bool isInNewLine = false;
			while (pos < ret.size())
			{
				if ('\r' == ret[pos] || '\n' == ret[pos])
				{
					if (!isInNewLine)
					{
						isInNewLine = true;
						ret[pos] = '\n';
						++pos;
					}
					else
					{//丢弃连续的换行,pos不变
						ret.erase(pos, 1);
					}
				}
				else
				{
					isInNewLine = false;
					++pos;
				}
			}
			return ret;
		}

	};

        这段代码中用到一些别的东西,基本上属于看到名字就能猜到功能的:

CStdOSFile 就是一个文件类,可以用任何别的文件类替换掉

CHtmlDoc::CHtmlTable2 这是一个表格类,可以输出为文本表格、HTML表格、变更JS,以后我可能会把这个类整理出来

TrimAll 删除两端空白字符

        这个代码是在UNIX、Linux上运行的。

        看注释应该能理解这个代码了,有问题也可以来问我。

(这里是结束)

你可能感兴趣的:(程序员造轮子(我的可重用代码),c++,CSV)