C语言在Visual Studio 2010环境下使用<regex.h>正则表达式函数库

在Visual Studio 2010环境下,如果C语言想要使用<regex.h>头文件进行正则表达式匹配,则需要pcre3.dll这个动态链接库,可以去网上下载。

下载的网址是:
Pcre for Windowspcre {whatisit}icon-default.png?t=N7T8https://gnuwin32.sourceforge.net/packages/pcre.htm下载的栏目是:Binaries和Developer files。

下载下来后是pcre-7.0-bin.zip和pcre-7.0-lib.zip两个压缩包。

第一节 建立Visual Studio 2010工程

建立一个VS2010控制台空白项目:

新建一个main.c源文件:

复制以下代码内容:

// 函数参考文档: https://www.man7.org/linux/man-pages/man3/regcomp.3.html
#include <regex.h>
#include <stdio.h>#pragma comment(lib, "pcre.lib")int main()
{int ret;regex_t reg;ret = regcomp(&reg, "\\w{8}", 0); // 设置正则表达式if (ret == 0){ret = regexec(&reg, "1234efGH", 0, NULL, 0); // 匹配第一个字符串if (ret == 0)printf("match\n");else if (ret == REG_NOMATCH)printf("mismatch\n");ret = regexec(&reg, "1234efG", 0, NULL, 0); // 匹配第二个字符串if (ret == 0)printf("match\n");else if (ret == REG_NOMATCH)printf("mismatch\n");regfree(&reg);}return 0;
}

在项目中建立pcre-7.0文件夹,并将下面几个h文件和lib文件解压到其中。
pcre-7.0-lib.zip\include\pcre.h
pcre-7.0-lib.zip\include\pcreposix.h
pcre-7.0-lib.zip\include\regex.h
pcre-7.0-lib.zip\lib\pcre.lib

在VS工程属性中附加pcre-7.0包含目录和库目录:

这时已经可以成功编译C程序,但是程序因缺少dll文件无法运行。

将pcre-7.0-bin.zip\bin\pcre3.dll解压到exe文件所在的Debug目录下(如C:\Documents and Settings\Octopus\桌面\pcre\regtest\Debug)就可以成功运行了。

第二节 判断正则表达式是否匹配某个字符串

C语言使用正则表达式函数前,需要先包含regex.h头文件。
#include <regex.h>

regex_t表示一个正则表达式对象,使用regcomp函数可根据正则表达式字符串创建正则表达式对象。
regcomp函数的原型如下:
int regcomp(regex_t *preg, const char *regex, int cflags);
preg参数用于保存待创建的正则表达式对象,是一个输出参数。
regex参数是正则表达式字符串,请注意C语言字符串中所有用于正则表达式的反斜杠都要双写
cflags参数可指定下列一个或多个选项(用|连接多个选项)。
REG_EXTENDED:使用扩展正则表达式语法(POSIX Extended Regular Expression syntax)。
REG_ICASE:匹配时忽略大小写。(比如"^\\w+gh$"可匹配"1234efGH")
REG_NOSUB:后面regexec函数只判断字符串是否匹配,不返回匹配了哪些部分,也就是pmatch和nmatch参数将被忽略。
REG_NEWLINE:允许^和$符号匹配\n换行符。(原本正则表达式"^\\w+gh$"不能匹配"aaa\nagh\nccc",加上REG_NEWLINE选项后,就可以成功匹配第二行的agh)
正则表达式对象创建成功时返回0,返回其他值表示创建失败。

正则表达式对象使用完毕后一定要用regfree函数释放掉。
void regfree(regex_t *preg);

使用regexec函数判断正则表达式是否匹配指定的字符串。
int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t *pmatch, int eflags);
preg参数是待使用的正则表达式。
string是要判断的字符串。
nmatch和pmatch参数下一节再讲,本节先分别设置成0和NULL。(在C语言中NULL特指空指针,不能用来给非指针变量赋值)
eflags参数是指定一些选项,这个本文也不讲,笔者认为没什么用,请自行参考官方文档。
字符串匹配成功时返回0,失败时返回REG_NOMATCH。

示例代码:

