TXT格式的Excel表格读取问题优化解法(目前我所知的最简洁算法)

18 篇文章 0 订阅
14 篇文章 0 订阅

        做大型游戏的国内公司,据我所知全都在用Excel做数据——程序把数据接口抽象出来,给策划在Excel里填写数据——除不同的编辑器之外,数据的使用方法几乎都是一样的。
        把Excel表格存为txt格式,就得到了一个很简单的文本表格。把它拿给程序识别,只需要解决两个问题:
        1、正确的按行、列分析读取数据
        2、数据存储


我打算把这篇博客写的尽量短,let's go。


一、表格读取问题


定义两个函数(按C++说明):
// 替换函数。作用:将字符串里的pattern全部替换为repl
void Replace( string s, string pattern, string repl )
// 切割函数。作用:将字符串按符号spl切开成n个字符串,把结果放到数组v里
void Split( string s, string spl, vector<string>& v )


算法:
// 先读取整个文件到s里,必须按二进制binary方式读取,否则换行符会被底层处理,导致格式不标准。略
// step1:
Replace(s, "\x0A\x0D", "\x0D");
// step2:
Replace(s, "\t\"", "\t");
// step3:
Replace(s, "\"\t", "\t");
// step4:
Replace(s, "\"\"", "\"");
// 至此,文件里的单元格内换行、引号等特殊情况已经几乎彻底被处理完毕。剩下的就只是以\x0D为换行符、以\t为分列符的简单的表格而已了
// step5, 把文件切成行lines:
Split( s, "\x0D", lines );
// step6,把每行line都切开成单元格cells:
Split( line, "\t", cells );


解释:
step1,将换行符统一化,这步是为了照顾Mac版本的Excel,让Mac版和Win版统一。如果只考虑Win版,那么可以省略step1,然后在step5里把第二个参数改为\x0A\x0D即可。
换行问题的难点在于——一个单元格内是可以出现换行等特殊符号的,我也是偶然发现,Excel保存后,单元格内的换行符是\x0A,而一行结束的换行符是\x0D,如此一来就很容易区别了,不需要上下文分析。
step2,step3,这两步所解决的问题是:当一个单元格内出现特殊符号时(英文逗号等等也算),Excel会在单元格前后各加一个英文引号。注意到这个引号总是在\t之后或之前,所以可以用这种简单办法去掉它。
step4,这步所解决的问题是:Excel自身为了区别上一步中Excel自动加的引号和用户真正输入的引号,将用户输入的引号全部变成连续两个引号(例如用户输入3个引号就变成6个引号),由于上一步已经去掉了自动加的引号,这步只要再把连续的两个引号变成一个引号就可以了。

step5和step6就没什么好解释的了。

特殊情况的万能解法:
上面算法中的Replace按我的经验已经可以解决99.9%的读取问题。剩下的问题是什么呢? 1、单元格内可能会出现\t和引号,虽然不知道特殊方法的人输入不了。2、Mac版的Excel有BUG,可能出现单元格内的换行和结尾换行都变成\x0D的情况(当出现这种情况时,Win版的Excel打开此文件也会出错,最好的办法是及时还原这个文件)。
我建议对付特殊情况,可以在项目的主程序之外,单独做表格检查工具,进行多角度的表格正确性检查。除了逻辑错误等等,对Excel保存的特殊情况也可以进行处理。处理方法只需要记住一点:

——出现在两个单独双引号之间的内容,一定是一个单元格。

如此,只需要遍历一遍文件就可以排查出特殊的格式了。我用Python的正则表达式库做过这个算法,很有意思。




二、保存数据的数据格式

我之前参与的项目里,数据结构是这种方式:表格Table包含Lines,Line包含Cells,这样的做法和直觉比较一致。但是缺点是:Cell有多种数据类型——整数、小数、字符串,以及他们的数组形式共6种数据类型,在一个Line结构体里表示时,必须设计一个比较复杂的数据结构来容纳它们,带来的问题一是代码不容易看懂,二是引入了很多无关变量,表格很大时内存占用很可观。


简单的做法:按列保存数据。为什么这么做,我就不剥夺读者的乐趣了,大家想想吧。按宫本茂(?)的话说:创意就是一口气解决好几个问题的东西。



C++保存不确定的类型的方法很值得记录一下。
class Table
{
vector<void*> data_cols; // 多个列的数据,每列是一个void*
}
当遇到不同类型时,data_cols被强制转换为以下类型(举例):
vector< vector<int>* >; // 整数
vector< vector<string>* >; // 字符串
vector< vector< vector<int> >* >; // 整数数组
vector< vector< vector<string> >* >; // 字符串数组


实际写代码的时候,倒是不会遇到三层vector的情况,一般都是这么用:
vector< vector<int> >* d = data_cols[i];
// 假设a是一个 vector<int>
d->push_back( a );


陷阱:
使用时会大量调用new来申请空间。
data_cols这种数据不能轻易clear或者delete,析构时也要强制转换成相应类型再delete或者clear,否则会因为没有调用内层的析构函数导致 泄 漏 。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值