How to Write Regular Expressions with AI—and Test Them

How to Write Regular Expressions with AI—and Test Them

Olivia Park
September 4, 2026· 11 min read

To write regular expressions with AI, name the engine and contract, then test it. Follow How to Use AI; the real engine decides whether it works.

Key Takeaways

  • Name the runtime, engine, version, API, flags, and full-match or search semantics.
  • Translate the requirement into an allowlist-style contract before writing syntax.
  • Test accepted, rejected, boundary, Unicode, newline, and adversarial input.
  • Keep regex syntax, programming-language string escaping, and shell escaping separate.
  • Verify captures and replacements, not only a true/false match.
  • Test performance with bounded input and watch for catastrophic backtracking.
  • Prefer a parser or ordinary string operations when regex becomes opaque.

Define the contract before you write regular expressions with AI

A regex cannot repair an ambiguous requirement. Write what is allowed, what is forbidden, whether the entire value must match, and what later code will do with captures.

For a synthetic ticket key, the contract might be:

FieldDecision
Accepted shapeTwo uppercase ASCII letters, hyphen, four digits
Whole value?Yes; no leading or trailing text
LengthExactly 7 characters
Accepted examplesAB-0042, ZX-9000
Rejected examplesab-0042, AB 0042, AB-42, XAB-0042Y
CapturesPrefix and numeric part
Input originUntrusted form field
Maximum input length64 characters before matching

OWASP recommends defining allowed characters and minimum and maximum lengths as part of input validation.[4] Prefer a narrow allowlist when the domain permits it. Do not ask for “a perfect regex for any identifier”; name the actual grammar and policy owner.

Keep validation and extraction distinct. A pattern that finds a ticket inside a paragraph should not automatically validate a complete form value. Likewise, client-side convenience validation does not replace server-side enforcement.

Which engine and call site will actually run the pattern?

Regex syntax and behavior differ across JavaScript, Python, Java, .NET, Rust crates, database functions, command-line tools, and editors. Even shared syntax may use different APIs or defaults.

Record:

FieldExample
Language/runtimePython 3.14
Engine/libraryStandard re module
APIre.fullmatch
Pattern representationRaw string literal
FlagsNone
Unicode policyASCII letters only by contract
CapturesNamed prefix and number
Replacement behaviorNot used

MDN documents that JavaScript flags change behaviors such as global search, case-insensitive matching, multiline anchors, dot matching newlines, and Unicode handling.[1] Python's re module has its own syntax, flags, matching functions, and replacement backreferences.[2] “It worked in an online tester” is not useful evidence unless the tester uses the same engine and settings.

The choice among full match, prefix match, and search is part of the contract. In JavaScript, explicit ^ and $ anchors are often used for full-value validation, while in Python fullmatch expresses that intent at the API level. Check newline and multiline semantics rather than assuming anchors mean the same thing everywhere.

Build regex test cases before the pattern

Create cases that a reviewer can approve without reading regex syntax. Start with examples from the requirement, then add boundaries and near misses.

CaseInputExpectedReason
P-01AB-0042Match; AB, 0042Typical valid value
P-02ZX-9000Match; ZX, 9000Different valid values
N-01ab-0042No matchLowercase forbidden
N-02AB 0042No matchWrong separator
N-03AB-42No matchToo few digits
N-04XAB-0042YNo matchExtra surrounding text
B-01Empty stringNo matchMinimum boundary
B-0264-character nonmatchNo match within budgetMaximum accepted input size
A-01Long repeated prefix plus !No match within budgetBacktracking challenge

Do not let AI create both the rule and every expected result without review. Derive the oracle from the product requirement, protocol, standard, or policy. The test-generation workflow can broaden the matrix, but the owner approves the labels.

Add cases for empty input, one character below and above each limit, repeated delimiters, embedded newlines, confusable Unicode, leading and trailing whitespace, null handling at the call site, and extremely long rejected values. When captures feed another operation, state their expected values explicitly.

Ask for a pattern and an explanation