#include <regex.h>
#include <stdio.h>#pragma comment(lib, "pcre.lib")int main()
{int ret;regex_t reg;ret = regcomp(&reg, "^\\w+gh$", REG_ICASE | REG_NOSUB); // 设置正则表达式if (ret == 0){ret = regexec(&reg, "1234efGH", 0, NULL, 0); // 匹配第一个字符串if (ret == 0)printf("match1\n");else if (ret == REG_NOMATCH)printf("mismatch1\n");ret = regexec(&reg, "1234efgh", 0, NULL, 0); // 匹配第二个字符串if (ret == 0)printf("match2\n");else if (ret == REG_NOMATCH)printf("mismatch2\n");regfree(&reg);}ret = regcomp(&reg, "^\\w+gh$", REG_NEWLINE | REG_NOSUB);if (ret == 0){ret = regexec(&reg, "aaa\nagh\nccc", 0, NULL, 0);if (ret == 0)printf("match3\n");else if (ret == REG_NOMATCH)printf("mismatch3\n");regfree(&reg);}ret = regcomp(&reg, "^\\w+gh$", REG_NOSUB);if (ret == 0){ret = regexec(&reg, "aaa\nagh\nccc", 0, NULL, 0);if (ret == 0)printf("match4\n");else if (ret == REG_NOMATCH)printf("mismatch4\n");regfree(&reg);}return 0;
}

程序运行结果:

第三节 判断正则表达式匹配了某个字符串的哪些部分

在实际应用中,除了判断正则表达式是否匹配某个字符串外,有时我们还想知道到底匹配的是字符串中的哪部分,还有正则表达式里面的小括号都匹配了哪些部分。
这时,regexec函数的nmatch和pmatch参数就派上用场了。请注意要使用这两个参数的话,regcomp函数不能指定REG_NOSUB选项。

首先我们要声明一个regmatch_t数组,数组长度应该是正则表达式里面小括号的个数加上1。
比如"(\\w{2})(\\w+)"这个正则表达式有两个小括号,那么数组的长度就应该为2+1=3。
regmatch_t matches[3];
或者用malloc动态分配:
regmatch_t *matches = malloc(3 * sizeof(regmatch_t));
regcomp函数返回的regex_t结构体里面的re_nsub成员的值就是小括号的个数,所以上面这句话还可以写成:
regmatch_t *matches = malloc((reg.re_nsub + 1) * sizeof(regmatch_t));

然后,regexec函数的nmatch参数填数组长度(3),pmatch参数填数组名(matches)。
matches[0]用来存放整个正则表达式的匹配结果,matches[1~2]用来存放正则表达式里面的两个小括号的匹配结果。
matches[x].rm_so表示匹配的字符串的起始下标,matches[x].rm_eo表示匹配的字符串的结束下标。
假如要表示"abcdef"里面的"cde",那么rm_so=2,rm_eo=5,两者之差rm_eo-rm_so=5-2=3就是"cde"字符串的长度。

我们来看例程:

#include <regex.h>
#include <stdio.h>
#include <string.h>#pragma comment(lib, "pcre.lib")// 提取匹配的子字符串
const char *get_matched_str(const char *str, const regmatch_t *match)
{static char buffer[1024];int len;len = match->rm_eo - match->rm_so; // 子字符串的长度 = 结束下标 - 开始下标if (len > sizeof(buffer) - 1)len = sizeof(buffer) - 1; // 保证不超过buffer缓冲区的大小memcpy(buffer, str + match->rm_so, len); // 复制子字符串的内容到buffer缓冲区buffer[len] = '\0';return buffer;
}// 打印正则表达式本身匹配的子字符串, 和所有小括号匹配的部分
void print_matches(const regex_t *reg, const char *str, const regmatch_t *matches)
{int i;printf("正则表达式本身匹配的子字符串: %s\n", get_matched_str(str, &matches[0]));printf("小括号的个数: %d\n", reg->re_nsub);for (i = 1; i <= (int)reg->re_nsub; i++)printf("第%d个小括号匹配的子字符串: %s\n", i, get_matched_str(str, &matches[i]));
}// 单次匹配
int match_once(const regex_t *reg, const char *str)
{int found = 0;int ret;regmatch_t *matches;matches = malloc((reg->re_nsub + 1) * sizeof(regmatch_t));if (matches != NULL){ret = regexec(reg, str, reg->re_nsub + 1, matches, 0);if (ret == 0){printf("匹配成功。\n");found = 1;print_matches(reg, str, matches);}else if (ret == REG_NOMATCH)printf("匹配失败。\n");free(matches);}return found;
}int main()
{int ret;regex_t reg;ret = regcomp(&reg, "(\\w{2})(\\w+)", 0); // 设置正则表达式if (ret == 0){match_once(&reg, "https://zhidao.baidu.com/");regfree(&reg);}return 0;
}

程序运行结果为:

