代码优化之自动修改

一、代码优化的文本编辑工具

        我们在进行代码优化的时候,会经常遇到代码查找和替换,手动“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>


五、总结

        主要是将问题简化和细化。简化:如将待处理代码保存为一个单独的文件;细化:将合并行和替换分别设计一个函数或程序处理。

你可能感兴趣的:(代码优化之自动修改)