查看原文
其他

​效率工具篇-文本处理

2016-09-02 Wenzy InsLab

Processing 和 Openframeworks 除了可以画画,同样能胜任文本处理的工作。我们可以用它给为自己定制一些提高工作效率的工具。下面以 Openframeworks 为例,分享一段有关文本处理的代码。

这段代码,源于自己的某个需求 - 对文档进行拆分。

最近在都在进行 Processing 和 Openframeworks 的教程写作,这两个部分的内容许多都是相通的。所以一直以来都在同一个文档中合写两个版本,等到后期再进行拆分。

这样做的好处有两点:一目了然,便于比较。而且其中一个版本修改了,另一个也能同时顾及。

如果每次都用手动的方式去复制粘贴,就会非常麻烦。所以设计了一种拆分的语法,让程序来完成这类工作。

词汇标记

这里设计了两类语法。一是对词汇的标记。假如某些词是 Processing 中特有,就会在词汇前加上“ (P5: ” 作为前缀,而 “* ) ” 会作为后缀。若是 Openframeworks 中特有,就会在词汇前加上“ (OF: ” 作为前缀, 并同样以 “* ) ” 作为后缀。

下面截取了一段先前的文章,这是它的原始版本。

下面开始介绍一种专门用于储存色彩的数据类型 - (P5:color*)(OF:ofColor*)。 它接近于前面提到的 (P5:boolean*)(OF:bool*) ,int,float 这类数据类型。

经过程序自动拆分后,便会生成如下两个版本:

(Processing 版)

下面开始介绍一种专门用于储存色彩的数据类型 - color。 它接近于前面提到的 boolean,int,float 这类数据类型。

(Openframeworks 版)

下面开始介绍一种专门用于储存色彩的数据类型 - ofColor。 它接近于前面提到的 bool,int,float 这类数据类型。

段落标记

另一个则是对段落的标记,可用于替换大段的代码示例。例如 Processing 的示例,前面可以以 “ -(P5begin) “ 开头,以” -(P5end) “作为结尾。Openframeworks 的示例则以” -(OFbegin) “开头,以” -(OFend) “作为结尾。

下面是原始版本:

代码示例(9-1):

-(P5begin)

int r,g,b; void setup(){  size(400,400);  r = 255;  g = 0;  b = 0; } void draw(){  background(0);  rectMode(CENTER);  fill(r,g,b);  rect(width/2,height/2,100,100); }

-(P5end)

-(OFbegin)

—- ofApp.h 内

#include "ofMain.h" class ofApp : public ofBaseApp{    public:        void setup();        void update();        void draw();        ...        int r,g,b; };

—- ofApp.cpp 内

void ofApp::setup(){    ofSetWindowShape(400,400);    r = 255;    g = 0;    b = 0; } void ofApp::update(){ } void ofApp::draw(){    ofBackground(0);    ofSetRectMode(OF_RECTMODE_CENTER);    ofSetColor(r,g,b);    ofDrawRectangle(ofGetWidth()/2,ofGetHeight()/2,100,100); }

-(OFend)

程序拆分后这两段便会独立地归到各自的文章当中。

文本处理源代码

下面是正式代码:

— ofApp.h 中 —

class ofApp : public ofBaseApp{    public:        void setup();        void update();        void draw();       string replaceStr(string loadStr,string keyWords,string replaceWords);       string replaceStr(string loadStr,string start,string end,string replaceWords);        void keyPressed(int key);        void keyReleased(int key);        void mouseMoved(int x, int y );        void mouseDragged(int x, int y, int button);        void mousePressed(int x, int y, int button);        void mouseReleased(int x, int y, int button);        void mouseEntered(int x, int y);        void mouseExited(int x, int y);        void windowResized(int w, int h);        void dragEvent(ofDragInfo dragInfo);        void gotMessage(ofMessage msg);    string myStr,reStr,P5_Str,OF_Str; };

— ofApp.cpp 中 —

