用OpenGL写了一份读取PLY文件并显示的代码,支持以下关键字:

ply/format/element vertex/property/element face/end_header

以及数据类型:

char(int8)/uchar(uint8)/short(int16)/ushort(uint16)/int(int32)/uint(uint32)/float(float32)/double(float64)

并支持变量:

坐标x y (z可选) (w可选)/法向向量nx ny nz/颜色red green blue (alpha可选)

读取之后,利用OpenGL绘制出来,并绘制坐标轴,允许可以通过方向键旋转坐标、‘-’和‘=’缩小、放大。

希望写成C++风格,但是很久没写C++代码了,如有不妥,还望各位看官斧正。

转载需经过本人同意!

///

main.cpp

/********************************************************项目名称:*Copyright (C), SCUT HCII-Lab (http://www.hcii-lab.net/)*模块名称:PLY文件读取与显示*作者:SCUT,EE,石育金*版本号(Version):V1-20150419*指导老师:张鑫********************************************************创建日期:2015.4.19*功能描述:*1,读取.PLY文件,支持关键字 ply/format/element vertex/property/element face/end_header*                  以及数据类型 char(int8)/uchar(uint8)/short(int16)/ushort(uint16)/int(int32)/uint(uint32)/float(float32)/double(float64)*                  并支持property x y (z) (w)/nx ny nz/red green blue (alpha)*2,绘制由.ply文件所描绘的立体,同时绘制坐标系。*       支持箭头↑↓←→旋转坐标系;*      支持=放大,-缩小;*包含文件:*main.cpp/PLYLoader.h/PLYLoader.cpp*修改记录:*2015.04.19修正了读取ubyte数据出错的bug。********************************************************/
#include "PLYLoader.h"GLvoid init();
GLvoid display();
GLvoid reshape(GLint,GLint);
GLvoid keyboard(GLubyte,GLint,GLint);
GLvoid specialKey(GLint,GLint,GLint);
PLYFile *p;void main(int argc, char** argv)
{p=new PLYFile("pyramid.ply");p->print();glutInit(&argc, argv);glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);glutInitWindowSize(800, 600);glutInitWindowPosition(100, 100);glutCreateWindow(argv[0]);init();glutDisplayFunc(display);glutReshapeFunc(reshape);glutKeyboardFunc(keyboard);glutSpecialFunc(specialKey);glutMainLoop();delete p;
}GLvoid init()
{glClearColor (0.0, 0.0, 0.0, 0.0);glShadeModel (GL_SMOOTH);//渐变模式。GL_FLAT:单色模式glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LESS);glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}GLvoid display()
{//PLY display functionp->PLYdisplay();
}GLvoid reshape(GLint w,GLint h)
{p->PLYreshape(w,h);
}GLvoid keyboard(GLubyte key,GLint x,GLint y)
{p->PLYkey(key);
}GLvoid specialKey(GLint key,GLint x,GLint y)
{p->PLYskey(key);
}

PLYLoader.h

/********************************************************项目名称:*Copyright (C), SCUT HCII-Lab (http://www.hcii-lab.net/)*模块名称:PLY文件读取与显示*作者:SCUT,EE,石育金*版本号(Version):V1-20150419*指导老师:张鑫********************************************************创建日期:2015.4.19*功能描述:定义了PLYFile类,用于读取PLY文件,并定义了4个回调函数用于绘制立体或交互。*********************************************************/
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <gl/glut.h>
using namespace std;class PLYFile
{
public:PLYFile(string = "");~PLYFile();GLvoid print();GLboolean PLYdisplay();GLboolean PLYreshape(GLint,GLint);GLboolean PLYkey(GLubyte);GLboolean PLYskey(GLint);
private:GLboolean PLYLoad(string = "");string path;/**///vertex information.For convinence,using vector as containerGLint vertexNum;/*vertex number*/vector<GLvoid *>array;/*save information of vertex,different element in different array*/vector<GLsizei>count;/*number of properties each element contains*/vector<GLenum>propertyType;/*type of property*/vector<GLenum>elementType;/*type of element,must be GL_VERTEX_ARRAY/GL_NORMAL_ARRAY/GL_COLOR_ARRAY*///face informationGLint faceNum;/*face number*/GLuint *indices;/*index of face,indicate the index of vertex that each face contains*///detail for projection translationGLdouble max[3];/*max of x,y,z*/GLdouble min[3];/*min of x,y,z*/GLdouble size;/*length of diagonal line对角线长度*///model translationGLdouble xRotAngle;GLdouble yRotAngle;GLdouble scale[3];
};

PLYLoader.cpp

/********************************************************项目名称:*Copyright (C), SCUT HCII-Lab (http://www.hcii-lab.net/)*模块名称:PLY文件读取与显示*作者:SCUT,EE,石育金*版本号(Version):V1-20150419*指导老师:张鑫********************************************************创建日期:2015.4.19*功能描述:实现PLYFile类*********************************************************/
#include"PLYLoader.h"PLYFile::PLYFile(string p)
{path=p;indices=NULL;max[0]=0;max[1]=0;max[2]=0;min[0]=0;min[1]=0;min[2]=0;xRotAngle=0;yRotAngle=0;PLYLoad();size = sqrt(pow(max[0]-min[0],2)+pow(max[1]-min[1],2)+pow(max[2]-min[2],2));scale[0]=1;scale[1]=1;scale[2]=1;
}PLYFile::~PLYFile()
{if(indices) delete []indices;for(int i=0;i<array.size();i++)delete []array[i];
}
/********************************************************PLYLoad*用于读取ply文件,存入类内的变量*传入参数p是文件路径,默认为空。如果构建类的时候传入了文件路径,此处可以省略。*返回值为1表示读取成功,反之失败。*******************************************************/
GLboolean PLYFile::PLYLoad(string p)
{fstream fopen;if(!p.empty())fopen.open(p,ios::in);else if(!path.empty())fopen.open(path,ios::in);else return 0;if(!fopen.is_open()){return 0;//open file fail!cout<<"read file fail!"<<endl;}//start to readstring filein;while(!fopen.eof()){fopen>>filein;if(filein=="ply"||filein=="PLY"){  //this is the header of .ply file,add TODO here}else if(filein=="comment"){ //this is the comment,add TODO heregetline(fopen,filein,'\n');}else if(filein=="format"){ //this is the format information,add TODO heregetline(fopen,filein,'\n');}else if(filein=="element"){ //element information,only accept vertex and face.ignore user-defined elementfopen>>filein;if(filein=="vertex"){  //read the number of vertexfopen>>vertexNum;}else if(filein=="face"){ //read the number of face fopen>>faceNum;//ignore this and the next linesgetline(fopen,filein,'\n');getline(fopen,filein,'\n');}else{ //user-defined elementcout<<"ignore user-defined element!"<<endl;getline(fopen,filein,'\n');}}else if(filein=="property"){  //property,only accept xyz/nxyz/rgbfopen>>filein;//read in type of elementif(filein=="char"||filein=="int8"){propertyType.push_back(GL_BYTE);}else if(filein=="uchar"||filein=="uint8"){propertyType.push_back(GL_UNSIGNED_BYTE);}else if(filein=="short"||filein=="int16"){propertyType.push_back(GL_SHORT);}else if(filein=="ushort"||filein=="uint16"){propertyType.push_back(GL_UNSIGNED_SHORT);}else if(filein=="int"||filein=="int32"){propertyType.push_back(GL_INT);}else if(filein=="uint"||filein=="uint32"){propertyType.push_back(GL_UNSIGNED_INT);}else if(filein=="float"||filein=="float32"){propertyType.push_back(GL_FLOAT);}else if(filein=="double"||filein=="float64"){propertyType.push_back(GL_DOUBLE);}else{   //use GLdouble as defaultcout<<"Undefined property type!"<<endl;propertyType.push_back(GL_DOUBLE);}fopen>>filein;if(filein=="x"){elementType.push_back(GL_VERTEX_ARRAY);getline(fopen,filein,'\n');//xgetline(fopen,filein,'\n');//ycount.push_back(2);}else if(filein=="nx"){elementType.push_back(GL_NORMAL_ARRAY);getline(fopen,filein,'\n');//nxgetline(fopen,filein,'\n');//nygetline(fopen,filein,'\n');//nzcount.push_back(3);}else if(filein=="red"){elementType.push_back(GL_COLOR_ARRAY);getline(fopen,filein,'\n');//redgetline(fopen,filein,'\n');//greengetline(fopen,filein,'\n');//bluecount.push_back(3);}else if(/*filein=="y"||*/filein=="z"||filein=="w"/*||filein=="green"||filein=="blue"*/||filein=="alpha"){count[count.size()-1]+=1;propertyType.pop_back();}else{cout<<"Unknown property!"<<endl;}}else if(filein=="end_header"){  //header end,add TODO here//ckeckif(count.size()!=propertyType.size()||count.size()!=elementType.size()){cout<<"the number of element collision"<<endl;return 0;}//start to read element information//initialise the arrayGLvoid *subarray=NULL;for(int i=0;i<propertyType.size();i++){switch(propertyType[i]){case GL_BYTE:subarray=new GLbyte[vertexNum*count[i]];array.push_back(subarray);break;case GL_UNSIGNED_BYTE:subarray=new GLubyte[vertexNum*count[i]];array.push_back(subarray);break;case GL_SHORT:subarray=new GLshort[vertexNum*count[i]];array.push_back(subarray);break;case GL_UNSIGNED_SHORT:subarray=new GLushort[vertexNum*count[i]];array.push_back(subarray);break;case GL_INT:subarray=new GLint[vertexNum*count[i]];array.push_back(subarray);break;case GL_UNSIGNED_INT:subarray=new GLuint[vertexNum*count[i]];array.push_back(subarray);break;case GL_FLOAT:subarray=new GLfloat[vertexNum*count[i]];array.push_back(subarray);break;case GL_DOUBLE:subarray=new GLdouble[vertexNum*count[i]];array.push_back(subarray);break;}}if(faceNum)indices=new GLuint[faceNum*3];//read vertex informationint totalelement=0;for(int i=0;i<count.size();i++)totalelement+=count[i];for(int i=0;i<vertexNum;i++)//接下来的vertexNum行都是顶点信息{for(int j=0;j<count.size();j++)//每行包括顶点的count.size()(<=3)种信息for(int k=0;k<count[j];k++)//每种信息包括count[j]个property{switch(propertyType[j]){case GL_BYTE:{GLdouble tem = 0;//(GLdouble)(((GLbyte*)(array[j]))[i*count[j]+k]);fopen>>tem;((GLbyte*)(array[j]))[i*count[j]+k] = GLbyte(tem);if(elementType[j] == GL_VERTEX_ARRAY && k < 3){if(tem > max[k])max[k] = tem;if(tem < min[k])min[k] = tem;}break;}case GL_UNSIGNED_BYTE:{GLint tem = 0;//(GLdouble)(((GLubyte*)(array[j]))[i*count[j]+k]);fopen>>tem;((GLubyte*)(array[j]))[i*count[j]+k] = GLubyte(tem);if(elementType[j] == GL_VERTEX_ARRAY && k < 3){if(tem > max[k])max[k] = tem;if(tem < min[k])min[k] = tem;}break;}case GL_SHORT:fopen>>((GLshort*)(array[j]))[i*count[j]+k];if(elementType[j] == GL_VERTEX_ARRAY && k < 3){GLdouble tem = (GLdouble)(((GLshort*)(array[j]))[i*count[j]+k]);if(tem > max[k])max[k] = tem;if(tem < min[k])min[k] = tem;}break;case GL_UNSIGNED_SHORT:fopen>>((GLushort*)(array[j]))[i*count[j]+k];if(elementType[j] == GL_VERTEX_ARRAY && k < 3){GLdouble tem = (GLdouble)(((GLushort*)(array[j]))[i*count[j]+k]);if(tem > max[k])max[k] = tem;if(tem < min[k])min[k] = tem;}break;case GL_INT:fopen>>((GLint*)(array[j]))[i*count[j]+k];if(elementType[j] == GL_VERTEX_ARRAY && k < 3){GLdouble tem = (GLdouble)(((GLint*)(array[j]))[i*count[j]+k]);if(tem > max[k])max[k] = tem;if(tem < min[k])min[k] = tem;}break;case GL_UNSIGNED_INT:fopen>>((GLuint*)(array[j]))[i*count[j]+k];if(elementType[j] == GL_VERTEX_ARRAY && k < 3){GLdouble tem = (GLdouble)(((GLuint*)(array[j]))[i*count[j]+k]);if(tem > max[k])max[k] = tem;if(tem < min[k])min[k] = tem;}break;case GL_FLOAT:{fopen>>((GLfloat*)(array[j]))[i*count[j]+k];if(elementType[j] == GL_VERTEX_ARRAY && k < 3){GLdouble tem = (GLdouble)(((GLfloat*)(array[j]))[i*count[j]+k]);if(tem > max[k])max[k] = GLdouble(tem);if(tem < min[k])min[k] = GLdouble(tem);}break;}case GL_DOUBLE:fopen>>((GLdouble*)(array[j]))[i*count[j]+k];if(elementType[j] == GL_VERTEX_ARRAY && k < 3){GLdouble tem = ((GLdouble*)(array[j]))[i*count[j]+k];if(tem > max[k])max[k] = tem;if(tem < min[k])min[k] = tem;}break;}}}//read face informationint front=0,i=0;;fopen>>front;while(front==3 && i<faceNum){fopen>>indices[i*3]>>indices[i*3+1]>>indices[i*3+2];fopen>>front;i++;}}else{continue;}}fopen.close();return 1;//finished
}
/********************************************************PLYdisplay*显示函数,需放在glutDisplayFunc()定义的回调函数内调用*返回值可以忽略*******************************************************/
GLboolean PLYFile::PLYdisplay()
{glClearColor(0.0, 0.0, 0.0, 0.0);glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glPushMatrix();//scaleglScalef(scale[0],scale[1],scale[2]);//rotateglRotatef(xRotAngle,1.0,0.0,0.0);glRotatef(yRotAngle,0.0,1.0,0.0);//smoothglEnable(GL_POINT_SMOOTH);glHint(GL_POINT_SMOOTH,GL_NICEST);glEnable(GL_LINE_SMOOTH);glHint(GL_LINE_SMOOTH,GL_NICEST);glEnable(GL_POLYGON_SMOOTH);glHint(GL_POLYGON_SMOOTH,GL_NICEST);//draw coordinate systemglColor3f(1.0,1.0,1.0);glBegin(GL_LINES);glVertex3f((min[0]<0)?(1.4*(min[0])):(-0.8*min[0]),0.0,0.0);glVertex3f(1.8*(max[0]),0.0,0.0);glVertex3f(0.0,(min[1]<0)?(1.4*(min[1])):(-0.8*min[1]),0.0);glVertex3f(0.0,1.8*(max[1]),0.0);glVertex3f(0.0,0.0,(min[2]<0)?(1.4*(min[2])):(-0.8*min[2]));glVertex3f(0.0,0.0,1.8*(max[2]));glEnd();glPushMatrix();glTranslatef(1.8*(max[0]),0.0,0.0);glRotatef(90.0,0.0,1.0,0.0);glutSolidCone(0.02*size,0.04*size,10,10);glPopMatrix();glPushMatrix();glTranslatef(0.0,1.8*(max[1]),0.0);glRotatef(-90.0,1.0,0.0,0.0);glutSolidCone(0.02*size,0.04*size,10,10);glPopMatrix();glPushMatrix();glTranslatef(0.0,0.0,1.8*(max[2]));glRotatef(90.0,0.0,0.0,1.0);glutSolidCone(0.02*size,0.04*size,10,10);glPopMatrix();//draw objectglColor3f(1.0,1.0,1.0);//enable and assignfor(int i=0;i<(elementType.size());i++){switch(elementType[i]){case GL_VERTEX_ARRAY:glEnableClientState(GL_VERTEX_ARRAY);glVertexPointer(count[i],propertyType[i],0,array[i]);break;case GL_NORMAL_ARRAY:glEnableClientState(GL_NORMAL_ARRAY);glNormalPointer(propertyType[i],0,array[i]);break;case GL_COLOR_ARRAY:glEnableClientState(GL_COLOR_ARRAY);glColorPointer(count[i],propertyType[i],0,array[i]);break;}}//use arrayfor(int i=0;i<faceNum;i++){glDrawElements(GL_TRIANGLES,3,GL_UNSIGNED_INT,&(indices[i*3]));}glPopMatrix();glutSwapBuffers();return 1;
}
/********************************************************PLYreshape*重绘函数,需放在glutReshapeFunc()定义的回调函数内调用*返回值可以忽略*******************************************************/
GLboolean PLYFile::PLYreshape(GLint w,GLint h)
{glViewport (0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode (GL_PROJECTION);glLoadIdentity ();gluPerspective(60.0, (GLfloat) w/(GLfloat) h, size * 0.05, size*5);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt (max[0]*3, max[1]*3, max[2]*3,/*eye position, changeable*/0.0, 0.0, 0.0,/*a point that on the sight line. FIXED to zero point*/-max[0]*3, -max[1]*3, (pow(max[0],2)+max[1],2)*3/max[2]/*Specifies the direction of the up vector, vertical to the sight line*/);return 1;
}
/********************************************************PLYkey*普通按键处理函数,需放在glutKeyboardFunc()定义的回调函数内调用*返回值可以忽略*******************************************************/
GLboolean PLYFile::PLYkey(GLubyte key)
{switch(key){case '=':scale[0]*=1.1;scale[1]*=1.1;scale[2]*=1.1;break;case '-':     scale[0]*=0.9;scale[1]*=0.9;scale[2]*=0.9;break;}glutPostRedisplay();return 1;
}
/********************************************************PLYskey*特殊按键处理函数,需放在glutSpecialFunc()定义的回调函数内调用*返回值可以忽略*******************************************************/
GLboolean PLYFile::PLYskey(GLint key)
{switch(key){case GLUT_KEY_UP:xRotAngle-=5.0;break;case GLUT_KEY_DOWN:xRotAngle+=5.0;break;case GLUT_KEY_RIGHT:yRotAngle+=5.0;break;case GLUT_KEY_LEFT:yRotAngle-=5.0;break;}glutPostRedisplay();return 1;
}
/********************************************************print*打印函数。将读入的问件内容显示出来。用于调试。*******************************************************/
GLvoid PLYFile::print()
{cout<<"PLY file"<<endl;cout<<"path:"<<path<<endl;cout<<"VertexNum:"<<vertexNum<<endl;cout<<"property number:"<<array.size()<<endl<<count.size()<<endl<<propertyType.size()<<endl<<elementType.size()<<endl;cout<<"count:"<<endl;for(int i=0;i<count.size();i++)cout<<i<<" : "<<count[i]<<endl;cout<<"propertyType"<<endl;for(int i=0;i<propertyType.size();i++)cout<<i<<" : "<<propertyType[i]<<endl;cout<<"elementType"<<endl;for(int i=0;i<elementType.size();i++)cout<<i<<" : "<<elementType[i]<<endl;cout<<"max:"<<endl;cout<<max[0]<<'\t'<<max[1]<<'\t'<<max[2]<<'\t'<<endl;cout<<"min:"<<endl;cout<<min[0]<<'\t'<<min[1]<<'\t'<<min[2]<<'\t'<<endl;cout<<"size: "<<size<<endl;cout<<"faceNum:"<<faceNum<<endl;//cout<<"array:"<<endl;//for(int i=0;i<array.size();i++)//{//  for(int j=0;j<vertexNum*count[i];j++)//       cout<<array[i][j]<<endl;//}//cout<<"indices:"<<endl;//for(int i=0;i<faceNum;i++)//{//   for(int j=0;j<3;j++)//        cout<<indices[i*3+j]<<"\t";//    cout<<endl;//}
}

比如读取下面这个文件:

pyramid.ply

ply
format ascii 1.0
comment created by MATLAB ply_write
element vertex 5
property float x
property float y
property float z
property float red
property float green
property float blue
element face 6
property list uchar char vertex_indices
end_header
0.000000 0.000000 0.000000 1.0 1.0 1.0
1.000000 0.000000 0.000000 1.0 0.0 0.0
1.000000 1.000000 0.000000 0.0 0.0 1.0
0.000000 1.000000 0.000000 0.0 1.0 0.0
0.500000 0.500000 1.600000 1.0 1.0 0.0
3 1 0 3
3 1 3 2
3 0 1 4
3 0 4 3
3 3 4 2
3 1 2 4

效果如图:

利用方向键和‘-’、‘=’键,可以看得立体图形的全貌。

PLY文件读取与显示相关推荐

  1. 〖3D激光点云〗激光雷达点云bin文件读取和显示!

    激光雷达点云bin文件读取和显示! 文章目录 一. 代码 二. 结果 三. 显示 四. 参考 首先声明文章主要参考:Ronny Restrepo!其中包括如下的内容. 点云数据的介绍 图像和点云坐标系 ...

  2. 【KITTI数据集】VELODYNE POINT CLOUD-激光雷达点云BIN文件读取和显示-OPEN3D

    1.在anaconda环境下: conda install -c open3d-admin open3d 缺少其他模块的话,pip安装就可以 2.点云的读取和显示 import os import n ...

  3. MATLAB图像基本操作(信息查询/文件读取/写入/显示)

    本博文主要讲解matlab下的图像基本操作,图片使用下方的一张图片,只需要右击收藏就行了.给出讲解内容有: 文章目录 1.图像信息查询 1.1语法格式 1.2 案例1 2.图像文件读取 2.1 语法格 ...

  4. CloudCompare源码分析:读取ply文件

    CloudCompare源码分析_读取ply文件 写这些博客的原因,是因为打算好好研究一下点云的各种库的源码,其中比较知名的是PCL(point cloud library)和CC(CloudComp ...

  5. Matlab GUI - 文件读取

    目录 uigetifile文件读取 路径显示 uigetifile文件读取 参考Matlab论坛 用户js4624331 matlab编程中有时想调入电脑中的某个文件,现找到了一些关于uigetifi ...

  6. Matlab常用函数集合(各类文件读取/保存、数据转换、处理)——持续更新中

    目录 一.各类文件读取.显示.保存 0.文件/文件夹 0.1.判断文件是否存在 1.图像文件 1.1.图片读取 1.2.图片显示 1.3.图片保存 2.音频文件 2.1 音频文件读取 2.2 音频文件 ...

  7. MATLAB对ply文件格式的读取和显示

    转自:https://blog.csdn.net/lafengxiaoyu/article/details/60574150 在网上搜索这个题目可以找到一些类似的文章,其来源大致都是http://pe ...

  8. PCL读取ply文件

    PCL读取ply文件 PCL读取ply文件 PCL读取ply文件 以下是利用PCL库读取ply文件代码 int readcloud(pcl::PointCloud<pcl::PointXYZ&g ...

  9. matlab显示YCrCb的图像,【Matlab系列】读取并显示YUV视频文件

    Date: 2019-5-12 1.读取并显示YUV视频文件Matlab代码 %% 1.读取视频内容并显示 fid = fopen('akiyo_cif.yuv','r'); %读入YUV文件 row ...

最新文章

  1. dnf拍卖行计算机在线,DNF助手拍卖行在哪里 拍卖行物价实时查询
  2. Python高阶函数和函数嵌套
  3. CALL注入--扫雷辅助(二)
  4. Jenkins file一行代码部署.NET程序到K8S
  5. 19日下午三点直播:DevOps体系中数据库端的四大问题及解决之道
  6. Spring-tx-TransactionStatus接口(savepoint)
  7. Exchange Server 2016管理系列课件22.通讯组概述
  8. 从排列与组合的python实现到生日问题的解释
  9. Blog访问量提升秘笈
  10. g2o:一种图优化的C++框架
  11. 今天吃什么随机网页_初秋应该多吃什么水果?饮食禁忌有哪些?今天燕多多跟你一起探究...
  12. 线性代数学习笔记(一)——二阶和三阶行列式
  13. idea java文件重命名_IDEA项目重命名的操作
  14. 自动透视校正为四边形对象
  15. vue 使用fs_在vue里面使用iVew框架
  16. bugkuctf 游戏通关玄学式速通
  17. Java实现对文件的增删改查操作
  18. 微型计算机的内存乘储器,微型计算机及接口技术名词解释题及解答题
  19. Momenta Mpilot Parking 自主泊车(APA/AVP)方案
  20. promethues+alertmanager+grafana监控docker容器和报警—基于手动配置和文件自动发现—详细文档

热门文章

  1. 软件测试基础-Web前端(二)
  2. 计算机一级解释,独家秘笈计算机一级错题解释.ppt
  3. 神奇英语语法系列(1)——定语从句
  4. Studing Day1 - python基础1
  5. 2018新版个税计算器---Python实现
  6. android虚拟机启动不了,android虚拟机adb不能启动情况汇总
  7. python自制免费代理IP服务
  8. 导弹发射-河南省第九届省赛D题
  9. 使用Arduino Tone()函数演奏旋律
  10. GDK8——强大的Linux内核调试工具