C++正则表达式

开头

这两天在写引擎二周目,复健C++,遇到一些问题记录一下

需求

写一个shader系统,需要自动分析出定义变量的名称

例如

1
2
3
.....
uniform sampler2D TINY_TexColor;
.....

我需要知道这个uniform变量的名称和类型
可以使用如下正则表达式

1
(?<=uniform\s+sampler2D\s+)(\S+)(?=;)

?<=表示根据后面的字符串(“uniform sampler2D “),后向匹配后方字符串,但是最后匹配结果中不保存”uniform sampler2D “

+表示前一个字符或者子串,匹配1-无限次.

*表示前一个字符或者子串,匹配0-无限次.

?=表示根据后面的字符串(“;”),前向匹配字符串,但是最后结果中不保存”;”

\S(大写S)表示匹配一个非空白字符,只匹配一个.所以要加上+,表示前一个字符需要匹配至少一次.

\s(小写s)表示匹配一个空白字符,只匹配一个.上+号的目的是为了防止某些小可爱在中间乱打空格.

\w(小写)表示匹配一个无特殊符号的”字”字符,也就是[0-9a-zA-Z_].注意最后是下划线.

()表示其中的字符串形成一组子串并且保存到最终结果中.所以()?的作用表示这个子串不保存.

这样就能得到最终字符串TINY_TexColor

问题

问题是C++的std::regex他喵的不支持后向查看.所以需要换一种方式来写.

1
R"(uniform\s+sampler2D\s+(\w+)(?=;))"

R”()”表示其中字符串的转义字符不需要转义.

于是使用如下C++代码就能得到结果TINY_TexColor.

1
2
3
4
5
6
7
std::string test = "uniform sampler2D TINY_TexColor;";
std::regex pattern(R"(uniform\s+sampler2D\s+(\w+)(?=;))");
std::smatch result;
if (std::regex_search(new_content, result, pattern))
{
std::cout << result[1] << std::endl;
}

由于C++自己的特性,std::smath在使用std::regex_search之后,0号位置一定会保存整个被匹配到的字符串,也就是”uniform sampler2D TINY_TexColor”.注意不包含’;’因为前向查看给否决了.

所以1号位置才是我们想要的结果.

为什么会有这样的结果?

因为()表达式表示保存这个匹配到的字符串,所以()中的内容就会被保存到结果中,并且因为这是第一个被保存的子串,所以它的index为1.

更进一步

根据以上规则,我们还可以进一步,继续提取这个uniform的类型信息.

1
2
3
4
5
6
7
8
std::string test = "uniform sampler2D TINY_TexColor;";
std::regex pattern(R"(uniform\s+(\w+)\s+(\w+)(?=;))");
std::smatch result;
if (std::regex_search(new_content, result, pattern))
{
std::cout << result[1] << std::endl; //type
std::cout << result[2] << std::endl; //name
}

(\w+)替换掉sampler2D所在的位置,注意这样写以后sampler2D的位置就成了第一个()保存的子串所在的位置了,变量名称就顺移到了下一位,即2的位置.

完成

所以使用如下代码,就能匹配出shader文件中所有的uniform变量类型和名称

1
2
3
4
5
6
7
8
std::string new_content = "all shader text";
std::regex pattern(R"(uniform\s+(\w+)\s+(\w+)(?=;))");
std::sregex_iterator end;
for (auto i = std::sregex_iterator(new_content.begin(), new_content.end(), pattern); i != end; i++)
{
std::cout << (*i)[1] << std::endl; //type
std::cout << (*i)[2] << std::endl; //name
}

std::sregex_iterator会在每一个迭代周期进行进行一次std::regex_search

对迭代器解引用就可以获得内部存在的match_results.

最后根据规则直接取变量就行了.

伪随机算法系列-05 Noise Variants(Fractals and Tiling)

翻译—-Pseudorandom Noise-Noise Variants

如果你觉得这篇教程不错,请去支持原作者

此教程使用的Unity版本为2020.3.12f1

  • 组合多个音阶(Octaves)来创建一个分析噪声
  • 引入扰动型(turbulence)柏林和值噪声
  • 增加一个选项来创建瓦片效果

这是伪随机噪声系列教程的第五篇.增加分形噪声,扰动型噪声和瓦片型布局.

A torus showing six octaves of lacunarity 3 fractal 3D Perlin noise.

阅读更多

伪随机算法系列-04 Perlin Noise(Gradient Noise)

翻译—-Pseudorandom Noise-Perlin Noise
如果你觉得这篇教程不错,请去支持原作者

此教程使用的Unity版本为2020.3.6f1

  • 制作一个泛型化的晶格(lattice)噪声
  • 添加对梯度噪声的支持
  • 生成1D,2D,3D的柏林噪音

这是伪随机噪音系列教程的第四篇.通过增强值噪音的功能来支持柏林噪声.

3D柏林噪音球体

阅读更多

伪随机算法系列-03 Value Noise(Lattice Noise)

翻译—-Pseudorandom Noise-Value Noise
如果你觉得这篇教程不错,请去支持原作者

此教程使用的Unity版本为2020.3.6f1

  • 建立一个可视化抽象类
  • 引入一个通用的噪音生成器
  • 生成1D,2D,3D的值噪音

这是伪随机噪声系列教程的第三篇.教大家如何把可视化效果从纯hash生成的方式变成晶格(Lattice)生成的方式.

值噪音球体

阅读更多

伪随机算法系列-02 Hashing Space(Hashing in an Arbitrary Grid)

翻译—-Pseudorandom Noise-Hashing Space
如果你觉得这篇教程不错,请去支持原作者

此教程使用的Unity版本为2020.3.6f1

  • 将Hash效果转换到3D空间中
  • 创建各种形状的立体模型
  • 手动制作向量化的Jobs
  • 创建一个形状生成器job模板类

这是伪随机噪音系列教程的第二篇.这次我们会修改hash算法让他能在空间中创建任意的形状.

效果展示

阅读更多

伪随机算法系列-01 Hashing(Small xxHash)

翻译—-Pseudorandom Noise-Hashing
如果你觉得这篇教程不错,请去支持原作者

此教程使用的Unity版本为2020.3.6f1

  • 创建一个可视化Hash网格
  • 把2D坐标转换成伪随机值
  • 实现一个小型的xxHash算法
  • 使用该Hash算法计算方块位置和颜色

这是Basics系列之后的第一个伪随机噪声的系列教程.教大家如何使用一种小型特化版的xxHash函数来生成伪随机数.

示例图片

阅读更多

Unity 坐标系与缩放

翻译 – Unity coordinates and scales
如果你觉得这篇教程不错,请去支持原作者

概述

与UDK不同,Unity不会主动限制游戏世界的大小,你可以随便增加游戏世界的大小使你的玩家迷失在其中,直到引擎坐标系统位数溢出并导致数学上的崩溃.

从全尺寸的行星到苹果里的虫子,Unity让你在任何你想要的尺度上工作——然而如果你试图同时在超小和超大的尺度上进行操作,你会发现你可能遇到了一些严重的问题.

这篇文章讲述了Unity的坐标系统的工作原理,以及它对游戏世界区域的影响,渲染时的问题,还有如何创建超大型的世界.

阅读更多