可以看到正则表达式匹配了"https://zhidao.baidu.com/"字符串里面的"https",两个小括号分别匹配了ht和tps。
实际上这个字符串还有其它匹配的部分,比如zhidao、baidu和com。
我们可以写一个多次匹配的函数,把一个字符串中所有匹配的地方都找出来。

// 多次匹配
int match_all(const regex_t *reg, const char *str)
{int count = 0;int offset = 0;regmatch_t *matches;matches = malloc((reg->re_nsub + 1) * sizeof(regmatch_t));if (matches != NULL){while (regexec(reg, str + offset, reg->re_nsub + 1, matches, 0) == 0){count++;printf("[第%d次匹配] %s\n", count, str + offset);print_matches(reg, str + offset, matches);offset += matches[0].rm_eo;}free(matches);}printf("总共匹配了%d次\n", count);return count;
}

 

第四节 将匹配部分替换成另外一个字符串

刚才的多次匹配的函数还可以改写成正则表达式字符串替换函数,把所有匹配的地方都替换成指定字符串。
当然,为了简单起见,没有做识别$1、$2这些符号的功能。

#include <regex.h>
#include <stdio.h>
#include <string.h>#pragma comment(lib, "pcre.lib")// 多次替换
char *replace_all(const regex_t *reg, const char *str, const char *replacement)
{char *new_str;int i, len = 0;int rlen;int offset;regmatch_t match;// 先计算替换后的字符串有多长rlen = strlen(replacement);offset = 0;while (regexec(reg, str + offset, 1, &match, 0) == 0){len += match.rm_so + rlen; // 没匹配上的长度 + 替换的子字符串的长度offset += match.rm_eo; // 继续下一次匹配}len += strlen(str + offset); // 剩余没匹配上的长度// 生成替换后的字符串new_str = malloc(len + 1);if (new_str == NULL)return NULL;i = 0;offset = 0;while (regexec(reg, str + offset, 1, &match, 0) == 0){if (match.rm_so > 0){memcpy(new_str + i, str + offset, match.rm_so);i += match.rm_so;}if (rlen > 0){memcpy(new_str + i, replacement, rlen);i += rlen;}offset += match.rm_eo;}if (i < len)memcpy(new_str + i, str + offset, len - i);new_str[len] = '\0';return new_str;
}int main()
{char *str;int ret;regex_t reg;ret = regcomp(&reg, "(\\w{2})(\\w+)", 0); // 设置正则表达式if (ret == 0){str = replace_all(&reg, "https://zhidao.baidu.com", "xy");if (str != NULL){printf("替换后的字符串: %s\n", str);free(str);}regfree(&reg);}return 0;
}

第五节 替换字符串时反向引用匹配的部分

在实际应用中,有时我们还想在替换字符串时反向引用正则表达式匹配的子字符串。
例如,用$0引用整个正则表达式匹配的内容,用$1引用第一个小括号匹配的内容,用$2引用第二个小括号匹配的内容,一直到$9。$$则表示$符号本身。
这个功能用C语言实现起来其实并不难,稍微改下上一节的程序就可以了。
请看代码:

