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.

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