Library / SDK
VerbalExpressions/JavaVerbalExpressions avatar
VerbalExpressions/JavaVerbalExpressions

JavaVerbalExpressions: building Java regexes as a readable chain of method calls

Java regular expressions made easy.

2,619 stars243 forksJavaMIT

At a glance

What is it?
JavaVerbalExpressions wraps java.util.regex in a builder whose methods read like sentences, so a URL or log-line pattern is assembled with then(), maybe() and anythingBut() instead of a raw character-class string. The trade-off is that the fluent chain is the only interface, and the underlying Pattern is still what runs.
Who is it for?
Adopt JavaVerbalExpressions when the pattern is long enough that a raw regex string has become unreadable, or when several people have to edit the same pattern and agree on what it means. Skip it when you need lookbehind, backreferences or named groups, or when a single short pattern would be clearer written by hand.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 30 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What JavaVerbalExpressions is for, and who ends up using it

The README describes the library in one line: it "helps to construct difficult regular expressions." That is the whole scope. This is not a new regex engine and not a replacement for java.util.regex. It is a builder that sits in front of the JDK engine and emits a pattern string you can hand to Pattern or Matcher.

The people who get value from it are Java developers who keep writing the same kind of pattern and keep getting it wrong: URL validation, log-line parsing, extracting a field out of a fixed-shape string. A pattern like ^(?:http)(?:s)?(?:\:\/\/)(?:www\.)?(?:[^\ ]*)$ is readable to someone who writes regex daily and hostile to everyone else. The same pattern expressed as startOfLine().then("http").maybe("s").then("://").maybe("www.").anythingBut(" ").endOfLine() survives a code review, because a reviewer can read the intent without decoding escapes.

It is a poor fit for anyone who needs the full expressiveness of the JDK engine. The builder exposes the constructs the README lists, and nothing suggests it covers the entire grammar. If your pattern needs something outside that set, you are back to a raw string anyway, and now you maintain two styles in one file.

How the builder turns method calls into a regex string

Every chain starts at the static VerbalExpression.regex() factory, which returns a Builder. Each call appends a fragment to an internal buffer and returns the same builder, so the chain is a sequence of appends. Calling build() closes the builder and produces a VerbalExpression, which is the object you test against.

The README shows what the buffer looks like when it is done. The URL example, built from startOfLine, then, maybe, anythingBut and endOfLine, prints as ^(?:http)(?:s)?(?:\:\/\/)(?:www\.)?(?:[^\ ]*)$. Two details in that output are worth noticing. First, every fragment is wrapped in a non-capturing group, (?:...), so concatenating fragments cannot change how a neighbouring fragment is interpreted. Second, nothing is escaped by hand: then("://") becomes (?:\:\/\/) in the output, which is the escaping you would otherwise have to remember.

Builders can be composed rather than written as one long chain. The README shows a digits builder holding capt().digit().oneOrMore().endCapt().tab(), which is then added twice into a second builder with add(digits). That is the mechanism for reusing a fragment across patterns without copy-pasting a regex string. A builder can also be cloned, and the README's clone example reaches back into the copied builder to add a modifier with addModifier('i') before closing it.

Matching is split across three methods, and the difference matters. test() asks whether part of the string matches. testExact() asks whether the whole string matches. getText() returns the matched substring. The README demonstrates the split directly: for the pattern startOfLine().then("abc").or("def").build() against "defzzz", test() returns true while testExact() returns false, and getText() returns "def".

Installing JavaVerbalExpressions from Maven Central and running a first match

The README gives the Maven coordinates directly. The groupId is ru.lanwen.verbalregex, the artifactId is java-verbal-expressions, and the version shown is 1.8, which the release list dates to 2021-03-19. Add this to the dependencies block of your pom.xml:

xml
<dependency>
  <groupId>ru.lanwen.verbalregex</groupId>
  <artifactId>java-verbal-expressions</artifactId>
  <version>1.8</version>
</dependency>

If you want the snapshot line instead of a release, the README says to add the Sonatype snapshots repository to pom.xml, with id ossrh and url https://oss.sonatype.org/content/repositories/snapshots. That is the only other repository the README documents.

For a first real use, take the URL pattern from the README and print both the match result and the generated regex. Seeing the generated string is the fastest way to understand what the builder is doing:

java
VerbalExpression testRegex = VerbalExpression.regex()
        .startOfLine().then("http").maybe("s")
        .then("://")
        .maybe("www.").anythingBut(" ")
        .endOfLine()
        .build();

String url = "https://www.google.com";
testRegex.testExact(url); // true
testRegex.toString();     // ^(?:http)(?:s)?(?:\:\/\/)(?:www\.)?(?:[^\ ]*)$

What you should see is true from testExact, because the example URL matches the whole pattern, and the generated string printed by toString(). If you change the URL to something with a space in it, anythingBut(" ") stops matching at that point and endOfLine() fails, which is the intended behaviour of the pattern rather than a bug in the library.

Captures work through capture() and endCapture(), and the extracted group is read by index. The README's example builds a pattern from find("a"), capture(), find("b"), anything(), endCapture(), then("cd") and runs it against "aaabcd". getText(text) returns "abcd" and getText(text, 1) returns "b". The index is positional, so the order of capture() calls in the chain is what determines the number you pass.

Where the fluent builder runs out of road

