Dart笔记:glob 文件系统遍历

Dart笔记
文件系统遍历工具:glob 模块

作者李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/134423951


很久前介绍过一个 NodeJS 中的类似工具,叫做 fast-glob,可参见《NodeJS文件系统遍历工具:fast-glob》一文(地址:https://jclee95.blog.csdn.net/article/details/129892856)。这类库对于自己开发底层工具来说是比较常用的。比如在前端中自己去做一些脚手架。本文介绍的 glob库就是 Dart 语言中这样一个类似的工具,在很多常用的命令行工具中,都有它的使用,比如 build_runner 库(一个用于 Dart 代码生成和模块化编译的构建系统),再比如 very_good_cli 等等。当需要自己做类似的 Flutter/Dart 项目的工程化工具时,可以回过头来参考本文中介绍的相关知识。


1. 概述

glob 库是一个强大的文件系统遍历工具,它提供了一种简洁的方式来描述和匹配文件路径模式。这种模式被称为 glob 模式,它可以包含各种通配符,使得我们可以轻松地匹配多个文件或目录。

glob 库的主要功能是根据给定的 glob 模式来查找和匹配文件系统中的文件和目录。它支持各种通配符,包括 *(匹配任意数量的字符)、?(匹配任意一个字符)、[abc](匹配任意一个列出的字符)等等。此外,它还支持使用 {} 来指定多个模式,以及使用 ** 来匹配任意深度的目录。

在 Dart 和 Flutter 的项目中,glob 库被广泛用于各种工程化工具中,如 build_runner(一个用于 Dart 代码生成和模块化编译的构建系统)和一些cli 工具中。当我们需要开发自己的工程化工具时,glob 库将是一个非常有用的工具。

2. glob 库入门

2.1 安装 glob 库

和安装其它的 Dart/Flutter 模块一样。首先,你需要在项目的 pubspec.yaml 文件中添加 glob 库的依赖。在 dependencies 部分添加以下代码:

  dependencies:glob: ^2.1.2

然后,运行 flutter或dart pub get 命令来下载和安装 glob 库:

dart pub get

这样,glob 库就被成功安装到你的项目中了。

或者直接使用 pub add 命令安装当前发布的最新版本:

flutter pub add glob

着将自动添加依赖到 pubspec.yaml 文件中并隐式运行 pub get命令。

2.2 初体验:使用 glob 库进行文件匹配

使用 glob 库进行文件匹配也非常简单。首先,你需要导入 glob 库:

import 'package:glob/glob.dart';

然后,你可以创建一个 Glob 对象,并使用 matches 方法来检查一个路径是否匹配给定的 glob 模式:

var glob = Glob('**.dart');
if (glob.matches('lib/main.dart')) {print('The path matches the glob pattern');
} else {print('The path does not match the glob pattern');
}

3. glob 模式的基本语法

3.1 glob 模式中的 通配符

在 glob 模式中,* 字符匹配除 / 之外的零个或多个任何字符。这意味着它可以用来匹配给定目录中匹配模式的所有文件,而不会匹配子目录中的文件。例如,lib/*.dart 将匹配 lib/glob.dart,但不匹配 lib/src/utils.dart

** 是类似 *,但也匹配 /。它对于匹配文件或递归列出目录很有用。例如,lib/**.dart 将匹配 lib/glob.dartlib/src/utils.dart

? 字符匹配除 / 之外的单个字符。与 * 不同,它不会匹配多于或少于一个字符。例如,test?.dart 将匹配 test1.dart,但不匹配 test10.darttest.dart

3.2 glob 模式中的 字符集

[...] 构造匹配几个字符中的一个。它可以包含单个字符,如 [abc],在这种情况下,它将匹配任何这些字符;它可以包含范围,如 [a-zA-Z],在这种情况下,它将匹配任何落在范围内的字符;或者它可以包含两者的混合。它只会匹配一个字符。例如,test[a-zA-Z_].dart 将匹配 testx.darttestA.darttest_.dart,但不匹配 test-.dart

如果它以 ^! 开头,构造将匹配所有未提到的字符。例如,test[^a-z].dart 将匹配 test1.dart,但不匹配 testa.dart

3.3 glob 模式中的 选择器

{...,...} 构造匹配几个选项中的一个,每个选项都是一个 glob。例如,lib/{*.dart,src/*} 匹配 lib/glob.dartlib/src/data.txt。它可以包含大于一个的任何数量的选项,甚至可以包含嵌套的选项。

3.4 glob 模式中的 目录匹配

所有 globs 使用 POSIX 路径语法,包括使用 / 作为目录分隔符,无论它们在哪个平台上。这对于 Windows 根目录也是如此;例如,匹配 C 驱动器中所有文件的 glob 将是 C:/*

默认情况下,globPosix 系统和浏览器上是 区分大小写的,在 Windows 上不区分大小写。

4. 使用 glob 库进行文件系统遍历

4.1 如何使用 glob 库查找文件

使用 glob 库查找文件,你可以使用 Glob.list()Glob.listSync() 方法列出所有匹配 glob 的文件:

import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';final dartFile = Glob("**.dart");// 列出当前目录中的所有 Dart 文件。
void main(List<String> arguments) {for (var entity in dartFile.listSync()) {print(entity.path);}
}

在这个例子中,我们创建了一个 glob 模式 **.dart,它会匹配所有的 Dart 文件。然后,我们使用 listSync() 方法列出所有匹配这个模式的文件。

下面是一个新建的项目,运行 glob_demo.dart:
在这里插入图片描述
从图中可以看到,其输出结果为:

.\bin\glod_demo.dart
.\lib\glod_demo.dart
.\test\glod_demo_test.dart

4.2 如何使用 glob 库查找目录

使用 glob 库查找目录,你可以创建一个 glob 模式来匹配目录,然后使用 Glob.list()Glob.listSync() 方法列出所有匹配 glob 的目录:

import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';final directory = Glob("**/");// 列出当前目录中的所有子目录。
void main(List<String> arguments) {for (var entity in directory.listSync()) {print(entity.path);}
}

还是那个demo项目,其运行结果如图:
在这里插入图片描述
可以看到输出为:

.\.dart_tool
.\.dart_tool\package_config.json
.\.gitignore
.\analysis_options.yaml
.\bin
.\bin\glod_demo.dart
.\CHANGELOG.md
.\lib
.\lib\glod_demo.dart
.\pubspec.lock
.\pubspec.yaml
.\README.md
.\test
.\test\glod_demo_test.dart

在这个例子中,我们创建了一个 glob 模式 **/,它会匹配所有的子目录。然后,我们使用 listSync() 方法列出所有匹配这个模式的目录。

4.3 关于在 glob 库中 是否递归的说明

glob 库中,递归查找是通过 glob 模式中的 ** 来控制的。** 表示匹配任意深度的目录,因此,如果你在 glob 模式中使用了 **,那么 glob 库将会递归地查找所有子目录。

例如,以下代码将递归列出当前目录及其所有子目录中的所有 Dart 文件:

import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';final dartFile = Glob("**.dart");// 递归列出当前目录中的所有 Dart 文件。
void main(List<String> arguments) {for (var entity in dartFile.listSync()) {print(entity.path);}
}

在这个例子中,**.dart 会递归匹配当前目录及其所有子目录中的所有 Dart 文件。

如果你只想在当前目录(不包括子目录)中查找文件,你应该使用单个星号 *。例如,以下代码将只列出当前目录中的所有 Dart 文件,不会查找子目录:

import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';final dartFile = Glob("*.dart");// 列出当前目录中的所有 Dart 文件。
void main(List<String> arguments) {for (var entity in dartFile.listSync()) {print(entity.path);}
}

在这个例子中,*.dart 只会匹配当前目录中的 Dart 文件,不会匹配子目录中的文件。

5. list 和 listSync函数说明

这两个方法都是 Glob 类的扩展方法,定义在 package:glob/list_local_fs.dart 中。它们都用于列出匹配 glob 模式的文件系统实体(文件、目录等)。

/// Platform specific extensions for where `dart:io` exists, which use the
/// local file system.
extension ListLocalFileSystem on Glob {/// Convenience method for [Glob.listFileSystem] which uses the local file/// system.Stream<FileSystemEntity> list({String? root, bool followLinks = true}) =>listFileSystem(const LocalFileSystem(),root: root, followLinks: followLinks);/// Convenience method for [Glob.listFileSystemSync] which uses the local/// file system.List<FileSystemEntity> listSync({String? root, bool followLinks = true}) =>listFileSystemSync(const LocalFileSystem(),root: root, followLinks: followLinks);
}

使用时,需要做以下导入:

import 'package:glob/list_local_fs.dart';

5.1 list 方法

list 方法是一个异步方法,返回一个 Stream。这个 Stream 包含所有匹配 *glob 模式的文件系统实体。由于它是异步的,所以它不会阻塞主线程。

list 方法接受两个可选参数:rootfollowLinks

  • root 参数用于指定搜索的根目录,如果不指定,则默认为当前目录;
  • followLinks 参数决定是否跟随符号链接,如果设置为 true,则会跟随符号链接,否则不会。

list 方法内部调用了 listFileSystem 方法,并传入了一个 LocalFileSystem 实例,这表示它在本地文件系统上进行操作。

5.1 listSync 方法

listSync 方法是一个同步方法,返回一个 List,包含所有匹配 glob 模式的文件系统实体。由于它是同步的,所以它会立即返回所有匹配的文件系统实体。

listSync 方法也接受 rootfollowLinks 两个可选参数,含义与 list 方法中的相同。

listSync 方法内部调用了 listFileSystemSync 方法,并传入了一个 LocalFileSystem 实例,这表示它在本地文件系统上进行操作。

F. 附录

F.1 Glob 类

/// 用于引用 globs 的正则表达式。
final _quoteRegExp = RegExp(r'[*{[?\\}\],\-()]');/// 用于匹配和列出文件和目录的 glob。
///
/// glob 作为路径匹配整个字符串。虽然 glob 模式使用 POSIX 语法,但它可以匹配 POSIX、Windows 或 URL 路径。它期望路径使用的格式基于 [Glob.new] 的 `context` 参数;默认为当前系统的语法。
///
/// 在与 glob 匹配之前,路径会被规范化,所以例如 glob `foo/bar` 匹配路径 `foo/./bar`。相对 glob 可以匹配绝对路径,反之亦然;globs 和路径都被解释为相对于 `context.current`,默认为当前工作目录。
///
/// 当用作 [Pattern] 时,glob 将根据整个字符串是否匹配 glob 返回一个或零个匹配。这些匹配目前没有捕获组,尽管这可能在未来会改变。
class Glob implements Pattern {/// 用于创建此 glob 的模式。final String pattern;/// 根据此 glob 解释路径的上下文。final p.Context context;/// 如果为 true,如果路径匹配 glob 本身或递归地包含在匹配的目录中,则路径匹配。final bool recursive;/// glob 是否区分大小写匹配路径。bool get caseSensitive => _ast.caseSensitive;/// glob 的解析 AST。final AstNode _ast;/// 用于实现 [list] 和 [listSync] 的底层对象。////// 这不应在 [_listTreeForFileSystem] 之外直接读取。ListTree? _listTree;/// 跟踪之前使用的文件系统。如果这改变了,那么/// [_listTree] 必须被废弃。////// 这在 [_listTreeForFileSystem] 中处理。FileSystem? _previousFileSystem;/// [context] 的当前目录是否是绝对的。bool get _contextIsAbsolute =>_contextIsAbsoluteCache ??= context.isAbsolute(context.current);bool? _contextIsAbsoluteCache;/// [pattern] 是否可以匹配绝对路径。bool get _patternCanMatchAbsolute =>_patternCanMatchAbsoluteCache ??= _ast.canMatchAbsolute;bool? _patternCanMatchAbsoluteCache;/// [pattern] 是否可以匹配相对路径。bool get _patternCanMatchRelative =>_patternCanMatchRelativeCache ??= _ast.canMatchRelative;bool? _patternCanMatchRelativeCache;/// 返回 [contents],其中包含在 globs 中有意义的字符,这些字符被反斜杠转义。static String quote(String contents) =>contents.replaceAllMapped(_quoteRegExp, (match) => '\\${match[0]}');/// 使用 [pattern] 创建一个新的 glob。////// 根据 [context] 解释与 glob 匹配的路径。默认为系统上下文。////// 如果 [recursive] 为 true,此 glob 不仅匹配和列出它明确匹配的文件和目录,而且还匹配那些下面的任何内容。////// 如果 [caseSensitive] 为 true,此 glob 只匹配和列出那些大小写与 glob 中的字符匹配的文件。否则,它无论大小写都匹配。当 [context] 为 Windows 时,默认为 `false`,否则为 `true`。factory Glob(String pattern,{p.Context? context, bool recursive = false, bool? caseSensitive}) {context ??= p.context;caseSensitive ??= context.style == p.Style.windows ? false : true;if (recursive) pattern += '{,/**}';var parser = Parser(pattern, context, caseSensitive: caseSensitive);return Glob._(pattern, context, parser.parse(), recursive);}Glob._(this.pattern, this.context, this._ast, this.recursive);/// 列出在提供的 [fileSystem] 中与 glob 匹配的 [root] 下的所有 [FileSystemEntity]。////// 这与 [Directory.list] 工作方式类似,但它只列出可能包含匹配 glob 的实体的目录。它不保证返回实体的顺序,尽管它确保只返回给定路径的一个实体。////// [root] 默认为当前工作目录。////// [followLinks] 与 [Directory.list] 的工作方式相同。Stream<FileSystemEntity> listFileSystem(FileSystem fileSystem,{String? root, bool followLinks = true}) {if (context.style != p.style) {throw StateError("Can't list glob \"$this\"; it matches "'${context.style} paths, but this platform uses ${p.style} paths.');}return _listTreeForFileSystem(fileSystem).list(root: root, followLinks: followLinks);}/// 在提供的 [fileSystem] 中,同步列出在 [root] 下的所有与 glob 匹配的 [FileSystemEntity]。////// 这与 [Directory.listSync] 的工作方式类似,但它只列出可能包含匹配 glob 的实体的目录。它不保证返回实体的顺序,尽管它确保只返回给定路径的一个实体。////// [root] 默认为当前工作目录。////// [followLinks] 与 [Directory.list] 的工作方式相同。List<FileSystemEntity> listFileSystemSync(FileSystem fileSystem,{String? root, bool followLinks = true}) {if (context.style != p.style) {throw StateError("Can't list glob \"$this\"; it matches "'${context.style} paths, but this platform uses ${p.style} paths.');}return _listTreeForFileSystem(fileSystem).listSync(root: root, followLinks: followLinks);}/// 返回此 glob 是否匹配 [path]。bool matches(String path) => matchAsPrefix(path) != null;Match? matchAsPrefix(String path, [int start = 0]) {// Globs 就像锚定的 RegExps,只匹配整个路径,所以如果匹配从第一个字符之后的任何地方开始,它就不能成功。if (start != 0) return null;if (_patternCanMatchAbsolute &&(_contextIsAbsolute || context.isAbsolute(path))) {var absolutePath = context.normalize(context.absolute(path));if (_ast.matches(toPosixPath(context, absolutePath))) {return GlobMatch(path, this);}}if (_patternCanMatchRelative) {var relativePath = context.relative(path);if (_ast.matches(toPosixPath(context, relativePath))) {return GlobMatch(path, this);}}return null;}Iterable<Match> allMatches(String path, [int start = 0]) {var match = matchAsPrefix(path, start);return match == null ? [] : [match];}String toString() => pattern;/// 处理获取可能缓存的 [ListTree] 为 [fileSystem]。ListTree _listTreeForFileSystem(FileSystem fileSystem) {// 不要为内存文件系统使用缓存的树,以避免内存泄漏。if (fileSystem is MemoryFileSystem) return ListTree(_ast, fileSystem);// 如果文件系统不同,丢弃我们缓存的 `_listTree`。if (fileSystem != _previousFileSystem) {_listTree = null;_previousFileSystem = fileSystem;}return _listTree ??= ListTree(_ast, fileSystem);}
}

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

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

相关文章

2023 羊城杯 final

前言 笔者并未参加此次比赛, 仅仅做刷题记录. 题目难度中等偏下吧, 看你记不记得一些利用手法了. arrary_index_bank 考点: 数组越界 保护: 除了 Canary, 其他保护全开, 题目给了后门 漏洞点: idx/one 为 int64, 是带符号数, 所以这里存在向上越界, 并且 buf 为局部变量,…

ROS1余ROS2共存的一键安装(全)

ROS1的安装&#xff1a; ROS的一键安装&#xff08;全&#xff09;_ros一键安装_牙刷与鞋垫的博客-CSDN博客 ROS2的安装 在开始这一部分的ROS2安装之前&#xff0c;是可以安装ROS1的&#xff0c;当然如果你只需要安装ROS2的话就执行从此处开始的代码即可 我是ubuntu20.4的版…

电力感知边缘计算网关产品设计方案-业务流程设计

1.工业数据通信流程 工业数据是由仪器仪表、PLC、DCS等工业生产加工设备提供的,通过以太网连接工业边缘计算网关实现实时数据采集。按照现有的通信组网方案,在理想通信状态下可以保证有效获取工业数据的真实性和有效性。 边缘计算数据通信框架图: 2.边缘计算数据处理方案 …

Linux驱动开发——块设备驱动

目录 一、 学习目标 二、 磁盘结构 三、块设备内核组件 四、块设备驱动核心数据结构和函数 五、块设备驱动实例 六、 习题 一、 学习目标 块设备驱动是 Linux 的第二大类驱动&#xff0c;和前面的字符设备驱动有较大的差异。要想充分理解块设备驱动&#xff0c;需要对系统…

高效开发与设计:提效Spring应用的运行效率和生产力 | 京东云技术团队

引言 现状和背景 Spring框架是广泛使用的Java开发框架之一&#xff0c;它提供了强大的功能和灵活性&#xff0c;但在大型应用中&#xff0c;由于Spring框架的复杂性和依赖关系&#xff0c;应用的启动时间和性能可能会受到影响。这可能导致开发过程中的迟缓和开发效率低下。优…

Golang基础-面向过程篇

文章目录 基本语法变量常量函数import导包匿名导包 指针defer静态数组动态数组(slice)定义方式slice追加元素slice截取 map定义方式map使用方式 基本语法 go语言输出hello world的语法如下 package mainimport ("fmt""time" )func main() {fmt.Println(&…

循环链表2

循环链表的实现 对于数据结构中所有的结构而言&#xff0c;每一次都是用之前初始化&#xff08;处理一开始的随机值&#xff09;一下&#xff0c; 用完销毁&#xff08;不管有没有malloc都能用&#xff0c;用了可以保证没有动态内存泄漏了&#xff09;一下 而在C里面&#x…

Dubbo开发系列

一、概述 以上是 Dubbo 的工作原理图&#xff0c;从抽象架构上分为两层&#xff1a;服务治理抽象控制面 和 Dubbo 数据面 。 服务治理控制面。服务治理控制面不是特指如注册中心类的单个具体组件&#xff0c;而是对 Dubbo 治理体系的抽象表达。控制面包含协调服务发现的注册中…

PLC设备相关常用英文单词(一)

PLC设备相关常用英文单词&#xff08;一&#xff09; Baud rate 波特率Bus 总线Binary 二进制Configuration 组态Consistent data 一致性数据Counter 计数器Cycle time 循环时间Conveyor 传送Device names 设备名称Debug 调试Download 下载Expand 扩展Fix 固定Flow 流量Functio…

【LeetCode:689. 三个无重叠子数组的最大和 | 序列dp+前缀和】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

WMS系统先验后收策略

在制造业工厂的仓库管理中&#xff0c;确保物料的质量和数量是至关重要的。传统的仓库管理方式往往采用“先收后验”策略&#xff0c;即先接收物料&#xff0c;然后再进行质量检验。然而&#xff0c;这种方式存在一定的风险&#xff0c;例如不良品流入、数量不准确等问题。为了…

腾讯云服务器标准型S5实例CPU性能如何?配置特性说明

腾讯云服务器CVM标准型S5实例具有稳定的计算性能&#xff0c;CVM 2核2G S5活动优惠价格280.8元一年自带1M带宽&#xff0c;15个月313.2元、2核4G配置748.2元15个月&#xff0c;CPU内存配置还可以选择4核8G、8核16G等配置&#xff0c;公网带宽可选1M、3M、5M或10M&#xff0c;腾…

优思学院|现代质量管理实践与六西格玛方法论如何融合?

企业要解决质量问题必然需要涉及管理&#xff0c;然而&#xff0c;如果仅仅将六西格玛法视为一种质量管理方法&#xff0c;必定会导致六西格玛管理法的失败。六西格玛法是一种具有特定战略性的管理方法&#xff0c;它涉及到市场、顾客、产品、服务、流程、质量、价值链以及财务…

Pytorch完整的模型训练套路

Pytorch完整的模型训练套路 文章目录 Pytorch完整的模型训练套路以CIFAR10为例实践 数据集加载步骤 使用适当的库加载数据集&#xff0c;例如torchvision、TensorFlow的tf.data等。 将数据集分为训练集和测试集&#xff0c;并进行必要的预处理&#xff0c;如归一化、数据增强等…

深搜回溯剪枝-全排列

LCR 083. 全排列 - 力扣&#xff08;LeetCode&#xff09; 根据题意&#xff0c;要根据给定的整数数组&#xff0c;穷举出所有可能的排列&#xff0c;从直观的角度上来看&#xff0c;可以使用多层 for 循环来解决&#xff0c;但如果是数组长度太大的时候&#xff0c;这种方式不…

配置Java环境变量不生效的解决办法

问题&#xff1a; 直接更换Java_HOME的JDK安装路径后&#xff0c;竟然环境变量不生效&#xff0c;在cmd窗口输入java -version或者javac -version后报错&#xff1f;&#xff1f;&#xff1f;这是为什么呢&#xff1f; 问题剖析&#xff1a; 在使用安装版本的JDK程序时&#…

DataFunSummit:2023年数据基础架构峰会-核心PPT资料下载

一、峰会简介 正如From、Join、排序等是SQL的基本算子&#xff0c;存储与计算是也是数据架构中数据生产与消费的基本算子&#xff0c;对于数据架构之下的技术栈层级&#xff0c;我们可将其定义为数据基础架构。 数据存储技术在适应大数据时代的规模需求基础之上&#xff0c;持…

【23真题】难!985难度前五名!

今天分享的是23年中山大学884的信号与系统试题及解析。 本套试卷难度分析&#xff1a;22年中山大学884考研真题&#xff0c;我也发布过&#xff0c;若有需要&#xff0c;戳这里自取!22年并不是很难&#xff0c;今年难度突然大幅度提升&#xff01;原因不明。23年平均分为100分…

新手教师如何迅速成长

对于许多新手教师来说&#xff0c;迈出教学的第一步可能会感到非常困难。不过&#xff0c;通过一些关键的策略和技巧&#xff0c;还是可以快速提升教学能力的&#xff0c;我将为大家提供一些实用的建议&#xff0c;帮助各位在教育领域迅速成长。 深入了解学科知识 作为一名老师…