Overview
Masterly.FluentRegex is a library for building .NET regular expressions you can read. Instead of writing ^\d{3}-\d{4}$ by hand, you write Pattern.With.StartOfLine.Digit.Repeat.Times(3).Literal("-")... and the library writes the regex for you, escaping every special character along the way.
A pattern is more than a builder, though. The same immutable Pattern matches and extracts text, explains itself in plain English, proves whether two patterns accept the same strings, finds ReDoS risks, generates test data, and exports itself to JavaScript, JSON Schema and the regex dialects of five databases. Every call returns a new pattern, so building blocks are safe to share.
The core package targets .NET Standard 2.1, .NET 8 and .NET 10 and has no dependencies. It also ships a source generator and analyzers that check your patterns while you type. Five optional packages add Entity Framework Core, ASP.NET Core, xUnit, AI drafting and a command-line tool.
How it works
Working with patterns has four stages. Most projects use the first two every day and reach for the last two when a pattern matters: when it guards input, runs in a database, or must behave the same in a browser. Select a stage to read its guide.
- BuildCompose a pattern from readable building blocks, convert a regex you already have, or start from a ready-made one.
Pattern.With Pattern.FromRegex(. . . ) CommonPatterns.Email() - Match and extractTest, find, replace and split text, and bind what you capture straight onto typed objects.
pattern.IsMatch(input) pattern.Extract<T>(input) Template.Of<T>(. . . ) - Prove and testProve two patterns equivalent, see which branches your tests miss, and generate matching and failing data.
Pattern.Compare(a, b) pattern.MeasureCoverage(. . . ) pattern.GenerateMatching() - Ship anywhereExport the same pattern to JavaScript, JSON Schema and SQL, or run it inside Entity Framework Core queries.
pattern.ToJavaScript() pattern.ToSqlCondition(. . . ) EF.Functions. Matches(. . . )
The packages
All six packages ship together with one version number. Install the core first; add a companion package only when you use the framework it plugs into.
| Package | What it adds | Targets |
|---|---|---|
Masterly.FluentRegex | The fluent builder, matching, extraction, templates, pattern algebra, optimizer, ReDoS analysis, export and SQL dialects, plus the bundled source generator and analyzers. No dependencies. | .NET Standard 2.1, .NET 8, .NET 10 |
Masterly.FluentRegex.EntityFrameworkCore | UseFluentRegex(): patterns in LINQ run as the database's own regex, with proven index hints and CHECK constraints. | .NET 8 (EF Core 8), .NET 10 (EF Core 10) |
Masterly.FluentRegex.AspNetCore | AddFluentPatterns(...) and the pattern(name) route constraint. | .NET 8, .NET 10 |
Masterly.FluentRegex.Xunit | [MatchingData] and [NonMatchingData] theory data for xUnit v3. | .NET 8, .NET 10 |
Masterly.FluentRegex.AI | PatternAssistant, which drafts patterns from plain-language descriptions with Claude and verifies every draft locally. | .NET 8, .NET 10 |
Masterly.FluentRegex.Tool | The fluentregex .NET tool: compare, optimize, export, scan a codebase for ReDoS, and more. | .NET 8 |
See Installation for the install commands.
What's new in 3.1
Version 3.1 can prove things about your patterns, learn them from examples, and take them beyond .NET into browsers, databases, tests and logs.
| Feature | In one line |
|---|---|
| Pattern algebra | Pattern.Compare(a, b) proves two patterns equivalent, or returns the shortest string that tells them apart. |
| Verified optimizer | Optimize() rewrites cat|car|cab to ca[trb] and proves that nothing changed. |
| Typed templates | Template.Of<T>("{Date:yyyy-MM-dd} [{Level}] {Message}") parses and formats typed values. |
| Learning from examples | Pattern.Learn(match, reject) infers a readable, anchored pattern. |
| Regex coverage | MeasureCoverage(inputs) shows the branches and limits your tests never reach. |
| Property testing | PatternProperty.ForMatching(...) finds and shrinks counterexamples to a property. |
| Semantic rules and PII redaction | .Must(Validators.Luhn) adds checks a regex cannot express; Redactor masks personal data. |
| Export, SQL dialects and SQL LIKE | JavaScript, JSON Schema, PostgreSQL, MySQL, MariaDB, SQL Server 2025 and SQLite, each with the exact .NET meaning. |
| Language facts | GetLanguageFacts() proves length bounds and the prefix and suffix every match must have. |
| Backtracking profiler | Profile(input) counts backtracking steps per construct. |
| Streaming | MatchesAsync(stream) matches input of any size in constant memory. |
| Unicode | Unicode scripts, script-specific digits, graphemes, IgnoreDiacritics() and an ASCII digit mode. |
| Entity Framework Core, ASP.NET Core, xUnit, AI drafting | Four new companion packages. |
| Highlighting | Highlight(text, format) marks every match and group capture, as ANSI colors, encoded HTML or plain brackets. |
| Model validation | [MatchesPattern(typeof(Patterns), nameof(Patterns.Sku))] validates DataAnnotations models (.NET 8 and .NET 10). |
| Pattern equality | Pattern implements IEquatable<Pattern>, so patterns that render the same regex work as dictionary keys. |
| Debugger view | The debugger shows a pattern as its regex. Expand it to see options, groups, an explanation and a ReDoS verdict. |
| Command-line tool | Eight new commands: compare, optimize, learn, profile, highlight, coverage, export and facts. |
| Playground | The library running in your browser with WebAssembly, with shareable links. |
| Installation | New .NET 10 targets. The net9.0 build is gone; .NET 9 apps use the .NET 8 build. |
Three additions you will use every day
Highlighting, value equality and the [MatchesPattern] attribute need no setup. They are part of the core package.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Masterly.FluentRegex;
Pattern phone = Pattern.With
.Digit.Repeat.Times(3)
.Literal("-")
.Digit.Repeat.Times(4);
// Highlighting: mark every match
Console.WriteLine(phone.Highlight("Call 555-1234 or 555-9876", HighlightFormat.Brackets));
// prints: Call [555-1234] or [555-9876]
Console.WriteLine(phone.Highlight("Call 555-1234 <now>", HighlightFormat.Html));
// prints: Call <mark class="frx-match">555-1234</mark> <now>
// Equality: same regex, same options, same timeout
Pattern built = Pattern.With.Digit.Repeat.Times(3);
Pattern parsed = Pattern.With.RegEx(@"\d{3}");
Console.WriteLine(built.Equals(parsed)); // True
Console.WriteLine(built == parsed); // False
Dictionary<Pattern, string> names = new Dictionary<Pattern, string> { [built] = "three digits" };
Console.WriteLine(names[parsed]); // three digits
// DataAnnotations: validate a model property against a pattern
Order order = new Order { Sku = "ab-12" };
List<ValidationResult> results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(order, new ValidationContext(order), results, validateAllProperties: true);
Console.WriteLine(isValid); // False
Console.WriteLine(results[0].ErrorMessage); // The field Sku does not match the required pattern.
public static class Patterns
{
public static Pattern Sku { get; } = Pattern.With
.StartOfLine
.UppercaseLetter.Repeat.Times(2)
.Literal("-")
.Digit.Repeat.Times(4)
.EndOfLine;
}
public sealed class Order
{
[MatchesPattern(typeof(Patterns), nameof(Patterns.Sku))]
public string? Sku { get; set; }
}
Highlightdefaults toHighlightFormat.Ansi, for terminals.Htmlencodes all of the text, so<now>becomes<now>and the output is safe to put on a page.Bracketsis plain text for tests and logs. See Highlighting.- Two patterns are equal when they render the same regex text with the same options and timeout, however you built them. The
==operator keeps reference semantics, so useEquals. See Core Concepts. [MatchesPattern]takes the type and the name of a public static property, field or parameterless method that returns aPatternor aConstrainedPattern. The whole value must match. Null and empty strings are valid, so add[Required]to reject them. The attribute is not in the .NET Standard 2.1 build. See Semantic Rules.
You do not need any code for the debugger view. Hover over a Pattern variable to see its regex, then expand it. See Validation and Debugging.
Coming from 2.x? Version 3.0 made patterns immutable. Read Upgrading from 2.x before you update.
What you can do
- Build patternsAnchors, character classes, quantifiers, groups and lookarounds, one readable call at a time.
- Convert existing regexesTurn a cryptic regex into fluent C# with ToFluentCode(), then maintain it like any other code.
- Use ready-made patternsEmail, URL, IP addresses, dates, credit cards and more, each one a normal Pattern you can extend.
- Match, replace and splitTest, find, replace and split text, with replacements built from validated group names.
- Extract typed valuesBind named groups onto records and classes, or describe a whole line with a typed template.
- Redact personal dataMask emails, phone numbers and card numbers in text, and get a report of what was found.
- Prove patterns equivalentCompare two patterns exactly, and get the shortest string that shows how they differ.
- Optimize safelySimplify a pattern and get a proof that it still matches exactly the same strings.
- Find ReDoS risksDetect catastrophic backtracking, and confirm each risk with a timed attack input.
- Explain, trace and drawDescribe a pattern in English, trace a match step by step, or draw it as a railroad diagram.
- Test your patternsMeasure coverage, generate matching and near-miss data, and run property tests.
- Learn from examplesGive strings that must and must not match; get a readable pattern back.
- Export everywhereWrite the same pattern for JavaScript, JSON Schema and five SQL regex dialects.
- Query the databaseRun patterns inside Entity Framework Core queries, with index hints and CHECK constraints.
- Catch mistakes at build timeThe bundled analyzers flag invalid patterns, ReDoS risks and group-name typos as you type.
- Highlight matchesMark every match and group in a text, as terminal colors, encoded HTML or plain brackets.
- Validate values and modelsAdd checksum and date rules a regex cannot express, and validate models with [MatchesPattern].
- Use the command lineScan a codebase for ReDoS in CI, or compare, optimize, export and highlight from a terminal.
Contributing and building from source
Contributions are welcome. For a major change, please open an issue on GitHub first to discuss what you would like to change.
What you need
| Tool | Why |
|---|---|
| .NET 10 SDK | Builds every target: .NET Standard 2.1, .NET 8 and .NET 10. |
| .NET 8 runtime | Runs the tests on .NET 8. The .NET 8 SDK includes it. |
| Node.js 20 or later | Runs the documentation site. |
| Docker (optional) | Runs the Entity Framework Core tests against real database servers. |
Building and testing
git clone https://github.com/a7mdfre7at/Masterly.FluentRegex.git
cd Masterly.FluentRegex
dotnet test # .NET 8 and .NET 10
dotnet run --project samples/Masterly.FluentRegex.Showcase # a tour of every feature
dotnet testbuilds the solution and runs every test project on .NET 8 and .NET 10. The playground tests run on .NET 10 only.- The tests in
test/Masterly.FluentRegex.EntityFrameworkCore.Tests/Containersstart PostgreSQL, MySQL, MariaDB and SQL Server 2025 in Docker. When Docker cannot be reached, they are skipped, not failed. To leave them out, rundotnet test --filter "FullyQualifiedName!~Containers", as the build workflow does. - The showcase is a console app that references the library project, so it shows your local changes. It prints one numbered section per feature, starting with
1. Immutable building blocks and operators. - The build workflow also scans the repository for risky regex literals. Run the same check with
dotnet run --project tools/Masterly.FluentRegex.Cli -- scan . --fail-on high. See Command-Line Tool. - A build does not create packages. Run
dotnet pack -c Releaseto create the.nupkgfiles.
Running the documentation site
The site you are reading lives in the website folder. It is a Docusaurus site:
cd website
npm ci
npm start
npm startserves the site athttp://localhost:3000/Masterly.FluentRegex/and reloads a page when you save its file inwebsite/docs.npm run buildbuilds the static site, as the docs workflow does. It fails on any broken link or anchor, so run it before you open a pull request that changes the docs.- The browser playground is a separate .NET project in
playground/. See Playground to run it locally.
Where to go next
- New hereQuick StartBuild a first pattern, test it, extract matches with named groups and explain it.
- See it in contextReal-world examplesComplete, working patterns for real validation and parsing tasks.
- Try it nowPlaygroundBuild, explain, test and secure patterns in your browser, with shareable links.
- Coming from 2.xUpgrading from 2.xWhat changed when patterns became immutable, and how to update your code.
- Looking something upAPI referenceEvery public type and member, one package at a time.
- Need a refresherRegex cheat sheetA one-page reference for regular expression syntax.