# Rule-based text extractor

Turn logs, invoices or scraped text into structured data with named regex rules. Grok-style %{PATTERN:field} shortcuts, JSON/CSV output, per-rule report.

## Run it

- **CLI:** `gizza tool rule-based-extractor "Paste the log lines, invoice text or records to extract from…" 'rules=date = %{DATE_ISO}
level = %{LOGLEVEL}
client = %{IPV4}'`
- **Web:** https://gizza.ai/tools/rule-based-extractor/
- **Agents:** machine-readable descriptor (parameters JSON Schema) at https://gizza.ai/tools/rule-based-extractor/tool.json

## Inputs

- `text` — Text _(field)_
- `rules` — Rules (one per line) _(field)_
- `split` — Split input into records by _(field)_
- `split_pattern` — Record separator regex (for custom separator) _(field)_
- `matches` — Matches per rule _(field)_
- `ignore_case` — Ignore case _(field)_
- `multiline` — Multiline (^ and $ per line) _(field)_
- `dotall` — Dot matches newline _(field)_
- `trim` — Trim captured values _(field)_
- `unique` — Drop duplicate values _(field)_
- `on_missing` — When a rule matches nothing _(field)_
- `skip_empty_records` — Skip records with no matches _(field)_
- `max_records` — Max records _(field)_
- `max_matches` — Max matches per rule, per record _(field)_
- `output` — Output _(field)_
- `pretty` — Pretty-print JSON _(field)_

## Output

- Extracted fields (text)

## Query parameters

Open the tool pre-filled and auto-run via URL:

- `text` — Text
- `rules` — Rules (one per line)
- `split` — Split input into records by
- `split_pattern` — Record separator regex (for custom separator)
- `matches` — Matches per rule
- `ignore_case` — Ignore case
- `multiline` — Multiline (^ and $ per line)
- `dotall` — Dot matches newline
- `trim` — Trim captured values
- `unique` — Drop duplicate values
- `on_missing` — When a rule matches nothing
- `skip_empty_records` — Skip records with no matches
- `max_records` — Max records
- `max_matches` — Max matches per rule, per record
- `output` — Output
- `pretty` — Pretty-print JSON

Example: `https://gizza.ai/tools/rule-based-extractor/?text=Paste%20the%20log%20lines%2C%20invoice%20text%20or%20records%20to%20extract%20from%E2%80%A6&rules=date%20%3D%20%25%7BDATE_ISO%7D%0Alevel%20%3D%20%25%7BLOGLEVEL%7D%0Aclient%20%3D%20%25%7BIPV4%7D&split=whole&split_pattern=%5E-%7B3%2C%7D%24&matches=first&ignore_case=true&multiline=true&dotall=true&trim=true&unique=true&on_missing=skip&skip_empty_records=true&max_records=5000&max_matches=1000&output=json&pretty=true`

---

## Extract structured fields with rules

Rule-based extraction is for the messy middle ground between a one-off regex and
a full parser. Paste text, write one rule per field, and get back structured
JSON, CSV, a readable listing, or a rule report. It is useful for logs, invoice
snippets, support emails, scraped pages, clipboard dumps, and any text where the
same date, code, email, IP address, ticket ID, or amount appears in a predictable
shape.

Each rule is one line. The most common form is `field = regex`, where the field
name becomes the output key and the first capture group (or the whole match if
there is no group) becomes the value. You can also write a bare regex with named
groups, or use Grok-style shortcuts such as `%{DATE_ISO:date}` and
`%{IPV4:client}` to keep patterns short. Define your own reusable macro with
`@NAME = regex`, then use `%{NAME}` or `%{NAME:field}` later in the rule block.

### Worked example

Input text:

```
Invoice INV-2026-014 dated 2026-08-15, billed to ada@example.com, total $1,240.00 (VAT 21%).
```

Rules:

```
invoice = INV-\d{4}-\d+
date = %{DATE_ISO}
email = %{EMAIL}
total = %{MONEY}
vat = %{PERCENT}
```

With **Output** set to JSON, the extractor returns:

```
{"invoice":"INV-2026-014","date":"2026-08-15","email":"ada@example.com","total":"$1,240.00","vat":"21%"}
```

For logs, switch **Split input into records by** to **Lines** so each line
becomes one output object. For every email address or every URL in a record,
switch **Matches per rule** to **Every match**; the JSON value becomes an array.

### Rule syntax

- `field = regex` extracts one named field. If the regex has a capture group,
  the first group is used; otherwise the full match is used.
- `%{PATTERN}` inserts a built-in macro as a non-capturing regex fragment.
- `%{PATTERN:field}` inserts a built-in macro as a named capture group.
- Bare regex lines must contain named captures, for example
  `%{DATE_ISO:date} %{LOGLEVEL:level} (?<message>.*)`.