#include <regex.h>
#include <stdio.h>
#include <string.h>#pragma comment(lib, "pcre.lib")// 计算某一次替换的长度
// 支持$0~$9引用
// $$替换为$
int get_replacement_length(const char *replacement, const regmatch_t *matches, int nsub)
{int i, len = 0;while (*replacement != '\0'){if (*replacement == '$' && *(replacement + 1) != '\0'){if (*(replacement + 1) == '$'){len++;replacement += 2;continue;}i = *(replacement + 1) - '0';if (i >= 0 && i <= 9 && i <= nsub){len += matches[i].rm_eo - matches[i].rm_so;replacement += 2;continue;}}len++;replacement++;}return len;
}// 生成某次替换后的字符串
char *get_replacement(const char *replacement, const char *src, const regmatch_t *matches, int nsub, int *rlen_out)
{char *s;int i, len = 0;int rlen;rlen = get_replacement_length(replacement, matches, nsub);if (rlen_out != NULL)*rlen_out = rlen;s = malloc(rlen + 1);if (s == NULL)return NULL;while (*replacement != '\0'){if (*replacement == '$' && *(replacement + 1) != '\0'){if (*(replacement + 1) == '$'){s[len] = '$';len++;replacement += 2;continue;}i = *(replacement + 1) - '0';if (i >= 0 && i <= 9 && i <= nsub){memcpy(s + len, src + matches[i].rm_so, matches[i].rm_eo - matches[i].rm_so);len += matches[i].rm_eo - matches[i].rm_so;replacement += 2;continue;}}s[len] = *replacement;len++;replacement++;}s[len] = '\0';return s;
}// 多次替换
char *replace_all(const regex_t *reg, const char *str, const char *replacement)
{char *new_str;char *new_replacement;int i, len = 0;int rlen;int offset;regmatch_t matches[10];// 先计算替换后的字符串有多长offset = 0;while (regexec(reg, str + offset, 10, matches, 0) == 0){rlen = get_replacement_length(replacement, matches, reg->re_nsub);len += matches[0].rm_so + rlen; // 没匹配上的长度 + 替换的子字符串的长度offset += matches[0].rm_eo; // 继续下一次匹配}len += strlen(str + offset); // 剩余没匹配上的长度// 生成替换后的字符串new_str = malloc(len + 1);if (new_str == NULL)return NULL;i = 0;offset = 0;while (regexec(reg, str + offset, 10, matches, 0) == 0){if (matches[0].rm_so > 0){memcpy(new_str + i, str + offset, matches[0].rm_so);i += matches[0].rm_so;}new_replacement = get_replacement(replacement, str + offset, matches, reg->re_nsub, &rlen);if (new_replacement != NULL){if (rlen > 0){memcpy(new_str + i, new_replacement, rlen);i += rlen;}free(new_replacement);}offset += matches[0].rm_eo;}if (i < len)memcpy(new_str + i, str + offset, len - i);new_str[len] = '\0';return new_str;
}int main()
{char *str;int ret;regex_t reg;ret = regcomp(&reg, "(\\w{2})(\\w+)", 0); // 设置正则表达式if (ret == 0){str = replace_all(&reg, "https://zhidao.baidu.com", "($0,$1,$2,$3,$$)");if (str != NULL){printf("替换后的字符串: %s\n", str);free(str);}regfree(&reg);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/665734.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Python] scikit-learn中数据集模块介绍和使用案例

sklearn.datasets模块介绍 在scikit-learn中&#xff0c;可以使用sklearn.datasets模块中的函数来构建数据集。这个模块提供了用于加载和生成数据集的函数。 API Reference — scikit-learn 1.4.0 documentation 以下是一些常用的sklearn.datasets模块中的函数 load_iris() …

回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小二乘支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小二乘支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小二乘支持向量机的数据多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于OOA-LSSVM鱼鹰算法…

Python||五城P.M.2.5数据分析与可视化_使用华夫图分析各个城市的情况(下)

目录 沈阳市的空气质量 华夫图 柱状图 总结 五城P.M.2.5数据分析与可视化——北京市、上海市、广州市、沈阳市、成都市&#xff0c;使用华夫图和柱状图分析各个城市的情况 沈阳市的空气质量 华夫图 import numpy as np import pandas as pd import matplotlib.pyplot as plt …

TorchVision的使用方法、更改默认路径

TorchVision的使用 1. 转换和增强图像 torchvision.transforms.v2 参数作用Resize将输入调整为给定大小RandomShortestSize随机调整输入的大小RandomResize随机调整输入的大小RandomCrop在随机位置裁剪输入RandomResizedCrop裁剪输入的随机部分并将其调整为给定大小RandomIoU…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-11-playwright操作iframe-上篇

1.简介 原估计宏哥这里就不对iframe这个知识点做介绍和讲解了&#xff0c;因为前边的窗口切换就为这种网页处理提供了思路&#xff0c;另一个原因就是虽然iframe很强大&#xff0c;但是现在很少有网站用它了。但是还是有小伙伴或者童鞋们私下问这个问题&#xff0c;那么宏哥就…

【后端开发】正向代理与反向代理

正向代理 正向代理&#xff08;forward proxy&#xff09;&#xff1a;是一个位于客户端和目标服务器之间的服务器(代理服务器)&#xff0c;为了从目标服务器取得内容&#xff0c;客户端向代理服务器发送一个请求并指定目标&#xff0c;然后代理服务器向目标服务器转交请求并将…

BUUCTF-Real-[ThinkPHP]2-Rce1

任意代码执行漏洞 ThinkPHP 2.x版本中&#xff0c;使用preg_replace的/e模式匹配路由&#xff1a; $res preg_replace((\w).$depr.([^.$depr.\/])e, $var[\\\1\]"\\2";, implode($depr,$paths)); 导致用户的输入参数被插入双引号中执行&#xff0c;造成任意代码执行…

Open CASCADE学习|拓扑变换

目录 平移变换 旋转变换 组合变换 通用变换 平移变换 TopoDS_Shape out;gp_Trsf theTransformation;gp_Vec theVectorOfTranslation(0., 0.125 / 2, 0.);theTransformation.SetTranslation(theVectorOfTranslation);BRepBuilderAPI_Transform myBRepTransformation(out, th…

Leetcode刷题笔记题解(C++):36. 有效的数独

思路一&#xff1a;暴力破解&#xff0c;两个二维数组记录行、列对应的数字出现的次数&#xff0c;比如rows[i][index]表示的数字index在i行出现的次数&#xff0c;三维数组记录每个块中对应数字出现的次数&#xff0c;比如boxes[i/3][j/3][index]表示的数字index在[i/3][j/3]个…

校园墙表白墙系统uniapp微信小程序

配置文件 (自动编号、配置参数名称、配置参数值)&#xff1b; 前端开发:vue 语言&#xff1a;javapythonnodejsphp均支持 运行软件:idea/eclipse/vscode/pycharm/wamp均支持 框架支持:Ssm/django/flask/thinkphp/springboot/springcloud均支持 数据库 mysql 数据库工具&#x…

定时器 Timer(超详细模拟实现)

目录 一、定时器 1.定时器概述 2.Java标准库提供的定时器类 3.定时器代码样例 二、实现 1.实现思路 2.代码实现 2.1纯享版 2.2注释版 3.代码解析(超详细) 3.1描述类MyTimerTask ①构造&#xff1a;MyTimerTask&#xff08;Runnable runnable, long delay&#xff…

如何使用本地私有NuGet服务器

写在前面 上一篇介绍了如何在本地搭建一个NuGet服务器&#xff0c; 本文将介绍如何使用本地私有NuGet服务器。 操作步骤 1.新建一个.Net类库项目 2.打包类库 操作后会生成一个.nupkg文件&#xff0c;当然也可以用dotnet pack命令来执行打包。 3.推送至本地NuGet服务器 打开命…

《计算机网络简易速速上手小册》第6章:网络性能优化(2024 最新版)

文章目录 6.1 带宽管理与 QoS - 让你的网络不再拥堵6.1.1 基础知识6.1.2 重点案例&#xff1a;提高远程办公的视频会议质量实现步骤环境准备Python 脚本示例注意事项 6.1.3 拓展案例1&#xff1a;智能家居系统的网络优化实现思路Python 脚本示例 6.1.4 拓展案例2&#xff1a;提…

计算机网络_1.5 计算机网络的性能指标

1.5 计算机网络的性能指标 一、总览二、常用的八个计算机网络性能指标1、速率&#xff08;1&#xff09;数据量&#xff08;2&#xff09;速率&#xff08;3&#xff09;数据量与速率中K、M、G、T的数值辨析&#xff08;4&#xff09;【练习1】计算发送数据块的所需时间 2、带宽…

Unity引擎学习笔记之【角色动画器操作】

角色动画Character Animation 一、使用方法 1. 添加一个静态的角色模型&#xff0c;并确保这个角色模型绑定了Avatar 2. 创建一个动画器控制器 Animator Controller 3. 将动画器控制器拖动添加到Player的Animator控制器上 4. 双击即可打开控制器 5. 将带有动画的预设&#xf…

docker下nacos(1.2.0)的持久化

一、创建数据库 运行以下代码自动创建数据库和表 CREATE DATABASE IF NOT EXISTS nacos_config /*!40100 DEFAULT CHARACTER SET utf8 */; USE nacos_config;SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for config_…

[Python] 如何在控制台进行输入输出

可莉今天带你来学习Python基础中在控制台的输入输出~ 接下来跟着可莉来学习吧~ 可莉将这篇博客收录在了&#xff1a;《Python》 可莉推荐的博主主页&#xff1a;Keven ’ s blog 目录 通过控制台输出 格式化输出 通过控制台输入 总结 通过控制台输出 我们通过print函数可以…

[VulnHub靶机渗透] WestWild 1.1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

leetcode正则表达式匹配问题(困难)

1.题目描述 2.解题思路&#xff0c;这道题自己没做出来&#xff0c;看了官方的题解&#xff0c;感觉对自己来说确实是比较难想的。使用了动态规划的解决方案&#xff0c;这种方案看题解都不一定能看明白&#xff0c;不过有个评论画图讲解的非常明白。其实仔细看题解的话&#…

从一个小故事讲解观察者模式~

定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 什么是观察者模式&#xff1f; 观察者模式在我们的日常生活中极其常见。 先来看看观察者模式的定义&#xff1a; 观察者模式定义了对象之间…