NeHe OpenGL第三十三课:TGA文件
NeHe OpenGL第三十三课:TGA文件
加载压缩和未压缩的TGA文件:
在这一课里,你将学会如何加载压缩和为压缩的TGA文件,由于它使用RLE压缩,所以非常的简单,你能很快地熟悉它的。
我见过很多人在游戏开发论坛或其它地方询问关于TGA读取的问题。接下来的程序及注释将会向你展示如何读取未压缩的TGA文件和RLE压缩的文件。这个详细的教程适合于OpenGL,但是我计划改进它使其在将来更具普遍性。
我们将从两个头文件开始。第一个文件控制纹理结构,在第二个里,结构和变量将为程序读取所用。
就像每个头文件那样,我们需要一些包含保护措施以防止文件被重复包含。
在文件的顶部加入这样几行程序:
#ifndef __TEXTURE_H__ // 看看此头文件是否已经被包含
#define __TEXTURE_H__ // 如果没有,定义它
然后滚动到程序底部并添加:
#endif // __TEXTURE_H__ 结束包含保护
这三行程序防止此文件被重复包含。文件中剩下的代码将处于这头两行和这最后一行之间。
在这个头文件中,我们将要加入完成每件工作所需的标准头文件。在#define __TGA_H__后添加如下几行:
#pragma comment(lib, "OpenGL32.lib") // 链接 Opengl32.lib
#include <windows.h> // 标准Windows头文件
#include <stdio.h> // 标准文件I/O头文件
#include <gl\gl.h> // 标准OpenGL头文件
第一个头文件是标准Windows头文件,第二个是为我们稍后的文件I/O所准备的,第三个是OpenGL32.lib所需的标准OpenGL头文件。
我们将需要一块空间存储图像数据以及OpenGL生成纹理所需的类型。我们将要用到以下结构:
typedef struct
{
GLubyte* p_w_picpathData; // 控制整个图像的颜色值
GLuint bpp; // 控制单位像素的bit数
GLuint width; // 整个图像的宽度
GLuint height; // 整个图像的高度
GLuint texID; // 使用glBindTexture所需的纹理ID.
GLuint type; // 描述存储在*ImageData中的数据(GL_RGB Or GL_RGBA)
} Texture;
现在说说其它的,更长的头文件。同样我们需要一些包含保护措施,这和上述最后一个是一样的。
接下来,看看另外两个结构,它们将在处理TGA文件的过程中使用。
typedef struct
{
GLubyte Header[12]; // 文件头决定文件类型
} TGAHeader;
typedef struct
{
GLubyte header[6]; // 控制前6个字节
GLuint bytesPerPixel; // 每像素的字节数 (3 或 4)
GLuint p_w_picpathSize; // 控制存储图像所需的内存空间
GLuint type; // 图像类型 GL_RGB 或 GL_RGBA
GLuint Height; // 图像的高度
GLuint Width; // 图像宽度
GLuint Bpp; // 每像素的比特数 (24 或 32)
} TGA;
现在我们声明那两个结构的一些实例,那样我们可以在程序中使用它们。
TGAHeader tgaheader; // 用来存储我们的文件头
TGA tga; // 用来存储文件信息
我们需要定义一对文件头,那样我们能够告诉程序什么类型的文件头处于有效的图像上。如果是未压缩的TGA图像,前12字节将会是0 0 2 0 0 0 0 0 0 0 0 0,如果是RLE压缩的,则是0 0 10 0 0 0 0 0 0 0 0 0。这两个值允许我们检查正在读取的文件是否有效。
// 未压缩的TGA头
GLubyte uTGAcompare[12] = {0,0, 2,0,0,0,0,0,0,0,0,0};
// 压缩的TGA头
GLubyte cTGAcompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0};
最后,我们声明两个函数用于读取过程。
// 读取一个未压缩的文件
bool LoadUncompressedTGA(Texture *, char *, FILE *);
// 读取一个压缩的文件
bool LoadCompressedTGA(Texture *, char *, FILE *);
现在,回到cpp文件,和程序中真正首当其冲部分,我将会省去一些错误消息处理代码并且使教程更短、更具可读性。你可以参看教程包含的文件(在文章的尾部有链接)。
马上,我们就可以在文件开头包含我们刚刚建立的头文件。
#include "tga.h" // 包含我们刚刚建立的头文件
不我们不需要包含其它任何文件了,因为我们已经在自己刚刚完成的头文件中包含他们了。
接下来,我们要做的事情是看看第一个函数,名为LoadTGA(…)。
// 读取一个TGA文件!
bool LoadTGA(Texture * texture, char * filename)
{
它有两个参数。前者是一个指向纹理结构的指针,你必须在你的代码中声明它(见包含的例子)。后者是一个字符串,它告诉计算机在哪里去找你的纹理文件。
函数的前两行声明了一个文件指针,然后打开由“filename”参数指定的文件,它由函数的第二个指针传递进去。
FILE * fTGA; // 声明文件指针
fTGA = fopen(filename, "rb"); // 以读模式打开文件
接下来的几行检查指定的文件是否已经正确地打开。
if(fTGA == NULL) // 如果此处有错误
{
...Error code...
return false; // 返回 False
}
下一步,我们尝试读取文件的首12个字节的内容并且将它们存储在我们的TGAHeader结构中,这样,我们得以检查文件类型。如果fread失败,则关闭文件,显示一个错误,并且函数返回false。
if(fread(&tgaheader, sizeof(TGAHeader), 1, fTGA) == 0)
{
...Error code here...
return false; // 如果失败则返回 False
}
接着,通过我们用辛苦编的程序刚读取的头,我们继续尝试确定文件类型。这可以告诉我们它是压缩的、未压缩甚至是错误的文件类型。为了达到这个目的,我们将会使用memcmp(…)函数。
// 如果文件头附合未压缩的文件头格式
if(memcmp(uTGAcompare, &tgaheader, sizeof(tgaheader)) == 0)
{
// 读取未压缩的TGA文件
LoadUncompressedTGA(texture, filename, fTGA);
}
// 如果文件头附合压缩的文件头格式
else if(memcmp(cTGAcompare, &tgaheader, sizeof(tgaheader)) == 0)
{
// 读取压缩的TGA格式
LoadCompressedTGA(texture, filename, fTGA);
}
else // 如果任一个都不符合
{
...Error code here...
return false; // 返回 False
}
我们将要开始读取一个未压缩格式文件的章节。
下面开始我们要做的第一件事,像往常一样,是函数头。
//读取未压缩的TGA文件
bool LoadUncompressedTGA(Texture * texture, char * filename, FILE * fTGA)
{
这个函数有3个参数。头两个和LoadTGA中的一样,仅仅是简单的传递。第三个是来自前一个函数中的文件指针,因此我们没有丢失我们的空间。
接下来我们试着再从文件中读取6个字节的内容,并且存储在tga.header中。如果他失败了,我们运行一些错误处理代码,并且返回false。
// 尝试继续读取6个字节的内容
if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0)
{
...Error code here...
return false; // 返回 False
}
现在我们有了计算图像的高度、宽度和BPP的全部信息。我们在纹理和本地结构中都将存储它。
texture->width = tga.header[1] * 256 + tga.header[0]; // 计算高度
texture->height = tga.header[3] * 256 + tga.header[2]; // 计算宽度
texture->bpp = tga.header[4]; // 计算BPP
tga.Width = texture->width; // 拷贝Width到本地结构中去
tga.Height = texture->height; // 拷贝Height到本地结构中去
tga.Bpp = texture->bpp; // 拷贝Bpp到本地结构中去
现在,我们需要确认高度和宽度至少为1个像素,并且bpp是24或32。如果这些值中的任何一个超出了它们的界限,我们将再一次显示一个错误,关闭文件,并且离开此函数。
// 确认所有的信息都是有效的
if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32)))
{
...Error code here...
return false; // 返回 False
}
接下来我们设置图像的类型。24 bit图像是GL_RGB,32 bit 图像是GL_RGBA
if(texture->bpp == 24) // 是24 bit图像吗?
{
texture->type = GL_RGB; //如果是,设置类型为GL_RGB
}
else // 如果不是24bit,则必是32bit
{
texture->type = GL_RGBA; //这样设置类型为GL_RGBA
}
现在我们计算每像素的字节数和总共的图像数据。
tga.bytesPerPixel = (tga.Bpp / 8); // 计算BPP
// 计算存储图像所需的内存
tga.p_w_picpathSize = (tga.bytesPerPixel * tga.Width * tga.Height);
我们需要一些空间去存储整个图像数据,因此我们将要使用malloc分配正确的内存数量
然后我们确认内存已经分配,并且它不是NULL。如果出现了错误,则运行错误处理代码。
// 分配内存
texture->p_w_picpathData = (GLubyte *)malloc(tga.p_w_picpathSize);
if(texture->p_w_picpathData == NULL) // 确认已经分配成功
{
...Error code here...
return false; // 确认已经分配成功
}
这里我们尝试读取所有的图像数据。如果不能,我们将再次触发错误处理代码。
// 尝试读取所有图像数据
if(fread(texture->p_w_picpathData, 1, tga.p_w_picpathSize, fTGA) != tga.p_w_picpathSize)
{
...Error code here...
return false; // 如果不能,返回false
}
TGA文件用逆OpenGL需求顺序的方式存储图像,因此我们必须将格式从BGR到RGB。为了达到这一点,我们交换每个像素的第一个和第三个字节的内容。
Steve Thomas补充:我已经编写了能稍微更快速读取TGA文件的代码。它涉及到仅用3个二进制操作将BGR转换到RGB的方法。
然后我们关闭文件,并且成功退出函数。
// 开始循环
for(GLuint cswap = 0; cswap < (int)tga.p_w_picpathSize; cswap += tga.bytesPerPixel)
{
// 第一字节 XOR第三字节XOR 第一字节 XOR 第三字节
texture->p_w_picpathData[cswap] ^= texture->p_w_picpathData[cswap+2] ^=
texture->p_w_picpathData[cswap] ^= texture->p_w_picpathData[cswap+2];
}
fclose(fTGA); // 关闭文件
return true; // 返回成功
}
以上是读取未压缩型TGA文件的方法。读取RLE压缩型文件的步骤稍微难一点。我们像平时一样读取文件头并且收集高度/宽度/色彩深度,这和读取未压缩版本是一致的。
bool LoadCompressedTGA(Texture * texture, char * filename, FILE * fTGA)
{
if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0)
{
...Error code here...
}
texture->width = tga.header[1] * 256 + tga.header[0];
texture->height = tga.header[3] * 256 + tga.header[2];
texture->bpp = tga.header[4];
tga.Width = texture->width;
tga.Height = texture->height;
tga.Bpp = texture->bpp;
if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32)))
{
...Error code here...
} }
tga.bytesPerPixel = (tga.Bpp / 8);
tga.p_w_picpathSize = (tga.bytesPerPixel * tga.Width * tga.Height);
现在我们需要分配存储图像所需的空间,这是为我们解压缩之后准备的,我们将使用malloc。如果内存分配失败,运行错误处理代码,并且返回false。
// 分配存储图像所需的内存空间
texture->p_w_picpathData = (GLubyte *)malloc(tga.p_w_picpathSize);
if(texture->p_w_picpathData == NULL) // 如果不能分配内存
{
...Error code here...
return false; // 返回 False
}
下一步我们需要决定组成图像的像素数。我们将它存储在变量“pixelcount”中。
我们也需要存储当前所处的像素,以及我们正在写入的图像数据的字节,这样避免溢出写入过多的旧数据。
我们将要分配足够的内存来存储一个像素。
GLuint pixelcount = tga.Height * tga.Width; // 图像中的像素数
GLuint currentpixel = 0; // 当前正在读取的像素
GLuint currentbyte = 0; // 当前正在向图像中写入的像素
// 一个像素的存储空间
GLubyte * colorbuffer = (GLubyte *)malloc(tga.bytesPerPixel);
接下来我们将要进行一个大循环。
让我们将它分解为更多可管理的块。
首先我们声明一个变量来存储“块”头。块头指示接下来的段是RLE还是RAW,它的长度是多少。如果一字节头小于等于127,则它是一个RAW头。头的值是颜色数,是负数,在我们处理其它头字节之前,我们先读取它并且拷贝到内存中。这样我们将我们得到的值加1,然后读取大量像素并且将它们拷贝到ImageData中,就像我们处理未压缩型图像一样。如果头大于127,那么它是下一个像素值随后将要重复的次数。要获取实际重复的数量,我们将它减去127以除去1bit的的头标示符。然后我们读取下一个像素并且依照上述次数连续拷贝它到内存中。
do // 开始循环
{
GLubyte chunkheader = 0; // 存储Id块值的变量
if(fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0) // 尝试读取块的头
{
...Error code...
return false; // If It Fails, Return False
}
接下来我们将要看看它是否是RAW头。如果是,我们需要将此变量的值加1以获取紧随头之后的像素总数。
if(chunkheader < 128) // 如果是RAW块
{
chunkheader++; // 变量值加1以获取RAW像素的总数
我们开启另一个循环读取所有的颜色信息。它将会循环块头中指定的次数,并且每次循环读取和存储一个像素。
首先,我们读取并检验像素数据。单个像素的数据将被存储在colorbuffer变量中。然后我们将检查它是否为RAW头。如果是,我们需要添加一个到变量之中以获取头之后的像素总数。
// 开始像素读取循环
for(short counter = 0; counter < chunkheader; counter++)
{
// 尝试读取一个像素
if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel)
{
...Error code...
return false; // 如果失败,返回false
}
我们循环中的下一步将要获取存储在colorbuffer中的颜色值并且将其写入稍后将要使用的p_w_picpathData变量中。在这个过程中,数据格式将会由BGR翻转为RGB或由BGRA转换为RGBA,具体情况取决于每像素的比特数。当我们完成任务后我们增加当前的字节和当前的像素计数器。
texture->p_w_picpathData[currentbyte] = colorbuffer[2]; // 写“R”字节
texture->p_w_picpathData[currentbyte + 1 ] = colorbuffer[1]; //写“G”字节
texture->p_w_picpathData[currentbyte + 2 ] = colorbuffer[0]; // 写“B”字节
if(tga.bytesPerPixel == 4) // 如果是32位图像...
{
texture->p_w_picpathData[currentbyte + 3] = colorbuffer[3]; // 写“A”字节
}
// 依据每像素的字节数增加字节计数器
currentbyte += tga.bytesPerPixel;
currentpixel++; // 像素计数器加1
下一段处理描述RLE段的“块”头。首先我们将chunkheader减去127来得到获取下一个颜色重复的次数。
else // 如果是RLE头
{
chunkheader -= 127; // 减去127获得ID Bit的Rid
然后我们尝试读取下一个颜色值。
// 读取下一个像素
if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel)
{
...Error code...
return false; // 如果失败,返回false
}
接下来,我们开始循环拷贝我们多次读到内存中的像素,这由RLE头中的值规定。
然后,我们将颜色值拷贝到图像数据中,预处理R和B的值交换。
随后,我们增加当前的字节数、当前像素,这样我们再次写入值时可以处在正确的位置。
// 开始循环
for(short counter = 0; counter < chunkheader; counter++)
{
// 拷贝“R”字节
texture->p_w_picpathData[currentbyte] = colorbuffer[2];
// 拷贝“G”字节
texture->p_w_picpathData[currentbyte + 1 ] = colorbuffer[1];
// 拷贝“B”字节
texture->p_w_picpathData[currentbyte + 2 ] = colorbuffer[0];
if(tga.bytesPerPixel == 4) // 如果是32位图像
{
// 拷贝“A”字节
texture->p_w_picpathData[currentbyte + 3] = colorbuffer[3];
}
currentbyte += tga.bytesPerPixel; // 增加字节计数器
currentpixel++; // 增加字节计数器
只要仍剩有像素要读取,我们将会继续主循环。
最后,我们关闭文件并返回成功。
while(currentpixel < pixelcount); // 是否有更多的像素要读取?开始循环直到最后
fclose(fTGA); // 关闭文件
return true; // 返回成功
}
原文及其个版本源代码下载:
http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=33
转载于:https://blog.51cto.com/yarin/381885
相关文章:

阿里自动驾驶新突破!达摩院自研ISP图像处理器大幅提升安全性
阿里巴巴达摩院在自动驾驶领域取得新突破!4月8日,据记者了解,达摩院已经自主研发出用于车载摄像头的ISP处理器,保障自动驾驶车辆在夜间拥有更好的“视力”,“看”得更清晰,从而大幅提升自动驾驶安全性, 而背…

3月14号作业
<!DOCTYPE html> <html> <head lang"en"><meta charset"UTF-8"><title></title> </head> <body> <br/> <br/> <img src"51job表单_03.gif"</br> <br/> <br/>…

NeHe OpenGL第三十五课:播放AVI
NeHe OpenGL第三十五课:播放AVI 在OpenGL中播放AVI: 在OpenGL中如何播放AVI呢?利用Windows的API把每一帧作为纹理绑定到OpenGL中,虽然很慢,但它的效果不错。你可以试试。 首先我得说我非常喜欢这一章节.Jonathan de Blok使我产生…
为什么TCP的TIME_WAIT状态要保持2MSL?
TIMEWAIT状态也称为 2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。我们知道这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生…
深度 | 一文读懂“情感计算”在零售中的应用发展
作者 | 黄程韦博士、刘刚、包飞博士、杨现博士、孙皓博士、沈艺博士来源 | 苏宁零售技术研究院零售商需要不断通过创新服务来提高顾客的购物体验,而情感计算在该领域具有独特优势。它在零售行业的应用,主要集中在提升购物体验的服务中。在这个科技逐步改…

mysql基于replication实现最简单的M-S主从复制
2019独角兽企业重金招聘Python工程师标准>>> 什么是replication Replication可以实现数据从一台数据库服务器(master)复制到一到多台数据库服务器。 默认情况下,属于异步复制,因此无需维持长连接。 通过配置࿰…

Linux下高并发socket最大连接数所受的各种限制
修改最大打开文件数 # ulimit -n 修改最大进程数 # ulimit -u ------------------------------------------------------ Linux下高并发socket最大连接数所受的各种限制 转自:http://blog.csdn.net/guowake/article/details/6615728 1、修改用户进程可打开…

linux安全问答(1)
一、如何限制对系统资源的过度使用? (1)、编辑/etc/security/limits.conf文件,在其中加入或改变下面这些内容: * hard core 0 //禁止创建core文件 * hard rss 5000 //表示除root用户之外,其他用户都只能最多…
快速搭建对话机器人,就用这一招!
作者 | Milvus.io 责编 | 胡巍巍问答系统是自然语言处理领域一个很经典的问题,它用于回答人们以自然语言形式提出的问题,有着广泛的应用。其经典应用场景包括:智能语音交互、在线客服、知识获取、情感类聊天等。常见的分类有:生成…

目前流行的源程序版本管理软件和项目管理软件都有哪些?各有什么优缺点?...
目前流行的源程序版本管理软件和项目管理软件:Microsoft TFS,Github,SVN,Coding 各自的优缺点: Microsoft TFS:优点:任务版上能将需求、项目进度一览无余,对于小团队而言,…

孙鑫mfc学习笔记第十四课
第十四课网络的相关知识,网络程序的编写,Socket是连接应用程序与网络驱动程序的桥梁,Socket在应用程序中创建,通过bind与驱动程序建立关系。此后,应用程序送给Socket的数据,由Socket交给驱动程序向网络上发…
Linux环境编译安装Mysql以及补装innodb引擎方法
mysql安装 5.6以后可能会收费,所以选择5.1以下从台湾中山大学镜像下载 1.首先要安装C编译环境 # yum install gcc-c 2.下载解压 # wget http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-5.1/mysql-5.1.73.tar.gz# tar zxvf mysql-5.1.73.tar.gz# cd mysql-5…
Python 炫技操作:合并字典的七种方法
来源 | Python编程时光(ID: Cool-Python)Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手&am…

shell脚本编程基础(1)及RAID阵列
shell脚本:Linux从底层到上层的系统架构:硬件-->内核-->库(lib)-->shell-->用户。shell既是一种命令语言,也是程序设计语言(shell脚本),作为一种命令语言,它提供了用户与内核的交互…

freemarker基本语法及实例
EG.一个对象BOOK 1.输出 ${book.name} 空值判断:${book.name?if_exists }, ${book.name?default(‘xxx’)}//默认值xxx ${ book.name!"xxx"}//默认值xxx 日期格式:${book.date?string(yyyy-MM-dd)} 数字格式:${boo…
前百度主任架构师创业,两年融资千万美元,他说AI新药研发将迎来黄金十年...
「AI技术生态论」 人物访谈栏目是CSDN发起的百万人学AI倡议下的重要组成部分。通过对AI生态专家、创业者、行业KOL的访谈,反映其对于行业的思考、未来趋势的判断、技术的实践,以及成长的经历。2020年,CSDN将对1000人物进行访谈,形…
Linux环境安装卸载JDK以及安装Tomcat和发布Java的web程序
Linux环境:CentOS7.2 一.安装JDK 安装好的CentOS会自带OpenJdk,最好还是先卸载系统自带的JDK,然后自己重新去Oracle网站下载最新的JDK安装。 1.卸载系统自带的JDK 查看java信息 # java -version 查看JDK # rpm -qa | grep java 或者 还…
(转)详解css3弹性盒模型(Flexbox)
今天刚学了css3的弹性盒模型,这是一个可以让你告别浮动、完美实现垂直水平居中的新特性。 Flexbox是布局模块,而不是一个简单的属性,它包含父元素和子元素的属性。 Flexbox布局的主体思想是似的元素可以改变大小以适应可用空间,当…
Java开发环境的搭建以及使用eclipse创建项目
一、Java 开发环境的搭建 这里主要说windows环境下怎么配置Java环境。如果是Linux环境参考本博客另一篇文章即可: Linux环境安装卸载JDK 1.首先安装JDK java的SDK简称JDK。 去官网下载最新的JDK即可: http://www.oracle.com/technetwork/java/javase…
MMIT冠军方案 | 用于行为识别的时间交错网络,商汤公开视频理解代码库
作者 | 商汤出品 | AI科技大本营(ID:rgznai100)本文主要介绍三个部分:一个高效的SOTA视频特征提取网络TIN,发表于AAAI2020ICCV19 MMIT多标签视频理解竞赛冠军方案,基于TIN和SlowFast一个基于PyTorch,包含大…

MySQL的主从服务器配置
MySQL的主从服务器配置常见开源数据库有:MySQL,PostgreSQL,SQLite等,商业性质的:Oracle,Sql Server,DB2,Sybase,Infomix其中,Oracle的版本有Oracle 11g,Oracl…

Anaconda中安装Orange3脚本-完整版
2019独角兽企业重金招聘Python工程师标准>>> #Anaconda中安装Orange3脚本,完整版。包括插件的安装,在脚本中一次完成。 sudo apt-get update sudo apt-get -y install git python-pip python-virtualenv python-qt4-dev python3-pyqt4 libqt…
使用eclipse创建Struts2项目
eclipse版本: Kepler Service Release 1 http://www.eclipse.org/downloads/ struts版本:2.3.16 http://struts.apache.org/ 1.新建web项目 打开Eclipse,新建一个web项目"Struts2" 项目名字 勾选 web.xml选项 建好的…

8、进程通信-匿名管道
匿名管道 一个单向,未命名的管道,通常用来在一个父进程和一个子进程间传输数据。只能实现本地机器上两个进程间的通信,而不能实现跨网络的通信。 BOOL CreatePipe( PHANDLE hReadPipe, // read handle PHANDLE hWriteP…
Enhanced-RCNN: 一种高效的比较句子相似性的方法 |WWW 2020
作者 | 彭爽出品 | AI科技大本营(ID:rgznai100)国际顶级会议WWW2020将于4月20日至24日举行。始于1994年的WWW会议,主要讨论有关Web的发展,其相关技术的标准化以及这些技术对社会和文化的影响,每年有大批的学者、研究人…

直接可以拿去用的正则验证表达式
直接可以拿去用的正则验证表达式为了方便自己也方便初学的学弟们,自己总结了网上的众多正则验证式,现分享给大家,可以直接拿去用。一、校验数字的1 数字:^[0-9]*$2 n位的数字:^\d{n}$3 至少n位的数字:^\d{n…
家庭局域网开启AP隔离利用无线路由器互连
一开始可以上网,可以ping网关192.168.1.1,但是几台电脑之间就是不能互ping。 其实,真实的原因就是没有开启无线路由器的AP隔离。 在浏览器中输入192.168.1.1进入路由搜索一般用户名密码都是admin,具体请参见自己路由的说明书 操…
通过 Python 代码实现时间序列数据的统计学预测模型
来源 | DeepHub IMBA封图 | CSDN 付费下载于视觉中国 在本篇中,我们将展示使用 Python 统计学模型进行时间序列数据分析。 目标是:根据两年以上的每日广告支出历史数据,提前预测两个月的广告支出金额。原始数据:2017-01-01 到 201…

神色洋溢的 域名背后的故事
前短时间,我刚申请一个域名,好的顶级域名都被被人一拥而上的都强去了,我只好找那些申请好的用户买呀,这叫炒作,就是这样的抄起来的。你说平常一个也就100左右就搞定,可是现在要是到那票手里,那就…

Rust语言开发基础(六)基础语法
2019独角兽企业重金招聘Python工程师标准>>> 一、变量的定义和使用 其它常见的编程语言对变量的定义通常是通过声明类型和使用关键new来创建一个变量,但Rust不是,Rust使用关键字let。 1. 变量绑定通过let实现 fn main() { let x 5; } 2. 变量…