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

news2024/9/21 8:02:01

在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.coloradmin.cn/o/1437395.html

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

相关文章

12-树-有序链表转换二叉搜索树

这是树的第12篇算法&#xff0c;力扣链接。 给定一个单链表的头节点 head &#xff0c;其中的元素 按升序排序 &#xff0c;将其转换为高度平衡的二叉搜索树。 本题中&#xff0c;一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。 示例 1: 输入: h…

解析与模拟常用字符串函数strcpy,strcat,strcmp,strstr(一)

今天也是去学习了一波字符串函数&#xff0c;想着也为了加深记忆&#xff0c;所以写一下这篇博客。既帮助了我也帮助了想学习字符串函数的各位。下面就开始今天的字符串函数的学习吧。 目录 strcpy与strncpy strcat与strncat strcmpy strstr strcpy与strncpy 在 C 语言中&…

宏景eHR fieldsettree SQL注入漏洞复现

0x01 产品简介 宏景eHR人力资源管理软件是一款人力资源管理与数字化应用相融合,满足动态化、协同化、流程化、战略化需求的软件。 0x02 漏洞概述 宏景eHR fieldsettree 接口处存在SQL注入漏洞,未经过身份认证的远程攻击者可利用此漏洞执行任意SQL指令,从而窃取数据库敏感…

ChatGPT学习第一周

&#x1f4d6; 学习目标 掌握ChatGPT基础知识 理解ChatGPT的基本功能和工作原理。认识到ChatGPT在日常生活和业务中的潜在应用。 了解AI和机器学习的基本概念 获取人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;的初步了解。理解这些技术是如何支撑…

电动汽车雷达技术概述 —— FMCW干扰问题

一、电动汽车上有多少种传感器&#xff1f; 智能电动汽车&#xff08;包括自动驾驶汽车&#xff09;集成了大量的传感器来实现高级驾驶辅助系统&#xff08;ADAS&#xff09;、自动驾驶功能以及车辆状态监测等功能。以下是一份相对全面的智能电动汽车中可能使用到的传感器列表…

虚拟飞控计算机:飞行控制系统验证与优化的利器

01.背景介绍 随着航空技术的飞速发展&#xff0c;飞行控制系统作为飞机的心脏&#xff0c;全面负责监测、调整和维持飞行器的姿态、航向、高度等参数&#xff0c;用以确保飞行的安全和稳定。为了满足这些要求&#xff0c;现代飞控系统通常采用先进的处理器和外设来确保其高效、…

CSS的动画

CSS的动画 在本节&#xff0c;我们将学习keyframes动画。 1. 动画的基本使用 1. 定义动画 定义动画有两种写法&#xff1a; 简单定义方式 keyframes 动画名 {/* from代表初始状态 */from {/*property1:value1*/transform: translate(0%);}/* to代表结束状态 */to {transfor…

vue3 之 商城项目—项目搭建起步

1.创建项目 1️⃣ npm init vuelatest2️⃣ npm install3️⃣ npm run dev4️⃣目录调整 2.git管理项目 基于creact-vue创建出来的项目默认没有初始化git仓库&#xff0c;需要我们手动初始化 执行命令 git init git add. git commit -m init3.项目起步—配置别名路径联…

Xampp中Xdebug的安装使用

工欲善其事&#xff0c;必先利其器 XDebug简介 XDebug 是一个用于 PHP 的调试和性能分析工具。它提供了一系列功能&#xff0c;帮助开发者在开发和调试 PHP 应用程序时更加高效。 以下是 XDebug 的一些主要特性和功能&#xff1a; 调试功能&#xff1a; 断点调试&#xff1a;…

幻兽帕鲁怎么样?好玩? Mac版的玩《幻兽帕鲁》也很简单,只需三个步骤

