Bodu.Text.Filtering 1.0.0

dotnet add package Bodu.Text.Filtering --version 1.0.0
                    
NuGet\Install-Package Bodu.Text.Filtering -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Bodu.Text.Filtering" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bodu.Text.Filtering" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Bodu.Text.Filtering" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Bodu.Text.Filtering --version 1.0.0
                    
#r "nuget: Bodu.Text.Filtering, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Bodu.Text.Filtering@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Bodu.Text.Filtering&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Bodu.Text.Filtering&version=1.0.0
                    
Install as a Cake Tool

Bodu.Text.Filtering

API stability — Stable. The public API surface is committed; breaking changes are reserved for a major-version bump per SemVer.

A high-performance include/exclude filtering engine for lists of text values. A set of glob (wildcard) and regex patterns compiles once into a TextFilter, which then classifies each pattern by evaluation cost and runs the cheapest matchers first — so filtering 100k+ items against 10–100+ patterns stays fast even when some patterns are regexes.

using Bodu.Text.Filtering;

var filter = TextFilter.Parse(new[]
{
    "error*",        // include everything starting with "error"
    "warn*",         // ... or "warn"
    "!*debug*",      // but exclude anything containing "debug"
});

foreach (var line in filter.Filter(lines))
    Console.WriteLine(line);

Design lineage

This library deliberately adopts the best-established designs from well-known filtering and globbing engines rather than inventing new semantics:

Design Borrowed from
Unordered include/exclude sets: empty includes ⇒ include-all; excludes always veto Ant DirectoryScanner, MSBuild item globs, Microsoft.Extensions.FileSystemGlobbing
Ordered rules where the last matching rule wins, ! negation, # comments gitignore / ESLint ignore files
Compile many patterns at once and extract literal/prefix/suffix strategies so cheap matchers run before regex; report which patterns matched Rust globset (ripgrep)
Fluent builder (AddInclude / AddExclude) over a compiled matcher Microsoft.Extensions.FileSystemGlobbing.Matcher
Glob grammar: *, ?, [abc] / [a-z] / [!abc], {a,b} alternation, \ escape Java PathMatcher, minimatch, shell glob
Filtering statistics surface ripgrep --stats

Semantics

An item is evaluated against the compiled pattern set according to the configured TextFilterEvaluationMode:

  • AnyMatch (default — Ant/MSBuild-style sets): an item is accepted iff (the include set is empty OR at least one include matches) AND no exclude matches. Group matching is an order-independent OR, so the engine is free to evaluate patterns cheapest-first.
  • LastMatchWins (gitignore-style ordered rules): the last matching rule's action decides; an item that matches no rule is included (gitignore-faithful). Allowlists are expressed with a leading exclude-everything rule: ["!*", "error*", "!*debug*"] keeps only error* items except those containing debug.

Matching is whole-string (use *abc* for contains-style matching) and, by default, ordinal and case-insensitive; case sensitivity is configurable per filter and overridable per pattern.

Glob grammar

Syntax Meaning
* zero or more characters
? exactly one character
[abc], [a-z] one character from the set / range
[!abc] one character not in the set
{a,b} alternation, expanded at build time ({error,warn}* compiles into two prefix matchers)
\x literal x (escapes *?[]{}\!# metacharacters)

Anything richer is expressed as a TextFilterPatternKind.Regex pattern. Regexes prefer the linear-time RegexOptions.NonBacktracking engine and always carry a match timeout; a timed-out pattern fails safe (a timed-out include does not admit the item; a timed-out exclude still vetoes it).

Cost tiers

At build time each glob is classified so evaluation runs cheapest-first (the globset idea):

MatchAll → Literal → Prefix / Suffix / PrefixAndSuffix → Contains → general wildcard (iterative two-pointer matcher) → Regex.

Indicative performance

BenchmarkDotNet (short job) on the development container, filtering a 100,000-value synthetic corpus per invocation; the baseline evaluates one compiled Regex per pattern per value:

Pattern count Mixed-tier TextFilter Per-pattern compiled regex Speed-up
10 ~15 ms ~36 ms ~2.4×
50 ~73 ms ~170 ms ~2.3×
100 ~132 ms ~443 ms ~3.4×

All TextFilter passes allocate nothing per value, and attaching a no-op ITextFilterObserver was within measurement noise. Reproduce with:

dotnet run -c Release --project Bodu.Text.Filtering/bench/Bodu.Text.Filtering.Benchmarks.csproj -- --filter '*TextFilter*'

Telemetry

TextFilter keeps always-on counters (items evaluated / accepted / excluded / not-included, per-pattern hit counts, regex timeouts, opt-in timing) exposed via GetStatistics(), and an optional ITextFilterObserver invoked per decision with the deciding pattern — a single null check when unattached.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 98 9/24/2026
0.7.0 473 9/24/2026
0.6.0 104 9/24/2026
0.5.0 108 9/23/2026