Give the model the contract and test table, not private production samples.

Target: Python 3.14 standard re, using re.fullmatch and a raw string.
Contract: exactly two uppercase ASCII letters, a literal hyphen, four digits.
Return one pattern with named groups prefix and number.
Explain every token and list assumptions. Do not broaden accepted characters.
Do not invent extra requirements. Include runnable tests for every supplied case.
Flag any engine or escaping uncertainty as [VERIFY].

A reasonable candidate is:

pattern = r"(?P<prefix>[A-Z]{2})-(?P<number>[0-9]{4})"

Because the call site uses re.fullmatch, explicit anchors are unnecessary for this exact example. That statement is engine- and API-specific, not a universal style rule. Ask why every group, quantifier, class, alternation, lookaround, and flag exists. Remove syntax that has no contract purpose.

Which three escaping layers must stay separate?

Many broken patterns are correct regex wrapped in the wrong string. Track these layers separately:

  1. Regex grammar: a digit class, literal dot, group, or backreference.
  2. Language string literal: how the source language represents backslashes and quotes.
  3. Transport or shell: how JSON, YAML, a command line, or another template encodes the string.

For Python, a raw string often makes a regex easier to read, but it still follows Python raw-string rules. In JavaScript, a regex literal and new RegExp("...") have different string-escaping needs. In a replacement string, backreferences may use yet another syntax; Python documents named and numbered references such as \g<name> and \g<number>.[2]

Store the exact source literal, the runtime pattern value if inspectable, and the call that executes it. Test a literal backslash, quote, newline, and replacement capture where relevant. Do not “fix” escaping by trial and error without explaining which layer changed.

How do you test AI-generated regex in the target runtime?

Run the corpus in the real project test framework. A small synthetic Python example is:

import re

PATTERN = re.compile(r"(?P<prefix>[A-Z]{2})-(?P<number>[0-9]{4})")

def parse_ticket(value: str):
    if len(value) > 64:
        return None
    return PATTERN.fullmatch(value)

assert parse_ticket("AB-0042").groupdict() == {
    "prefix": "AB",
    "number": "0042",
}
assert parse_ticket("ab-0042") is None
assert parse_ticket("XAB-0042Y") is None

The length check bounds work before matching; its exact limit comes from the synthetic contract. In a real application, validate types and nulls before calling the engine. Run server-side tests even if a browser also checks input.

If the regex transforms text, assert the entire replacement output and unmatched portions. If it runs repeatedly with a global or stateful API, test iteration and empty matches. If it processes multiple lines, cover both line endings and the chosen multiline behavior.

Check Unicode, flags, captures, and replacement

Character classes can hide policy decisions. Does \d mean ASCII digits or a wider Unicode category in your engine and mode? Does case-insensitive matching fold characters beyond A-Z? Does . include a newline? Does a global flag preserve state between calls?

Build a review table:

DimensionQuestionEvidence
UnicodeWhich scripts and normalization forms are valid?Approved corpus
WhitespaceAre spaces, tabs, and newlines distinct?Boundary tests
FlagsWhy is each flag required?Runtime configuration
CapturesAre names and optional values stable?Exact assertions
ReplacementHow are missing groups handled?Output fixtures
IterationCan an empty match stall a loop?Loop test

Never silently use ASCII-only syntax for international names, or Unicode-wide syntax for a protocol that requires ASCII. The product or protocol owner decides; the model implements the recorded policy.

How do you test regex ReDoS risk and performance?

Some backtracking engines can take dramatically longer on crafted input. OWASP highlights risky structures such as repetition inside repeated groups or overlapping alternation and gives examples including (a+)+$.[3] This is why a regex test suite needs adversarial nonmatches, not only normal examples.

Review for nested ambiguous quantifiers, overlapping alternatives, backreferences over uncontrolled text, and user-supplied pattern fragments. Then:

  1. Bound input length before matching.
  2. Use a safer equivalent or a non-backtracking engine where appropriate.
  3. Benchmark representative and adversarial failures in a controlled process.
  4. Set a timeout or resource boundary outside the regex when the runtime supports a safe mechanism.
  5. Record engine, hardware class, input generator, repetitions, and threshold.