void ofApp::setup(){    // 读取原始文件    ofBuffer buffer = ofBufferFromFile("原始版.md");    myStr = buffer.getText();     // 文本替换测试    reStr = replaceStr(myStr,"基本知识","今天");    ofBuffer msg(reStr.c_str(), reStr.length());    // 导出为 markdown 格式    ofBufferToFile("字符替换测试.md", msg);    // 导出 P5 版    P5_Str = replaceStr(myStr,"-(OFbegin)","-(OFend)","");    P5_Str = replaceStr(P5_Str,"-(P5begin)","");    P5_Str = replaceStr(P5_Str,"-(P5end)","");    P5_Str = replaceStr(P5_Str,"(OF:","*)","");    P5_Str = replaceStr(P5_Str,"(P5:","");    P5_Str = replaceStr(P5_Str,"*)","");    ofBuffer msg1(P5_Str.c_str(), P5_Str.length());    ofBufferToFile("Processing版.md", msg1);    // 导出 OF 版    OF_Str = replaceStr(myStr,"-(P5begin)","-(P5end)","");    OF_Str = replaceStr(OF_Str,"-(OFbegin)","");    OF_Str = replaceStr(OF_Str,"-(OFend)","");    OF_Str = replaceStr(OF_Str,"(P5:","*)","");    OF_Str = replaceStr(OF_Str,"(OF:","");    OF_Str = replaceStr(OF_Str,"*)","");    ofBuffer msg2(OF_Str.c_str(), OF_Str.length());    ofBufferToFile("Openframeworks版.md", msg2); } void ofApp::draw(){    ofBackground(0); } string ofApp::replaceStr(string loadStr,string keyWords,string replaceWords){    string newStr;    int num = 0; // 可统计词数    for(int i = 0;i < loadStr.size();i++){        // 激活标志        bool match = true;        if(loadStr[i] == keyWords[0]){            for(int j = 1;j < keyWords.size();j++){                if(loadStr[i + j] == keyWords[j]){                    match = true;                }else{                    match = false;                    j = keyWords.size();                }            }        }else{            match = false;        }        if(match){            newStr.append(replaceWords);            i += keyWords.size() - 1; // 作用是跳过            num++;        }else{            string temp = ofToString(loadStr[i]);            newStr.append(temp);        }    }    cout << num << endl;    return newStr; } string ofApp::replaceStr(string loadStr,string start,string end,string replaceWords){    string newStr;    int num = 0; // 可统计词数    for(int i = 0;i < loadStr.size();i++){        // 激活标志        bool matchA = true;        if(loadStr[i] == start[0]){            for(int j = 1;j < start.size();j++){                if(loadStr[i + j] == start[j]){                    matchA = true;                }else{                    matchA = false;                    j = start.size();                }            }        }else{            matchA = false;        }        if(matchA){            i += start.size() - 1; // 作用是跳过            // 寻找下一个标记,计算距离            int index = 0;            bool matchB = false;            while(!matchB){                index++;                if(loadStr[i + index] == end[0]){                    for(int j = 1;j < end.size();j++){                        if(loadStr[i + index + j] == end[j]){                            matchB = true;                        }else{                            matchB = false;                            j = end.size();                        }                    }                }else{                    matchB = false;                }            }            i += index + end.size() - 1;            newStr.append(replaceWords);            num++;        }else{            string temp = ofToString(loadStr[i]);            newStr.append(temp);        }    }    cout << num << endl;    return newStr; }

导出文件:



代码浅析

  • 标记语法是自己定义的。只要简洁好记,并且不容易和文本内容混淆即可。

  • 由于原始文档是用 markdown 格式去写的,因而使用时需要将原始文件命名为“ 原始版.md ”,并放到 data 文件夹中。Openframeworks 也可以处理 txt 文档,只要在源代码中将后缀修改成 “.txt” 即可

  • 只要执行一次程序,最终便会在data文件夹生成三个文本,一个用作词汇的替换测试,一个是 Processing 版,一个是 Openframeworks 版

  • ofBuffer 是专门用于存取文件的类型。ofBufferFromFile 用于读取文件,ofBufferToFile 用于保存文件

  • replaceStr 是自定义函数,适用于中英文本的处理。有两种重载方法,参数数量为 3 时,可替换关键字。第一个参数传入文本 string,第二个参数传入希望被替换的关键字,第三个参数传入替换字符。而当参数数量为 4 时,可替换整个段落。第一个参数传入文本 string,第二个参数传入段落标记的前缀,第三个参数传入段落标记的后缀,第四个参数传入替换后的段落。

另一种应用-简单的代码转换器

利用上面的函数,转换下思路。就可以做一个代码转换器,例如将 openframeworks 的代码转换成 processing 的代码。

代码示例

— ofApp.h 内声明

string myStr,newStr;

— ofApp.cpp 内声明

void ofApp::setup(){    // 读取文件    ofBuffer buffer = ofBufferFromFile("ofApp.cpp");    myStr = buffer.getText();    newStr = replaceStr(myStr,"#include \"ofApp.h\""," ");    newStr = replaceStr(newStr,"ofDrawRectangle","rect");    newStr = replaceStr(newStr,"ofBackground","background");    newStr = replaceStr(newStr,"ofSetWindowShape","size");    newStr = replaceStr(newStr,"void ofApp::keyPressed(int key)","void keyPressed()");    newStr = replaceStr(newStr,"void ofApp::keyReleased(int key)","void keyReleased()");    newStr = replaceStr(newStr,"void ofApp::mouseMoved(int x, int y )","void mouseMoved()");    newStr = replaceStr(newStr,"void ofApp::mouseDragged(int x, int y, int button)",                         "void mouseDragged()");    newStr = replaceStr(newStr,"void ofApp::mousePressed(int x, int y, int button)",                        "void mousePressed()");    newStr = replaceStr(newStr,"void ofApp::mouseReleased(int x, int y, int button)",                        "void mouseReleased()");    newStr = replaceStr(newStr,"void ofApp::mouseEntered(int x, int y)","void mouseEntered()");    newStr = replaceStr(newStr,"void ofApp::mouseExited(int x, int y)","void mouseExited()");    newStr = replaceStr(newStr,"void ofApp::windowResized(int w, int h){"," }"," ");    newStr = replaceStr(newStr,"void ofApp::gotMessage(ofMessage msg){"," }"," ");    newStr = replaceStr(newStr,"void ofApp::dragEvent(ofDragInfo dragInfo){"," }"," ");    newStr = replaceStr(newStr,"void ofApp::","void ");    ofBuffer msg(newStr.c_str(), newStr.length());    // 导出为 pde 格式    ofBufferToFile("p5export.pde", msg); }

这是原始的 cpp 文件


这是转换后的 pde 文件(Processing 工程文件)


对于一些简单的程序,有了转换器以后就会非常方便。我们可以基于上面的代码进行扩写,添补更多的函数。

END

对写程序而言,学会复用非常重要,磨刀不误砍柴工。当你把一些流程化的步骤打包好,就能一劳永逸。用得越多,节省的时间就越多。像之前制作的 Gif 模块。如果这件事你只打算做一两次,那可以无需用代码,而是对程序直接录屏,再用别的工具将视频生成 gif 即可。

但如果你确定这个事情是之后会持续去做的,前期就可以花些功夫去探索。结合一些库,把它做成模块,以后就变成几行代码的事情了。假设使用这个模块,每个练习生成一次能节省 5 分钟,那 200 个就能节省 1000 分钟。

当然,换个角度。哪怕花时间一样,甚至略多。我认为都是值得去做的。因为你不是做重复性的枯燥劳动。而是在做一些探索性的,有创造力的工作。

这个思路同样适用于创作。自己一直把 “复用一切” 放到最高的位置。练习不是孤立的,不是为了最终生成那几秒动画才去写它。从大量的练习里,你可以看到图形之间的共性,最终就能进行整合,抽象成一个系统。

所以,尝试把一些东西写成函数,写成类,写成库吧,你就可以有千军万马任意差遣。有这些积累,也就能有更多的精力,去展现头脑中更精彩的画面。


(以上实例可通过点击阅读原文进行下载)
(
)
实例名称:DivideText,OpenframeworksToProcessing

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存