深入理解 fnmatch 函数的实现

ops/2025/2/21 7:57:27/

0、背景

fnmatch 函数是 C 标准库和 POSIX 中用于匹配文件路径的工具,它使得我们能够根据模式字符串对文件名进行模式匹配。常见的用途包括在文件系统中查找符合某种模式(如通配符)的文件。例如,fnmatch(“.txt", “file1.txt”) 应该返回 true,而 fnmatch(".txt”, “file1.pdf”) 应该返回 false。该函数中模式和规则和shell中模式的规则类似,该函数可以用做简单的字符串匹配,如果需要复杂的字符串匹配,需要使用正则表达式进行匹配。

1、fnmatch函数概述

fnmatch 函数的原型通常如下:

#include <fnmatch.h>int fnmatch(const char *pattern, const char *string, int flags);

参数说明:

  • pattern:这是包含模式匹配的通配符的字符串(例如 *.txt),它指示了要匹配的文件名的模式。
  • string:要进行匹配的文件名或字符串。
  • flags:指定匹配的选项(如是否区分大小写,是否支持正则表达式等)。常见的 flags 包括:
    FNM_CASEFOLD:忽略大小写。
    FNM_PATHNAME:仅匹配路径分隔符(通常是 /)的部分。
    FNM_PERIOD:.只能以明文进行匹配,不可以再使用?或*进行匹配。
    FNM_NOESCAPE:不允许使用转义字符
    返回值:
  • 如果字符串 string 与 pattern 匹配,则返回 0。
  • 如果不匹配,则返回非零值(通常为 1)。
  • 如果出现错误,则返回 -1。
    常见用途:
  • 在 Unix/Linux 系统中,它通常用于文件名通配符匹配。
  • 在编写 shell 程序或命令行工具时,经常用来处理文件查找和匹配操作。

2、fnmatch的匹配规则

在深入了解 fnmatch 的实现之前,我们需要理解它如何进行模式匹配。fnmatch 使用的匹配规则类似于传统的 Unix shell 通配符规则。

  • *:匹配零个或多个字符。
  • ?:匹配一个字符。
  • []:匹配指定范围内的一个字符(例如 [a-z] 匹配所有小写字母)。
  • \:转义字符,允许匹配实际的 *、? 或其他特殊字符。

3、fnmatch 函数的实现原理

该函数的主要工作流程如下:
1、遍历模式字符:逐个检查模式中的字符,根据规则判断是通配符(*、? 等)还是普通字符。
2、处理通配符:

  • *:可以匹配零个或多个字符,因此需要递归地尝试不同的匹配方式。
  • ?:匹配一个字符,可以直接比较当前字符是否匹配。
  • []:检查字符是否在给定范围内。
  • \:跳过转义字符,匹配下一个字符。
    3、字符匹配:当遇到普通字符时,直接比较当前字符和模式字符。
    4、返回结果:当所有字符都成功匹配时返回 0;否则返回 1。
    下面代码实现了一个简化版的fnmatch函数。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <iostream>// 匹配函数:简化版 fnmatch 实现
