Donkey

Regex Written & Tested From Your Examples

Paste what should match and what shouldn't, and get a working regular expression, a plain-English breakdown, and ready-to-run tests in your language.

What you get: A Markdown report with the final regex in your chosen flavor, a copy-paste string literal for your language, a token-by-token explanation, a pass/fail table for every example you gave plus extra edge cases, a ready-to-run unit test snippet, performance and safety notes, and stricter or looser varian Price: $9 flat. Time: usually ready in minutes. Done by AI; you review it, with one free change.

We're not taking orders yet. Join the waitlist and we'll email you once when we open.

Real example

This is an actual result our AI made in a test run, from sample details. Yours is made from your details.

Show the example

Regex Deliverable: Invoice IDs in Email Subject Lines

1. Summary

This regex finds every invoice ID like INV-2024-00123 inside longer subject lines. It captures the 4-digit year and the 1–5 digit invoice number as separate named groups. It targets Python 3's re module, searches within text (not full-string), and uses re.IGNORECASE. It is built for re.finditer, so it returns every ID on a line.

Assumptions

  • Year: any 4 digits (0000–9999). The goal says "4-digit year," so no range check is applied. A stricter variant is in section 7.
  • Number as text: the invoice number is captured as a string, so leading zeros are kept (00042, not 42). Use int(...) afterward if you need a number.
  • No glued characters: an ID must not have a letter, digit, or underscore stuck to either end. That is why XINV-… and INV-2024-123456 are rejected. Punctuation, spaces, brackets, colons, or the start or end of the line are all fine as neighbors.
  • Exact format: no spaces are allowed around the dashes, and only plain dashes (-) are accepted.
  • ASCII digits only: the pattern uses [0-9] instead of \d, so non-ASCII digits (such as Arabic-Indic ٢٠٢٤) don't count.
  • No conflicts: the goal and the examples agree, so nothing needed to be resolved.

2. The Regex

\bINV-(?P<year>[0-9]{4})-(?P<number>[0-9]{1,5})\b

Flags: re.IGNORECASE (pass it as the second argument to re.compile). You don't need re.MULTILINE, because no ^ or $ anchors are used.

Python literal (copy-paste ready):

import re

INVOICE_RE = re.compile(
    r"\bINV-(?P<year>[0-9]{4})-(?P<number>[0-9]{1,5})\b",
    re.IGNORECASE,
)

for m in INVOICE_RE.finditer(subject):
    year, number = m.group("year"), m.group("number")
Group # / Name Captures Example value
1 / year The 4-digit year 2024
2 / number The 1–5 digit invoice number, leading zeros kept 00123
0 (whole match) The full ID, in its original case INV-2024-00123

3. How It Works

Piece Meaning
\b Word boundary. The character before INV must not be a letter, digit, or underscore. This blocks XINV-….
INV The literal letters INV. They match in any case because of re.IGNORECASE.
- A literal dash.
(?P<year>[0-9]{4}) Exactly 4 digits, saved as year.
- A literal dash.
(?P<number>[0-9]{1,5}) 1 to 5 digits, saved as number.
\b Word boundary. The next character must not be a letter, digit, or underscore. This blocks a 6th digit, so INV-2024-123456 is rejected rather than cut down to 12345.

4. Test Results

Results below are traced by hand, not executed.