- `@NAME = regex` defines a reusable macro for later lines.
- Blank lines, `# comments`, and `// comments` are ignored.

Built-ins include `WORD`, `INT`, `NUMBER`, `EMAIL`, `URL`, `IPV4`, `IPV6`,
`MAC`, `UUID`, `HASH`, `DATE_ISO`, `DATE_US`, `TIME`, `TIMESTAMP_ISO`,
`SYSLOG_TIME`, `LOGLEVEL`, `HTTP_METHOD`, `HTTP_STATUS`, `HOSTNAME`, `PATH`,
`MONEY`, `PERCENT`, `PHONE`, `SEMVER`, and `TICKET`.

### Options

- **Split input into records by** — run rules against the whole input, each
  line, each blank-line-separated paragraph, or chunks split by your own regex.
- **Matches per rule** — keep the first match per record, or collect every match
  as an array.
- **Ignore case**, **Multiline**, and **Dot matches newline** map to the common
  regex flags for case-insensitive, per-line anchors, and `.` crossing line
  breaks.
- **Trim captured values** removes surrounding whitespace from captures.
- **Drop duplicate values** deduplicates arrays when matching every occurrence.
- **When a rule matches nothing** can skip the key, keep it as `null` / empty, or
  fail fast with the field name.
- **Rule report** output shows how many records each rule hit, which is the
  fastest way to debug a pattern set before exporting JSON or CSV.

### Limits and edge cases

- Input text is capped at **1 MB**.
- At most **200 extraction rules** are accepted.
- `max_records` must be between **1 and 50,000**; `max_matches` must be between
  **1 and 10,000** per rule per record.
- Regex compilation uses Rust's regex engine: no look-around or backreferences,
  but matching is guaranteed not to blow up exponentially.
- If no rule matches anywhere, extraction fails with a hint to switch to the
  report output.
- Duplicate field names are rejected so a later rule cannot silently overwrite
  an earlier field.

## FAQ

<details>
<summary>How is this different from a normal regex tester?</summary>

A regex tester tells you whether a pattern matches. This tool turns a whole set
of named patterns into structured output. You can split text into records,
extract several fields per record, choose JSON or CSV, and use the report view to
see which rules never matched.

</details>

<details>
<summary>Can I use one rule block for many log lines?</summary>

Yes. Set **Split input into records by** to **Lines**. Each line is processed as
one record and the JSON output becomes an array of objects. Turn on **Skip records
with no matches** to ignore headers, blank lines, or unrelated lines.

</details>

<details>
<summary>What does `%{DATE_ISO:date}` mean?</summary>

`DATE_ISO` is a built-in pattern for dates such as `2026-08-15`, and `:date`
names the capture group. `%{DATE_ISO:date}` is equivalent to writing a named
regex group by hand, but it is shorter and less error-prone.

</details>

<details>
<summary>Why did my lookbehind or backreference fail?</summary>

The tool uses Rust's regex engine, which deliberately excludes look-around and
backreferences so matching remains predictable and fast in WebAssembly. Rewrite
the pattern with explicit captures, split the input into smaller records, or use
a separate parser if you need context-sensitive matching.

</details>

<details>
<summary>How do I debug a rule that is not matching?</summary>

Set **Output** to **Rule report**. The report lists every field, the rule line
that produced it, how many records it hit, and which fields never matched. That
lets you adjust one pattern without guessing from an empty JSON result.

</details>

## Related tools

- [Regex to JSON](https://gizza.ai/tools/regex-to-json/): Parse each line of text with a named-capture regex and turn it into structured JSON — group names become keys. Type coercion, NDJSON output, in your browser.
- [Regex Capture Groups to CSV](https://gizza.ai/tools/regex-capture-to-csv/): Scan text with a regex and get CSV: one row per match, named capture groups as columns. Pick the delimiter, quoting, and columns — all in your browser.
- [Extract Action Items from Meeting Notes](https://gizza.ai/tools/action-item-extractor/): Extract action items, owners, and decisions from meeting notes or daily notes with deterministic rules. Markdown checklist or JSON, private in-browser.
- [Add Line Numbers](https://gizza.ai/tools/add-line-numbers/): Add line numbers to every line of text online, like nl or cat -n — custom start, step, separator, and alignment. Free and private, runs in your browser.
- [ANSI Log Renderer](https://gizza.ai/tools/ansi-log-renderer/): Paste ANSI-colored terminal output or CI logs and render them as HTML, or strip escape codes to plain text. Handles 16-color, 256-color, and truecolor SGR codes.