The honest limitation is coverage. The README documents startOfLine, endOfLine, then, maybe, or, anything, anythingBut, find, capture, endCapture, add, addModifier, build, and the predefined character groups wordChar, nonWordChar, space, nonSpace, digit and nonDigit. If a pattern needs a construct outside that set, the library does not offer a method for it, and the README does not describe an escape hatch that lets you drop a raw fragment into the middle of a chain.

That matters for backreferences and lookaround in particular. The README never mentions them, and there is no method in the documented API that corresponds to them. A pattern that needs to match a repeated group, or to assert that something does not follow, is a case where this library is the wrong tool and a plain string handed to Pattern.compile is the right one.

A second limitation is the generated string itself. Because every fragment is wrapped in a non-capturing group, the output is longer than a hand-written pattern and harder to read in a log line or a debugger. That is the price of composability, and it is worth knowing before you print a built pattern into an error message that a user will see.

The third is project activity. The last push to the repository was on 2026-09-01, but the newest release listed is 1.8 from 2021-03-19. The gap between the two is not explained by the README, and the release notes are not available, so anyone who needs a documented fix or a new feature should check the repository directly rather than assume the release line tracks the commit line.

JavaVerbalExpressions against writing the pattern string by hand

The real alternative is not another library. It is Pattern.compile with a string literal, which is what JavaVerbalExpressions generates anyway. The difference in approach is where the readability lives. With a literal, the pattern is one dense token in the source, and the reader has to parse it in one pass. With the builder, the pattern is a sequence of named steps, and the reader can follow it line by line, but the compiled artifact is still the same kind of string and the runtime cost is the same.

That reframes the choice as a source-level one. If a pattern is short, say a simple digit check, a literal is clearer and the builder adds a dependency for nothing. If a pattern is long enough that you have had to write a comment above it explaining what each part means, the builder moves that comment into the code itself, which is the case the README's URL example is making.

There is a second alternative worth naming because the README names it: the same builder idea exists in other languages, and the README links implementations for JavaScript, PHP, Python, C#, Objective-C, Ruby, Groovy, Haskell and C++, with a pointer to VerbalExpressions.github.io for the rest. That matters for teams with services in more than one language, because the method names carry across and a pattern can be discussed once. It does not help a Java-only codebase, where the choice is still builder versus literal.

Licence, upgrade cost, and what to check before you depend on it

The repository is MIT licensed, and the LICENSE file sits at the top level alongside pom.xml, src/ and bnd.bnd. MIT is permissive: it allows use, modification and redistribution provided the copyright notice and permission notice are kept. That is a description of the licence text, not legal advice, and the bnd.bnd file at the top level suggests the artifact is also built as an OSGi bundle, which is worth confirming against your own container before you assume it.

Upgrade cost is low by construction. The dependency is a thin builder over java.util.regex, so there is no runtime service, no configuration file and no migration step. The version you pin in pom.xml is the whole surface. The relevant risk is the opposite one: the gap between the newest release, 1.8 from 2021-03-19, and the last push on 2026-09-01 means that if you hit a bug, the fix may exist in the repository without a corresponding release. Check the repository's release list before assuming a published artifact contains a change you read about in the source.

Two things are worth verifying in your own environment. First, the README documents no minimum Java version, so compile a small class against the 1.8 artifact with the JDK your project targets before rolling the dependency out. Second, the README's own examples show test() and testExact() disagreeing on the same input, so decide which one each call site needs rather than defaulting to test().

Editorial conclusion

Adopt JavaVerbalExpressions when the pattern is long enough that a raw regex string has become unreadable, or when several people have to edit the same pattern and agree on what it means. Skip it when you need lookbehind, backreferences or named groups, or when a single short pattern would be clearer written by hand. Before committing, verify two things in your own code: that test() and testExact() behave the way your call site expects, since the README shows them disagreeing on the same input, and that the Java version your build targets can consume the artifact published as 1.8 on Maven Central, because the README documents no minimum Java level.

Frequently asked questions

What is JavaVerbalExpressions?

It is a Java library that builds regular expressions through a fluent builder instead of a raw pattern string. The README describes it as a library that "helps to construct difficult regular expressions," and the builder emits a string you can pass to the JDK regex classes.

How do I install JavaVerbalExpressions in a Maven project?

Add the dependency with groupId ru.lanwen.verbalregex, artifactId java-verbal-expressions and version 1.8 to your pom.xml. The README also documents a Sonatype snapshots repository with id ossrh if you want the snapshot line.

What is the difference between test() and testExact() in JavaVerbalExpressions?

test() returns true when part of the string matches, while testExact() returns true only when the entire string matches. The README shows both on the input "defzzz" against a pattern built from startOfLine().then("abc").or("def").build(): test() is true and testExact() is false.

Can I reuse a JavaVerbalExpressions builder in more than one pattern?

Yes. The README shows a builder being added into another builder with add(digits), and it also shows a builder being cloned and then extended with addModifier('i') before build() is called.

How do I get the generated regex string from a JavaVerbalExpressions builder?

Call toString() on the built VerbalExpression. The README's URL example prints ^(?:http)(?:s)?(?:\:\/\/)(?:www\.)?(?:[^\ ]*)$, with each fragment wrapped in a non-capturing group.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. VerbalExpressions/JavaVerbalExpressions on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/verbalexpressions-javaverbalexpressions.svg)](https://hysenlabs.com/projects/verbalexpressions-javaverbalexpressions)