Invoice Field Regex Generator in Python for OCR Text
For suppliers with stable layouts, a few well tested regular expressions beat a heavy model: they are fast, explainable and easy to audit. This generator builds a Python parser for the four fields every AP team needs, with a basic total check.
Fill in
Alternatives separated by |
Patterns
import re
from datetime import datetime
CUR = re.escape("$")
INVOICE_NO = re.compile(r"(?:Invoice No|Invoice #|Inv No)\s*[:#.]?\s*([A-Z0-9][A-Z0-9\-/]{3,24})", re.I)
PO_NO = re.compile(r"(?:PO Number|Purchase Order|P.O. No)\s*[:#.]?\s*([A-Z0-9][A-Z0-9\-/]{3,24})", re.I)
DATE_TXT = re.compile(r"(?:Invoice Date|Date)\s*[:.]?\s*(\d{4}-\d{2}-\d{2}|\d{1,2}[/.-]\d{1,2}[/.-]\d{2,4})", re.I)
TOTAL = re.compile(r"(?:Total Due|Amount Due|Grand Total|Total)\s*[:.]?\s*" + CUR + r"?\s*([0-9]{1,3}(?:,[0-9]{3})*(?:\.[0-9]{2})|[0-9]+(?:\.[0-9]{2})?)", re.I)
LINE_AMT = re.compile(r"^.*?" + CUR + r"?\s*([0-9]{1,3}(?:,[0-9]{3})*\.[0-9]{2})\s*$", re.M)Parser
def first(pattern, text):
m = pattern.search(text)
return m.group(1).strip() if m else None
def parse_date(raw):
if not raw:
return None
for fmt in ("%Y-%m-%d", "%d/%m/%Y", "%d-%m-%Y", "%d.%m.%Y", "%m/%d/%Y", "%d/%m/%y"):
try:
return datetime.strptime(raw, fmt).date().isoformat()
except ValueError:
continue
return None
def parse_amount(raw):
return float(raw.replace(",", "")) if raw else None
def parse_invoice(text):
result = {
"invoice_number": first(INVOICE_NO, text),
"po_number": first(PO_NO, text),
"invoice_date": parse_date(first(DATE_TXT, text)),
"total": parse_amount(first(TOTAL, text)),
}
result["flags"] = []
for key in ("invoice_number", "invoice_date", "total"):
if result[key] is None:
result["flags"].append("missing_" + key)
return resultTotal check against line amounts
def check_total(text, parsed, tolerance=0.01):
amounts = [parse_amount(a) for a in LINE_AMT.findall(text)]
if not amounts or parsed["total"] is None:
return "cannot_check"
# the last amount on a page is usually the total, so exclude it from the sum
body = amounts[:-1]
if abs(sum(body) - parsed["total"]) <= tolerance:
return "ok"
return "review"
if __name__ == "__main__":
sample = open("invoice.txt", encoding="utf-8").read()
data = parse_invoice(sample)
data["total_check"] = check_total(sample, data)
print(data)This is a starting point for text-based extraction, not a complete product. It assumes decimal point amounts, so adapt the amount pattern and parse_amount for decimal comma locales. The total check is a heuristic that ignores tax and freight lines; replace it with line level logic that matches your invoice layouts. Test on real supplier samples before relying on it.
Outgrowing regex?
Optional: an expert can assess your invoice layouts and suggest where regex ends and a model should start.
Optional. The tool and every export work without this. No spam.
How to use the generator
List the labels your suppliers print, separated by a vertical bar, then set the currency symbol and total tolerance. Copy the script, save OCR text from your engine into invoice.txt, and run it. The output is a dictionary with the invoice number, PO number, ISO date and total, plus flags for missing mandatory fields. Extend the label lists as you meet new supplier layouts, and keep one test text file per supplier so changes do not break old ones.
Design of the patterns
Each pattern looks for a label, an optional separator such as a colon or hash, and then a value shaped like the field. Invoice and PO numbers allow letters, digits, hyphens and slashes, with a minimum length to avoid matching words. The date pattern accepts ISO dates and numeric day, month and year forms. The amount pattern accepts thousands commas and two decimals.
- Labels are case-insensitive alternatives
- Longer labels should come before shorter ones, so Total Due matches before Total
- Dates are normalized to ISO format
- Missing mandatory fields raise flags rather than silent blanks
Where regex stops working
Regex works when the label and value sit on the same line. It struggles with tables, labels above values, multi column layouts, handwritten notes and scans with heavy noise. Ambiguous dates such as 04/05/2026 cannot be resolved by pattern alone, so decide the order per supplier or country. When the number of layouts grows, move to a layout-aware extraction approach and keep regex for validation.
Common mistakes
A pattern that matches the first Total in the text may pick up a subtotal. Prefer the last match on the page, or anchor to the words Amount Due. Another mistake is skipping validation: pair every extracted value with a check such as date range, duplicate invoice detection by supplier and number, and the total comparison.
Frequently Asked Questions
Can regex extract data from any invoice?
No. It suits invoices where the label and value appear on one line in a consistent form. For varied layouts, tables and noisy scans you need layout-aware extraction, with regex kept for validating the results.
How do I handle dates like 04/05/2026?
Pattern matching cannot tell whether that is April 5 or May 4. Store the expected date order per supplier or country, and apply that order first. Reject dates that fall outside a plausible range around the receipt date.
How do I support decimal comma amounts?
Change the amount pattern to use dots for thousands and a comma for decimals, and update parse_amount to remove dots and replace the comma with a dot. Select the pattern per supplier locale rather than guessing from the text.
Why is my total picking up the subtotal?
The word Total also appears inside Subtotal. List the more specific labels first, use word boundaries, and prefer the last match in the text, since final totals usually appear at the end of the page.
If you need extraction that handles many layouts and writes validated results back to your ERP, Netray can build it as a private service on your infrastructure.
Related Tools
Invoice Extraction JSON Schema Generator
Create a JSON Schema that defines and validates the structure of extracted invoice data, including header fields, line items, confidence scores and review flags.
Quality & InspectionOCR Confusable Characters Reference for AP Validation
A lookup of look-alike characters (0 and O, 1 and l, 5 and S and more) with the fields where they cause errors and a validation rule for each.
Developer ToolsTesseract OCR Command Line Generator
Build the exact tesseract command for a single file and a PowerShell loop for a whole folder, with language, page segmentation mode, resolution and output format set from a form.
Supply Chain & InventoryInvoice Document Type Codes Reference (UNTDID 1001, X12, EDIFACT)
Look up what invoice type codes such as 380, 381 and 389 mean, and map them to the X12 transaction sets and EDIFACT messages used for purchasing, shipping, invoicing and payment.
More Developer Tools tools
See all- SAP Sales Order ABAP SELECT Generator (VBAK, VBAP, VBFA)
- SAP Sales Order Create BAPI ABAP Generator (BAPI_SALESORDER_CREATEFROMDAT2)
- SAP ABAP Open Items SELECT Generator
- SAP ACDOCA SQL Query Generator
- SAP QM Inspection Lot ABAP SELECT Generator
- SAP Quality Notification Pareto SQL Query Generator
- SAP QM Usage Decision BAPI Skeleton
- ABAP Custom Exception Class Generator
- ABAP Unit Test Class Generator