引言:
最近实现课程项目实践考核,要求将自动机生成的中间结果以图的形式展现出来。就随手学了一下Graphviz这个软件,非常的简单好用,下面将使用C++函数利用Graphviz生成对应图片的方法进行如下总结。
安装和配资:
安装和配置十分简单,下面以windows为例:
(1)下载:
在官网直接进行下载:https://graphviz.gitlab.io/download/
下载2.38稳定版的.msi文件
(2)安装配置
下载后直接双击可执行文件进行安装即可,记住安装路径,方便之后的环境变量的配置。
例如我的安装路径为:D:\graphviz
我们将 D:\graphviz\bin 添加到 环境变量Path中:
配置完成后使用可能需要重启电脑,使环境变量修改生效
dot基本语法:
(1)打开Gvedit.exe:安装完成后不会生成快捷方式,要在相关路径中查找,查找到后可以生成方式,保存在桌面。
(2)基本语法:
在.gv文件中输入下面代码,按F5生成图片。
digraph G{
a -> b[label="{f}"];
a -> c[label="{g}"];
b -> c[label="{s}"];
}
我们看看代码就可以知道
在大括号{ }中每一个;号代表一条边
-> 两遍代表有向边的两个节点名
[label=“xxxxx”]代表边上的转移关系
更多更详细的有关画图属性的语法说明,可以上网搜索
(3)cmd命令行执行:
** Step 1: ** 首先,需要编辑dot脚本
可以使用你熟悉的纯文本编辑器进行脚本编写(必须是纯文本编辑器,如vim、notepad++,像word这样的富文本编辑器是不行的),只需设置编码为UTF-8。
编辑下面的脚本代码,保存为test.dot(先不用管其具体的意思,直接复制就行了):
digraph G{
main -> parse -> execute;
main -> init;
main -> cleanup;
execute -> make_string;
execute -> printf;
init -> make_string;
main -> printf;
execute -> compare;
}
Step 2: 随后,选用布局生成结果
使用如下命令生成结果:
dot -Tpng sample.dot -o sample.png
对于这条命令,dot表示用dot布局,-Tpng表示生成png图片格式,sample.dot是脚本文件名,-o sample.png表示生成输出的图片名称。
改命令也可以写成dot -Kdot -Tpng sample.dot -o sample.png,其中-Kdot表示使用dot布局。
C++函数实现图片生成: 环境codeblocks
(1)最简单的调用:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
void gen_graphic()
{
freopen("graph.dot","w",stdout);
cout<<"digraph G{"<<endl;
cout<<"main -> parse -> execute;"<<endl;
cout<<"main -> init;"<<endl;
cout<<"}"<<endl;
fclose(stdout);
system("dot -Tpng graph.dot -o sample.png");
}
int main()
{
gen_graphic();
return 0;
}
将相关dot语句直接使用cout写入graph.dot文件中
调用system函数执行系统命令 dot -Tpng graph.dot -o sample.png 生成图片。
从上面的例子我们可以看出,使用Graohviz生成图片的关键在于,我们只需要生成所需拼接处表示边的字符串即可。
(2)动态生成有向图:
在codeblocks中添加grapg_in.txt文件,其中包含建图需要的四元组信息。(以从某一txt文件中读入动态信息为例,也可以直接从某一数组或数据结构中读入数据)。包含以下信息:
({f},s,{f,g},{null})
({f},q,{f},{g})
({f},p,{g},{f})
({g},s,{f,g},{null})
({g},q,{f},{g})
({null},true,{f,g},{null})
一个四元组表示一条有向边,第一个元素为边尾节点,最后一个元素为边头结点,中间两个元素为边上转移关系。
使用以下函数生成对应的graph.dot文件:
执行 readGraph(“graph_in.txt”,“graph.dot”);
//参数说明:
//string input:包含四元组的txt文件名
//string output:对应生成的dot文件名
void readGraph(string input,string output) //.txt 转 .dot
{
const char* in = input.data();
const char* ou = output.data();
freopen(in,"r",stdin);
freopen(ou,"w",stdout);
cout<<"digraph G{"<<endl;
string s;
while(cin>>s)
{
string u,v,lable;
int n = s.length();
int i = 1;
u = "\"";
while(s[i] != ',' || s[i-1] != '}') i++;
u += s.substr(1,i-1);
u += "\"";
v = "\"";
int j = n-2;
while(s[j] != ',' || s[j+1] != '{') j--;
v += s.substr(j+1,n-1-j-1);
v += "\"";
lable = s.substr(i+1,j-i-1);
string edge = "";
edge += u;
edge += "->";
edge += v;
edge += "[label=\"";
edge += lable;
edge += "\"];";
cout<<edge<<endl;
}
cout<<"}"<<endl;
fclose(stdin);
fclose(stdout);
}
结果:
根据生成的dot文件生成图片:
执行: makeGraph(“graph.dot”,“graph4.png”);
//参数说明:
//string inputname:dot文件名
//string outputname:生成的png文件名
void makeGraph(string inputname,string outputname) //生成png图片
{
string s = "";
s += "dot -Tpng ";
s += inputname;
s += " -o ";
s += outputname;
const char* cmd = s.data();
const char* iname = inputname.data();
system(cmd);
}
结果:
完整代码:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
void readGraph(string input,string output) //.txt 转 .dot
{
const char* in = input.data();
const char* ou = output.data();
freopen(in,"r",stdin);
freopen(ou,"w",stdout);
cout<<"digraph G{"<<endl;
string s;
while(cin>>s)
{
string u,v,lable;
int n = s.length();
int i = 1;
u = "\"";
while(s[i] != ',' || s[i-1] != '}') i++;
u += s.substr(1,i-1);
u += "\"";
v = "\"";
int j = n-2;
while(s[j] != ',' || s[j+1] != '{') j--;
v += s.substr(j+1,n-1-j-1);
v += "\"";
lable = s.substr(i+1,j-i-1);
string edge = "";
edge += u;
edge += "->";
edge += v;
edge += "[label=\"";
edge += lable;
edge += "\"];";
cout<<edge<<endl;
}
cout<<"}"<<endl;
fclose(stdin);
fclose(stdout);
}
void makeGraph(string inputname,string outputname) //生成png图片
{
string s = "";
s += "dot -Tpng ";
s += inputname;
s += " -o ";
s += outputname;
const char* cmd = s.data();
const char* iname = inputname.data();
system(cmd);
}
void gen_graphic()
{
freopen("graph.dot","w",stdout);
cout<<"digraph G{"<<endl;
cout<<"main -> parse -> execute;"<<endl;
cout<<"main -> init;"<<endl;
cout<<"}"<<endl;
fclose(stdout);
system("dot -Tpng graph.dot -o sample.png");
}
int main()
{
// gen_graphic();
readGraph("graph_in.txt","graph.dot");
makeGraph("graph.dot","graph4.png");
return 0;
}