幻兽帕鲁怎么样 幻兽帕鲁是一款集合了多种游戏元素的游戏&#xff0c;它巧妙地融合了《方舟:生存进化》的野外生存挑战、《荒野之息》的开放世界探索、《魔兽世界》的多元角色互动以及宝可梦的精灵捕捉与培养等经典游戏元素。游戏的核心系统是「帕鲁」捕获&#xff0c;你可以让…

电力负荷预测 | 基于LSTM、TCN的电力负荷预测(Python)

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 电力负荷预测 | 基于LSTM、TCN的电力负荷预测(Python) 源码设计 #------------------

Java HashSet 重写 equals() 和 hashCode() 对象去重

Ailt Insert 选择 equals() 和 hashCode() package com.zhong.collection.set;import java.util.HashSet; import java.util.Objects;public class HashSetDeduplication {public static void main(String[] args) {// HashSet 对象去重HashSet<Student> students new …

momentJs推导日历组件

实现效果: 代码&#xff1a; 引入momentjs然后封装两个函数构建出基本数据结构 import moment from moment;// 某月有多少天 export const getEndDay (m) > m.daysInMonth();/*** description 获取本月空值数据* param { Date } year { } 年度* param { Number } month …

springboot Feign方式注入注解详解

一、FeignClient注解详解 FeignClient是Spring Cloud中用于声明Feign客户端的注解&#xff0c;它使得编写HTTP客户端变得更简单。通过Feign的自动化配置机制&#xff0c;可以很容易地编写HTTP API客户端。以下是FeignClient的详解&#xff1a; 作用&#xff1a;FeignClient注解…

C++,stl,vector容器详解

目录 1.vector基本概念 2.vector的创建 3.vector赋值操作 4.vector容量和大小的操作 5.vector容器的插入和删除 6.vector容器的数据存取 7.vector互换容器 8.vector容器预留空间 1.vector基本概念 2.vector的创建 #include<bits/stdc.h> using namespace std;int m…

CSS综合案例4

CSS综合案例4 1. 综合案例 我们来做一个静态的轮播图。 2. 分析思路 首先需要加载一张背景图进去需要4个小圆点&#xff0c;设置样式&#xff0c;并用定位和平移调整位置添加两个箭头&#xff0c;也是需要用定位和位移进行调整位置 3. 代码演示 html文件 <!DOCTYPE htm…

一个Vivado仿真问题的debug

我最近在看Synopsys的MPHY仿真代码&#xff0c;想以此为参考写个能实现PWM-G1功能的MPHY&#xff0c;并应用于ProFPGA原型验证平台。我从中抽取了一部分代码&#xff0c;用Vivado自带的仿真器进行仿真&#xff0c;然后就遇到了一个莫名其妙的问题&#xff0c;谨以此文作为debug…

vue3项目中使用mapv

vue3项目中使用mapv mapv是百度地图官方提供的地图数据可视化开源项目&#xff0c;提供了很多效果酷炫的绘图api mapv地址在这里&#xff0c;示例图在这里 先解释为什么要用mapv echarts画的地图&#xff0c;都是行政区划&#xff0c;就算是geo地图&#xff0c;也只能在行政…

基于YOLOv8的暗光低光环境下(ExDark数据集)检测,加入多种优化方式---DCNv4结合SPPF ,助力自动驾驶(一)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文主要内容:详细介绍了暗光低光数据集检测整个过程&#xff0c;从数据集到训练模型到结果可视化分析&#xff0c;以及如何优化提升检测性能。 &#x1f4a1;&#x1f4a1;&#x1f4a1;加入 DCNv4结合SPPF mAP0.5由原始的0.682提升至…

跳过mysql密码并重置密码 shell脚本

脚本 目前只是验证了5.7 版本是可以的&#xff0c;8.多的还需要验证 以下是一个简单的Shell脚本&#xff0c;用于跳过MySQL密码设置并重置密码&#xff1a; #!/bin/bash yum install psmisc -y# 停止MySQL服务 sudo service mysqld stop# 跳过密码验证 sudo mysqld --skip-g…