一、代码优化的文本编辑工具
我们在进行代码优化的时候,会经常遇到代码查找和替换,手动“ctrl + c”和“ctrl + v”等操作。博主主要使用VS2013作为代码编辑工具,首先就介绍几个VS2013的常用快捷操作。(参考:VS2013快捷键)
1,VS2013支持的操作
“ctrl + f" 在文件内查找,也可以执行替换(f -- find)
"ctrl +shift + f"在整个解决方案内查找(可选择对话框)
”ctrl + Tab“ 换页(轮换)
”ctrl + k + c“ 注释 (c -- comment line);”ctrl + k + u“ 解注释;
”alt + shift + 上下箭头“启用列模式,如统一添加或删除virtual关键字
2,notepad++
1)notepad++的“ctrl + f" 功能更强大;
2)它的”编辑“菜单栏提供了对”空白“(行尾空白,行首空白)的处理;
3)它的”编辑“菜单栏提供了对“空格”和“Tab”的转换;
4)它的”编辑“菜单栏提供了“大小写转换”功能;
5)支持列模式;
6)提供格式转换“UTF-8”和"ANSI",推荐使用"UTF-8无BOM格式“,参考:UTF-8与UTF-8无BOM
3,SVN与BC
推荐将SVN的代码比较工具设置为BC
二、案例引入
很多时候,我们遇到的大量代码优化的问题,不能简单的通过“ctrl + f" 和列模式解决。比如,在上一篇博客(代码优化之map应用)中,我们没有办法直接将if/else if语句块转换为map的初始化列表。
源代码:
<span style="font-size:14px;">// 解析测量使能开关 void classA::ParseMeasurementFlag(const QString &itemName) { if (itemName.isEmpty()) { qCritical() << "itemName.isEmpty()"; return; } long lMeasurementFlag = 0; long lCurrTestItemFlag = 0; if (!QString::compare(itemName, QString1, Qt::CaseInsensitive)) { lMeasurementFlag = bit1 | bit2 | bit3; lCurrTestItemFlag = enum1; } else if (!QString::compare(itemName, QString2, Qt::CaseInsensitive)) { lMeasurementFlag = bit3 | bit5 | bit7; lCurrTestItemFlag = enum2; } ... else { lMeasurementFlag = 0; lCurrTestItemFlag = 0; } } </span>
目标代码:
<span style="font-size:14px;">void classA::InitTestCaseMap() { #define INSERT_MAP(expression1, expression2, expression3) \ m_mapTestCase.insert(QString(expression1).trimmed().toLower(), { expression2, expression3 }) INSERT_MAP(QString1, bit1 | bit2 | bit3, enum1); INSERT_MAP(QString2, bit3 | bit5 | bit7, enum2); INSERT_MAP(QString3, bit2 | bit5 | bit8, enum4); ... #undef INSERT_MAP(expression1, expression2, expression3) } </span>这个问题即涉及到不止一次的查找与替换,还设计到合并行。
既然工具不能搞定,不如自己动手编写一个小程序来实现这个功能,其核心就是字符串处理。
PS:我刚开始尝试用notepad++的宏录制功能,发现效果很不理想,无法受控,也不能检查问题出在哪。
三、”字符查找、替换和合并行“程序设计
1,设计思路
该程序的目的就是:从“source.cpp”文件中读取字符行,并找到目标函数;在读取目标函数块(以一对花括符为边界)的字符行,查找“QString::compare(itemName",找到该行,提出中间的字符串(如string1);然后查找”lMeasurementFlag"提取等号后面的值,再然后查找“lCurrTestItemFlag",提取等号后面的值。最后将提取的这三个值按目标文件中的格式组成一行,并输出到”target.cpp“文件中。反复执行上述”读入,查找,提取,组装,输出“的操作,直至函数尾。
2,注意点:
1)整个处理是已函数块为上下边界,简单的办法就是将待处理的内容拷贝到一个空白文件中,将边界由函数块转为整个文件。复杂的办法就是,设计一个计数器:函数名之后就是一个”{“,函数结尾则是一个对应的”}“,在函数体中,花括符也是成对出现的。这样我们定义一个变量”int count = 0;“作为循环结束判断条件,遇到一个”{“则”++count;“;遇到一个”}“则”--count;“。
2)循环体中,每次只处理一行字符,并设计一个容器,如vector,缓存多次迭代的处理结果。刚开始的时候,我设计的是:查找到“QString::compare(itemName"后,读取一行并丢弃,再读一行,并提取”lMeasurementFlag"对应的值,然后读一行,提取“lCurrTestItemFlag"对应的值,最后又读取一行并丢弃。但是,由于代码块中多了几行注释行,就打乱了这种设计的预期。
3)对字符行的处理推荐使用QString。最初,我使用的是std::string,发现提取子字符很不方便。
上代码:
<span style="font-size:14px;">#include <QCoreApplication> #include <QFile> #include <QTextStream> #include <QString> #include <QStringList> #include <iostream> void MergeLines(QTextStream & in, QTextStream & out); int main(int argc, char *argv[]) { //QCoreApplication a(argc, argv); QString fileName = {"source.txt"}; QFile inFile(fileName); if (inFile.exists()) inFile.open(QIODevice::ReadOnly); else qDebug("The input file does not exist !"); QTextStream in(&inFile); QFile outFile(QString("_") + fileName); outFile.open(QIODevice::WriteOnly | QIODevice::Truncate); QTextStream out(&outFile); MergeLines(in, out); outFile.flush(); outFile.close(); inFile.close(); return EXIT_SUCCESS; //return a.exec(); } void MergeLines(QTextStream & in, QTextStream & out) { int countLine = 0; int count = 0; QString line; QStringList list; while (!in.atEnd()) { line = in.readLine(); std::cout << ++countLine << "\t"; if (line.indexOf("Qt::CaseInsensitive") != -1) { QStringList list1 = line.split(", "); list.append(list1[1].trimmed()); } else if (line.indexOf("lMeasurementFlag") != -1 || line.indexOf("lCurrTestItemFlag") != -1) { QStringList list2 = line.split(" = "); list2[1].chop(1); list.append(list2[1].trimmed()); } if (list.size() == 3) { std::cout << "\n" << ++count << "\n"; QString align1; if (list[0].size() < 15) align1 = { "\t\t" }; else align1 = { "\t" }; //out << "m_mapTestCase.insert(QString(" << list[0] << ").trimmed().toLower()," << align1 << "{ " // << list[1] << ",\t" << list[2] << " });" << "\n"; out << "INSERT_MAP1(" << list[0] << "," << align1 << list[1] << ",\t" << list[2] << " );" << "\n"; list.clear(); } } }</span>注:
1,新建Qt console程序;
2,source.txt中保存的是拷贝的待处理代码块(简化处理)。
四、”查找并替换“程序设计
接上述案例,其中”QString1,QString2,QString3,..."定义在一个“constant.h"文件,格式如下:
<span style="font-size:14px;">#ifndef CONSTANTS #define CONSTANTS const QString QString1 = "dog"; const QString QString2 = "cat"; const QString QString3 = "mouse"; const QString QString4 = "marmot"; ... #endif</span>现需要精简代码,将map中的”QString1,QString2,QString3,..."用原始的字符串替换,并删除“constant.h"文件。由此设计了一个程序:
从”_source.txt“中读取行,并分离出”QString1,QString2,QString3,...",在到“constant.h"文件中找到对应的字符串,然后替换,最后将新的字符串输出。
上代码:
<span style="font-size:14px;">... void SearchAndReplace(QTextStream & in, QTextStream & in2, QTextStream & out); int main(int argc, char *argv[]) { //QCoreApplication a(argc, argv); QString fileName = {"_source.txt"}; QFile inFile(fileName); if (inFile.exists()) inFile.open(QIODevice::ReadOnly); else qDebug("The input file does not exist !"); QTextStream in(&inFile); QFile inFile2("constants.h"); if (inFile2.exists()) inFile2.open(QIODevice::ReadOnly); else qDebug("The input file does not exist !"); QTextStream in2(&inFile2); QFile outFile(QString("_") + fileName); outFile.open(QIODevice::WriteOnly | QIODevice::Truncate); QTextStream out(&outFile); //MergeLines(in, out); SearchAndReplace(in, in2, out); outFile.flush(); inFile.close(); inFile2.close(); outFile.close(); return EXIT_SUCCESS; //return a.exec(); } void SearchAndReplace(QTextStream & in, QTextStream & in2, QTextStream & out) { while (!in.atEnd()) { QString line1; QString source; line1 = in.readLine(); if (!line1.isEmpty()) { QStringList list1 = line1.split(","); source = list1[0].trimmed(); source.remove(0, 12); } QString target{}; while (!in2.atEnd()) { QString line2; line2 = in2.readLine(); if (line1.isEmpty()) continue; QStringList list2 = line2.split("="); if (list2.size() == 2) { list2[0] = list2[0].trimmed(); list2[0].remove(0, 14); if (source == list2[0]) { target = list2[1].trimmed(); target.chop(1); break; } } } if (!target.isEmpty()) line1.replace(source, target); out << line1 << "\n"; } }</span>
主要是将问题简化和细化。简化:如将待处理代码保存为一个单独的文件;细化:将合并行和替换分别设计一个函数或程序处理。