将ANTLR生成的.tokens文件重格式化(C++版)

相关链接:
[url=http://rednaxelafx.iteye.com/blog/176731]将ANTLR生成的.tokens文件重格式化(Ruby版)[/url]
[url=http://rednaxelafx.iteye.com/blog/177092]将ANTLR生成的.tokens文件重格式化(C#版)[/url]

先前写了Ruby的版本,链接在上面。作为对比,我很想看看自己如果用C++以同样的思路写会写出怎样的东西出来。Java和C#就暂时算了,总之肯定是介于C++与Ruby版本之间的样子。
[color=blue](更新:结果趁着热度顺便把C#的也写了,链接在上面)[/color]

于是下面的代码就是我交出的答卷。正好下午帮个同学写了俩短小C++作业,有些经验正好借鉴过来。
最重要的一点大概就是RAII的使用了。在vector里放指针的话,所指向的空间在程序结束的时候不会自动被释放掉,让人很郁闷。要么就自己记得在main()的最后显式的循环过整个vector来释放内存,要么就……RAII几乎是不二的选择了。就因为实现这很简陋的RAII类代码就多了不少,不过如果坚持要用C++的话,这绝对是值得的。
(如果要说“反正这是小程序,内存泄漏也无所谓,搞RAII那么麻烦有什么用”的话,那我也没话说了,摊手)

在字符串的模式匹配上,只用C和C++的标准库还是麻烦了点。我现在是用<string>里的getline(istream& is, string& str, char delim),不过这终究不是什么好办法。要用功能强点的模式匹配果然还是用正则表达式比较方便。MFC里CString提供的Split()就很合适这个场景。本来可以试试Boost::Regex,下次吧。
要是用Boost的话相关代码或许会像是这样吧:
#include <iostream>
#include <string>
#include <boost/regex.hpp>

int main() {
boost::regex re("^([^=]+)=([0-9]+)$");
boost::cmatch matches;
std::string str("Identifier=21");

boost::match(str, matches, re);

string name(matches[1].first, matches[1].second);
string value(matches[2].first, matches[2].second);

cout << value << "=" << name << endl; // 21=Identifier

return 0;
}

我原本装的Boost乱掉了,用VS2008暂时还没装上,总之就是有问题 T T

在排序和输出方面,C++的代码还是相当简洁的。可惜C++里的functor无法做in-place definition,非得给那个functor一个名字,这样就有了下面的ValueLess结构。Java里好歹可以用匿名内部类,C#更好可以用delegate和lambda expression,Ruby的block也差不多。

reformat.h:
#ifndef REFORMAT_H
#define REFORMAT_H

#include <vector>

#define BUFFER_SIZE 256

struct TokenNameValuePair {
char m_name[256];
int m_value;

TokenNameValuePair();
};

// RAII-style
class ResourceManager {
public:
ResourceManager(std::vector<TokenNameValuePair*>* pLines) : m_pLines(pLines) { }

~ResourceManager() {
for (std::vector<TokenNameValuePair*>::iterator it = m_pLines->begin();
it < m_pLines->end();
++it) {
delete *it;
}
}

std::vector<TokenNameValuePair*>* getLines();

private:
std::vector<TokenNameValuePair*>* m_pLines;
};

#endif // end of REFORMAT_H


reformat.cpp:
#include <cstdlib>
#include <cstring> // for memset
#include <iostream>
#include <fstream>
#include <algorithm>

#include "reformat.h"

using namespace std;

TokenNameValuePair::TokenNameValuePair() { memset(m_name, 0, BUFFER_SIZE); }

vector<TokenNameValuePair*>* ResourceManager::getLines() {
return m_pLines;
}

struct ValueLess {
bool operator()(TokenNameValuePair* first, TokenNameValuePair* second) {
return (first->m_value < second->m_value);
}
};

int main(int argc, char* argv[]) {
if (argc != 3) {
printf("Usage: %s [tokens file] [output file]", argv[0]);
exit(1);
}

ResourceManager res(new vector<TokenNameValuePair*>);

ifstream in(argv[1]);
if (!in.fail()) {
string line;
while (in.good()) {
getline(in, line, '=');
if (!line.compare("")) break;

TokenNameValuePair* pair = new TokenNameValuePair;
strcpy(pair->m_name, line.c_str());
getline(in, line);
pair->m_value = atoi(line.c_str());
res.getLines()->push_back(pair);
}
in.close();
}

sort(res.getLines()->begin(), res.getLines()->end(), ValueLess());

ofstream out(argv[2]);
if (!out.fail()) {
for (vector<TokenNameValuePair*>::iterator it = res.getLines()->begin();
res.getLines()->end() != it;
++it) {
out << (*it)->m_value << "=" << (*it)->m_name << endl;
}
out.flush();
out.close();
}

return 0;
}


用GCC 3.4.5的MinGW来编译测试的。效果还好吧。感觉是比Ruby版“快”一些,启动时间似乎稍微短些。(应该测一下……)

比起Ruby的代码,上面的C++最不顺手的地方就在内存管理和字符串的模式匹配吧。单就代码长度来看那绝对是长了不少,要做这么件小事显然是不爽的。
纯C的话,容器得自己写了吧,如果只能用纯C的话还是会怀念STL容器啊~想了下,如果是自己写的话,我可能会倾向于链式容器,主要是懒得去管动态增加容器大小时的复制。排序有<stdlib.h>里的qsort(),并不麻烦;输出也没什么麻烦的,fprintf()和fwrite()都可以用。果然还是在输入和内存管理上稍微麻烦些。
不知道大家是怎么看的呢。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值