FluentRegexBuilder 1.0.2

dotnet add package FluentRegexBuilder --version 1.0.2
                    
NuGet\Install-Package FluentRegexBuilder -Version 1.0.2
                    
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="FluentRegexBuilder" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FluentRegexBuilder" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="FluentRegexBuilder" />
                    
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 FluentRegexBuilder --version 1.0.2
                    
#r "nuget: FluentRegexBuilder, 1.0.2"
                    
#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 FluentRegexBuilder@1.0.2
                    
#: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=FluentRegexBuilder&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=FluentRegexBuilder&version=1.0.2
                    
Install as a Cake Tool

FluentRegexBuilder

"Some people, when confronted with a problem, think 'I know, I'll use regular expressions.' Now they have two problems." — Jamie Zawinski, 1997. Still true.

You know regex. Of course you know regex. You've "known" regex for years — which is to say, every single time you need one, you open a new tab and search "regex lookbehind syntax" like it's the first day of your career.

Is lookbehind (?<= or (?=<? Is a non-capturing group (?: or (:?? Does {2,} mean "two or more" or "I have made a terrible mistake"? Why does . match everything except the one thing you wanted? Nobody knows. Nobody has ever known. Regex is a language designed by people who charged by the character.

FluentRegexBuilder lets you write the pattern the way you'd say it, and generates the line noise for you:

var url = FluentRegex.Create()
    .StartOfLine()
    .Then("http")
    .Maybe("s")
    .Then("://")
    .Maybe("www.")
    .AnythingBut(" ")
    .EndOfLine()
    .ToRegex();

// Generates: ^http(?:s)?://(?:www\.)?[^ ]*$
// You wrote zero backslashes. You escaped nothing. You feel nothing but peace.

It's still 100% real .NET Regex underneath — this is a builder, not a new engine. You get a normal Regex object at the end; we just spare you the part where you count backslashes at 11 PM.

Install

dotnet add package FluentRegexBuilder

Targets .NET 8 and .NET 10. Zero dependencies. Zero opinions about your tab-vs-spaces stance.

The "I definitely can't remember that" translation table

You wanted to say You were supposed to remember Now you write
start / end of line ^ $ StartOfLine() EndOfLine()
start / end of the whole string, honest \A \z StartOfString() EndOfString()
edge of a word \b (no, not backspace) WordBoundary()
this exact text, dots and all abc\.\$\( (escape it yourself, coward) Then("abc.$(") — escaped for you
maybe this text (?:abc)? Maybe("abc")
a digit / letter / space \d \w \s Digit() WordChar() Whitespace()
one of these characters [abc] AnyOf("abc")
anything except these [^abc] NoneOf("abc")
a range [a-z] Range('a', 'z')
exactly 3 / at least 2 / 2 to 5 {3} {2,} {2,5} Exactly(3) AtLeast(2) Between(2, 5)
one or more / any amount / optional + * ? OneOrMore() ZeroOrMore() Optional()
don't be greedy about it *? (a question mark that means something different here) .ZeroOrMore().Lazy()
remember this bit (...) Group(g => ...)
remember it by name (?<name>...) NamedGroup("name", g => ...)
group without remembering (?:...) NonCaptureGroup(g => ...)
the same thing group 1 matched \1 SameAs(1)
same, but by name \k<name> SameAs("name")
only if followed by (?=...) IfFollowedBy(...)
only if NOT followed by (?!...) IfNotFollowedBy(...)
only if preceded by (?<=...)not (?=<, you've been burned before IfPrecededBy(...)
only if NOT preceded by (?<!...) IfNotPrecededBy(...)
this or that, scoped sanely (?:cat\|dog) OneOf("cat", "dog")
ignore case RegexOptions.IgnoreCase CaseInsensitive()

Every method carries XML docs showing the exact regex it emits — so IntelliSense doubles as the cheat sheet you were going to google anyway.

A real one: SSN validation

Here is a genuinely correct SSN pattern, in its natural habitat:

^(?!(\d)\1{2}-\1{2}-\1{4})(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}$

Beautiful. Correct. Utterly unreviewable. Six months from now this line will have a // do not touch comment above it and a small shrine beside it.

The same rules, in a form your code reviewer can actually review:

var ssn = FluentRegex.Create()
    .StartOfLine()
    .IfNotFollowedBy(b => b               // reject 111-11-1111 and friends
        .Group(g => g.Digit())            // capture the first digit → group 1
        .SameAs(1).Exactly(2)
        .Then("-").SameAs(1).Exactly(2)
        .Then("-").SameAs(1).Exactly(4))
    .IfNotFollowedBy(b => b               // reject invalid area numbers
        .OneOf("000", "666")
        .Or(o => o.Then("9").Digit().Exactly(2)))
    .Digit().Exactly(3)
    .Then("-")
    .IfNotFollowedBy("00")
    .Digit().Exactly(2)
    .Then("-")
    .IfNotFollowedBy("0000")
    .Digit().Exactly(4)
    .EndOfLine()
    .ToRegex();

Yes, that's negative lookaheads with backreferences into a capture group, and it reads like a checklist instead of a cry for help.

Things it quietly saves you from

  • The escaping game. Literals passed to Then, Maybe, OneOf, and the lookaround overloads are Regex.Escaped automatically. . means dot. $ means dollar sign. ( will not silently open a group and break capture numbering three lines away.
  • The classic quantifier bug. In raw regex, abc{2} matches abcc — the {2} only grabs the c, and this exact bug has shipped to production more times than anyone will admit. Here, .Then("abc").Exactly(2) produces (?:abc){2}. The whole thing. Twice. Like you meant.
  • Catastrophic backtracking roulette. ToRegex(TimeSpan) sets a match timeout, so untrusted input can't put your CPU into interpretive dance. There's AtomicGroup(...) too, if you know what that is (and if you do — hello, fellow person of suffering).
  • The "nothing to quantify" bug. FluentRegex.Create().Exactly(3) throws a clear exception instead of silently producing garbage.

And when you do know exactly what you want, Set("A-Za-z0-9") is the raw, unescaped escape hatch. We're a builder, not your manager.

Using with [GeneratedRegex]

The source generator wants a compile-time constant string, so use the builder as a design-time tool: build the chain, print .Pattern, paste the result into the attribute, and keep the fluent version in a test as living documentation. Best of both worlds — readable source of truth, zero runtime cost.

When NOT to use this

Honesty corner: regex is a lingua franca. \d+ is shorter than .Digit().OneOrMore() and you'll meet it again in JavaScript, SQL, grep, and the darker corners of your CI config — for trivial patterns, just learn the three characters. This library earns its keep on the patterns complex enough to need comments: multi-lookahead validation, backreference tricks, anything where a reviewer would otherwise just approve the PR on vibes.

License

MIT. Take it, use it, ship it. The regexes it generates are yours; the trauma that inspired it remains ours.

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.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.

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.2 125 7/7/2026
1.0.1 108 7/7/2026
1.0.0 116 7/7/2026