int fnmatch(const char *pattern, const char *string, int flags = 0) {while (*pattern) {if (*pattern == '*') {// '*' 匹配零个或多个字符if (*(pattern + 1) == '\0') {return 0;  // 如果 '*' 后面没有字符,说明匹配成功}// 尝试匹配零个或多个字符while (*string) {if (fnmatch(pattern + 1, string, flags) == 0) {return 0;}string++;}return 1;  // 如果没有匹配到,返回 1} else if (*pattern == '?') {// '?' 匹配一个字符if (*string) {pattern++;string++;} else {return 1;  // 如果 string 已结束,但模式还没有结束,返回不匹配}} else if (*pattern == '[') {// 字符集匹配,如 [a-z] 或 [0-9]const char *closing_bracket = strchr(pattern, ']');if (!closing_bracket) {return 1;  // 如果没有找到匹配的 ']'}// 检查字符是否在字符集范围内int matched = 0;pattern++;  // 跳过 '['while (pattern < closing_bracket) {if (*pattern == '-') {if(*(pattern-1) != '[' && *(pattern + 1) != ']' && *(pattern - 1) < *string && *(pattern + 1) > *string) {matched = 1;break;}} else if (*pattern == *string) {matched = 1;break;}pattern++;}if (!matched) {return 1;  // 如果字符不在字符集内,返回不匹配}pattern = closing_bracket + 1;string++;} else {// 普通字符匹配if (*pattern == *string) {pattern++;string++;} else {return 1;  // 如果普通字符不匹配,返回 1}}}// 如果模式和字符串都遍历完,表示完全匹配return (*pattern == '\0' && *string == '\0') ? 0 : 1;
}int main() {// 测试用例printf("Test 1: %d\n", fnmatch("*.txt", "file.txt"));  // Expected output: 0 (match)printf("Test 2: %d\n", fnmatch("file?.txt", "file1.txt"));  // Expected output: 0 (match)printf("Test 3: %d\n", fnmatch("file[0-9].txt", "file5.txt"));  // Expected output: 0 (match)printf("Test 4: %d\n", fnmatch("file[0-9].txt", "fileA.txt"));  // Expected output: 1 (no match)printf("Test 5: %d\n", fnmatch("file?.txt", "file12.txt"));  // Expected output: 1 (no match)return 0;
}

4、结论

fnmatch 是一个功能强大的文件名匹配函数,它可以处理常见的通配符模式,并且在许多操作系统中得到了广泛应用。在实现时,它使用递归回溯的方式处理通配符(如 * 和 ?),并且支持字符集范围匹配([])。如果是简单的字符串匹配,可以使用这个函数,如果是复杂的字符串匹配,建议使用前面介绍的正则表达式库。因为fastdds源码中用到了这个函数实现字符串匹配,学习总结一下,接下来就要啃fastdds中的核心rtps了,加油,小安。


http://www.ppmy.cn/ops/159717.html

相关文章

DeepSeek VS OpenAI:AI巨头应用对比

DeepSeek 和 OpenAI 都是领先的 AI 公司&#xff0c;具备各自的优势。这两天我读了一篇很棒的文章&#xff0c;作者Da-vinci对这两家AI巨头做了很直观的介绍比较。以下是来自原创的部分内容&#xff1a; DeepSeek、ChatGPT 比较表 DeepSeek、ChatGPT 比较表 | 来源于Da-vinci …

高速硬件电路设计

高速PCB 设计三大原则 3W原则 **1.定义&#xff1a;**线和线之间的距离保持3倍线宽。 2.作用&#xff1a;减少线间的串扰&#xff0c;可以保证70%的线间电场不互相干扰 3.总结&#xff1a;高速信号3W规则走&#xff0c;低速信号最低2W 20H 原则 1.图1&#xff0c;电源层和地…

Unity 打开摄像头 并显示在UI

需求: 打开相机并显示在UI上 效果: 注意&#xff1a; 电脑可能有多个摄像头&#xff0c;注意名称 代码: using System; using System.Linq; using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; #if UNITY_EDITOR using UnityEditor; #endifname…

【Scrapy】Scrapy教程5——第一个Scrapy项目

文章目录 Scrapy目录结构第一个爬虫运行爬虫必要说明start_requests()和start_urls如何关闭allowed_domains的限制通过前几节的学习,我们已经了解了Scrapy的基本操作,下面我们开始第一个项目,我以本人的 网址为例进行爬虫讲解,之所以用我自己的网站,是因为我这个网站本来…

idea日常报错之UTF-8不可映射的字符

目录 一、UTF-8不可映射的字符的解决 1、出现这种报错的情形 2、具体解决办法 前言&#xff1a; 在我们日常代码编写的时候可能会遇到各式各样的错误&#xff0c;有时候并不是你改动了代码&#xff0c;而是莫名其妙就出现的报错&#xff0c;今天我就遇到一个在maven编译的时候…

HarmonyOS的核心特性:分布式技术引领创新

在数字化浪潮汹涌的今天&#xff0c;物联网&#xff08;IoT&#xff09;技术的飞速发展正逐步打破设备间的界限&#xff0c;使万物互联成为可能。HarmonyOS&#xff0c;作为华为自主研发的分布式全场景操作系统&#xff0c;凭借其核心的分布式技术&#xff0c;不仅引领了操作系…

平面与平面相交算法杂谈

1.前言 空间平面方程&#xff1a; 空间两平面如果不平行&#xff0c;那么一定相交于一条空间直线&#xff0c; 空间平面求交有多种方法&#xff0c;本文进行相关讨论。 2.讨论 可以联立方程组求解&#xff0c;共有3个变量&#xff0c;2个方程&#xff0c;而所求直线有1个变量…

STM32 外部中断和NVIC嵌套中断向量控制器

目录 背景 外部中断/事件控制器(EXTI) 主要特性 功能说明 外部中断线 嵌套向量中断控制器 特性 ‌中断线&#xff08;Interrupt Line&#xff09; 中断线的定义和作用 STM32中断线的分类和数量 优先级分组 抢占优先级&#xff08;Preemption Priority&#xff09; …