Allocation-free glob matching for byte strings in pure Zig.
*,?, sets ([abc]), ranges ([a-z]), and negated sets ([!abc],[^abc])- Backslash escaping and whole-pattern negation with a leading
! - Optional pathname, leading-period, no-escape, and ASCII case-insensitive behavior
- Configurable matching reports malformed patterns
- Constant stack use, no allocation, and no dependencies
- Multiple-pattern helpers:
matchAnyandmatchAll
Matching is byte-oriented. ? consumes one byte, not one Unicode scalar.
zig fetch --save=glob https://github.com/xcaeser/glob.zig/archive/v0.2.0.tar.gzAdd the module to your executable or library:
const glob_dep = b.dependency("glob", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("glob", glob_dep.module("glob"));const std = @import("std");
const glob = @import("glob");
pub fn main() !void {
std.debug.assert(glob.match("src/*.zig", "src/main.zig"));
std.debug.assert(glob.match("file[0-9].txt", "file7.txt"));
std.debug.assert(glob.match("!*.tmp", "notes.txt"));
std.debug.assert(glob.matchAll(&.{ "*.txt", "!test_*" }, "notes.txt"));
const path = try glob.matchWithOptions("src/*.zig", "src/lib/main.zig", .{
.pathname = true,
});
std.debug.assert(!path); // * cannot cross /
const readme = try glob.matchWithOptions("README.*", "readme.md", .{
.case_sensitivity = .insensitive_ascii,
});
std.debug.assert(readme);
try glob.validate("[a-z]*");
}match(pattern, text) bool— the default. Malformed patterns returnfalse.matchWithOptions(pattern, text, options) !bool— configurable matching; malformed patterns return aValidationError.validate(pattern) !void— validate default pattern syntax without matching.matchAny(patterns, text) bool— match at least one default-syntax pattern.matchAll(patterns, text) bool— match every default-syntax pattern.
| Pattern | Meaning |
|---|---|
* |
Zero or more bytes |
? |
Exactly one byte |
[abc] |
One byte in the set |
[a-z] |
One byte in the inclusive range |
[!abc], [^abc] |
One byte outside the set |
\*, \?, \[ |
Escaped literal byte |
!pattern |
Negate the whole pattern |
- is literal at either edge of a class or when escaped. Invalid patterns
return false from match; matchWithOptions reports UnclosedBracket,
EmptyBracket, InvalidRange, or TrailingBackslash.
matchWithOptions accepts:
case_sensitivity = .insensitive_ascii— fold ASCII letter casepathname = true— wildcards and classes cannot match/period = true— a leading.must be matched by a literal.; withpathname, this also applies after/no_escape = true— treat\as an ordinary byte
matchAny treats every pattern independently, including negated patterns.
Use matchAll(&.{ "*.txt", "!test_*" }, text) for an include-and-exclude
filter. The list helpers intentionally use default options; for custom options,
loop over the patterns and call matchWithOptions explicitly.
zig fmt --check .
zig build test --summary all
zig build docsMIT licensed. See LICENSE and CONTRIBUTING.md.