Do not publish a universal “safe in 10 ms” number. The budget belongs to the application and environment. A model's assurance that a pattern is linear is not a measurement or proof.

Never concatenate raw user input into a regex. If the intent is literal search, use the engine's escaping function and still apply input bounds. If users are intentionally allowed to supply patterns, treat that as a separate high-risk feature with engine isolation, resource limits, and security review.

Know when not to use regex

Choose ordinary string operations, a parser, or a domain library when the grammar is nested, context-sensitive, or maintained by a standard. HTML, programming languages, complex addresses, and evolving business rules often exceed what a readable pattern should own.

Stop when:

  • reviewers cannot explain the pattern token by token;
  • variants require overlapping nested alternation;
  • the engine lacks required Unicode or timeout behavior;
  • captures form an unstable hidden API;
  • the requirement needs normalization or semantic checks after syntax;
  • a maintained parser already implements the standard;
  • the test corpus cannot describe accepted and rejected input clearly.

If the pattern fails, use the AI debugging process to reduce a concrete mismatch. For generated code around it, apply the pre-run review checklist.

Summary checklist

  • The language, engine, version, API, flags, and representation are recorded.
  • The matching contract states character set, length, anchoring, captures, and use.
  • Positive, negative, boundary, Unicode, newline, and adversarial cases exist.
  • Expected results come from an owner or standard, not the model alone.
  • Regex, string, JSON/YAML, and shell escaping are not conflated.
  • The candidate compiles and all cases run in the target environment.
  • Captures, replacements, repeated execution, and empty matches are checked.
  • Input length is bounded and ReDoS risk is reviewed.
  • The pattern is readable enough for another maintainer to explain.
  • A parser is used when regex is the wrong abstraction.

FAQ

Can I write regular expressions with AI from one example?

One example cannot define all accepted and rejected input. Provide a written contract plus positive, negative, boundary, and adversarial cases, then execute them in the target engine.

Why can a pattern fail when I write regular expressions with AI?

The tester may use a different engine, flags, match API, or escaping layer. Reproduce the exact runtime pattern and call site in your project test.

Should I use anchors or a full-match API?

Use the idiom that accurately expresses whole-value matching in your target engine. Test embedded newlines and multiline flags because anchor behavior can vary.

What is the difference between regex escaping and string escaping?

Regex escaping defines pattern syntax; string escaping defines how source code stores that pattern. JSON, YAML, templates, and shells can add further encoding layers.

Is \d always the same as [0-9]?

No. Depending on the engine and mode, \d may match a wider Unicode digit set. Choose the class that implements the recorded input policy and test representative characters.

How do I check ReDoS when I write regular expressions with AI?

Review its structure, bound input, add long crafted nonmatches, and measure it in the real engine under controlled resource limits. Do not rely on the model's complexity claim alone.

Can I put user input inside a regex?

Do not concatenate it as pattern syntax. Use a literal-escaping function for literal search, or design an isolated, resource-bounded feature if user-defined patterns are truly required.

When should I use a parser instead of trying to write regular expressions with AI?

Use a parser when data is nested, context-sensitive, standardized, or hard to explain with a small pattern. Readability and maintainability are part of correctness.

Sources

  1. MDN Web Docs, “Regular expressions” — https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions
  2. Python documentation, “re — Regular expression operations” — https://docs.python.org/3/library/re.html
  3. OWASP Foundation, “Regular expression Denial of Service — ReDoS” — https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
  4. OWASP Cheat Sheet Series, “Input Validation” — https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html

Sources checked 4 September 2026.

Related articles

Start your 3-day free trial

Sign up to experience all premium features at no cost.

*Available only to new users. Each user is limited to one trial.

How to Write Regular Expressions with AI—and Test Them | AethoVPN