Input Expected Result Captured / matched text Note
INV-2024-00123 Match ✅ INV-2024-00123 → 2024 / 00123 Basic case
inv-2023-9 Match ✅ inv-2023-9 → 2023 / 9 Case-insensitive; 1-digit number
Re: INV-2025-00042 (paid) Match ✅ INV-2025-00042 → 2025 / 00042 Surrounding text ignored
Invoices INV-2024-1 and INV-2024-22 attached 2 matches ✅ INV-2024-1 → 2024 / 1; INV-2024-22 → 2024 / 22 "Invoices" starts with "Inv" but fails at the dash
INV-24-00123 No match ✅ — Year has only 2 digits
INV2024-00123 No match ✅ — Missing dash after INV
PO-2024-00123 No match ✅ — Wrong prefix
INV-2024-123456 No match ✅ — 6 digits; trailing \b fails at every backtrack point
XINV-2024-00123 No match ✅ — No word boundary between X and I
(empty string) (added) No match ✅ — Nothing to match
INV-2024-12345 (added) Match ✅ INV-2024-12345 → 2024 / 12345 Maximum length of 5 digits
Inv-2024-7, INV-2024-8. (added) 2 matches ✅ Inv-2024-7 → 2024 / 7; INV-2024-8 → 2024 / 8 Mixed case; comma and period count as boundaries
(INV-2024-00123) (added) Match ✅ INV-2024-00123 → 2024 / 00123 Brackets count as boundaries
RE:FW:INV-2024-5 (added) Match ✅ INV-2024-5 → 2024 / 5 Colon right before the ID
INV - 2024 - 5 (added) No match ✅ — Spaces around dashes not allowed
INV-2024- (added) No match ✅ — Number missing
INV-20245-1 (added) No match ✅ — After 4 year digits, a 5 appears where - is required
INV-2024-00123A (added) No match ✅ — A letter glued to the end
INVINV-2024-1 (added) No match ✅ — The second INV has no boundary before it
INV-٢٠٢٤-1 (added) No match ✅ — Non-ASCII digits are rejected by [0-9]
INV-2024-00123_draft (added) No match ⚠️ — Trade-off: _ counts as a word character, so the ID is not found. See section 7 if this is common.
INV-2024-1-2 (added) Match ⚠️ INV-2024-1 → 2024 / 1 Trade-off: a dash counts as a boundary, so a trailing -2 is ignored rather than rejecting the whole thing

5. Ready-to-Run Tests

import re

INVOICE_RE = re.compile(
    r"\bINV-(?P<year>[0-9]{4})-(?P<number>[0-9]{1,5})\b",
    re.IGNORECASE,
)

def find_ids(text):
    return [(m.group("year"), m.group("number")) for m in INVOICE_RE.finditer(text)]

CASES = [
    # Customer positives
    ("INV-2024-00123", [("2024", "00123")]),
    ("inv-2023-9", [("2023", "9")]),
    ("Re: INV-2025-00042 (paid)", [("2025", "00042")]),
    ("Invoices INV-2024-1 and INV-2024-22 attached", [("2024", "1"), ("2024", "22")]),
    # Customer negatives
    ("INV-24-00123", []),
    ("INV2024-00123", []),
    ("PO-2024-00123", []),
    ("INV-2024-123456", []),
    ("XINV-2024-00123", []),
    # Added edge cases
    ("", []),
    ("INV-2024-12345", [("2024", "12345")]),
    ("Inv-2024-7, INV-2024-8.", [("2024", "7"), ("2024", "8")]),
    ("(INV-2024-00123)", [("2024", "00123")]),
    ("RE:FW:INV-2024-5", [("2024", "5")]),
    ("INV - 2024 - 5", []),
    ("INV-2024-", []),
    ("INV-20245-1", []),
    ("INV-202

Questions

How does the regex written & tested from your examples work?

You answer a few questions, pay $9, and an AI worker does the job. The result appears on your private page, usually within minutes.

Can I get changes?

Yes. Every job includes one free change request: tell the donkey what to fix and it redoes the job with your notes.

Is it really done by AI?

Yes, fully. No human writes it. AI can make mistakes, so please review the result before you rely on it.

What happens to my information?

It is used only to do your job, is never used to train AI models, and is deleted after 30 days.

What if it fails?

Failed jobs are retried once, then refunded automatically.

Need something different?

Can't find it? Describe anything.

Our AI checks it's something we can do safely, then shows you exactly what you'll get and a flat price. Nothing is charged until you order.

More in Tech & code