> ## Documentation Index
> Fetch the complete documentation index at: https://bettertickets.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Regex

> How regular expressions work: literals, character classes, quantifiers, anchors, groups and flags, with an interactive playground to test a pattern live.

export const RegexPlayground = () => {
  const [pattern, setPattern] = useState("free nitro|discord\\.gg\\/\\w+");
  const [flags, setFlags] = useState("i");
  const [testString, setTestString] = useState("Hey, check out this free nitro giveaway: discord.gg/abc123");
  const inputClass = "w-full font-mono text-sm px-3 py-2 rounded-lg border dark:border-zinc-950/80 border-zinc-950/10 bg-zinc-950/2 dark:bg-white/5 text-zinc-950 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary/40";
  const labelClass = "block text-sm font-medium text-zinc-950/70 dark:text-white/70 mb-1";
  const captionClass = "mt-1.5 text-xs text-zinc-950/50 dark:text-white/50";
  let error = null;
  let matched = false;
  let parts = [testString];
  if (pattern) {
    try {
      matched = new RegExp(pattern, flags).test(testString);
      const globalFlags = flags.includes("g") ? flags : `${flags}g`;
      const highlightRegex = new RegExp(pattern, globalFlags);
      parts = [];
      let lastIndex = 0;
      let match = highlightRegex.exec(testString);
      let guard = 0;
      while (match !== null && guard < 1000) {
        guard += 1;
        if (match[0].length === 0) {
          highlightRegex.lastIndex += 1;
          match = highlightRegex.exec(testString);
          continue;
        }
        parts.push(testString.slice(lastIndex, match.index));
        parts.push({
          text: match[0]
        });
        lastIndex = match.index + match[0].length;
        match = highlightRegex.exec(testString);
      }
      parts.push(testString.slice(lastIndex));
    } catch (e) {
      error = e.message;
    }
  }
  return <div className="p-5 border dark:border-zinc-950/80 border-zinc-950/10 rounded-2xl shadow-sm bg-zinc-950/[0.015] dark:bg-white/[0.02] not-prose space-y-5">
      <div className="flex flex-wrap gap-3 items-start">
        <div className="flex-1 min-w-[220px]">
          <label className={labelClass} htmlFor="regex-pattern">
            Pattern
          </label>
          <input id="regex-pattern" className={inputClass} value={pattern} onChange={e => setPattern(e.target.value)} placeholder="e.g. free nitro|discord\.gg\/\w+" spellCheck={false} />
        </div>
        <div className="w-36">
          <label className={labelClass} htmlFor="regex-flags">
            Flags
          </label>
          <input id="regex-flags" className={inputClass} value={flags} onChange={e => setFlags(e.target.value.replace(/[^dgimsuvy]/g, ""))} placeholder="i" spellCheck={false} />
          <p className={captionClass}>e.g. "i" (case-insensitive)</p>
        </div>
      </div>

      <div>
        <label className={labelClass} htmlFor="regex-test-string">
          Test message
        </label>
        <textarea id="regex-test-string" rows={2} className={`${inputClass} resize-y`} value={testString} onChange={e => setTestString(e.target.value)} spellCheck={false} />
      </div>

      <div className="pt-1 border-t dark:border-zinc-950/80 border-zinc-950/10 space-y-2">
        <div className="flex items-center justify-between pt-4">
          <label className={`${labelClass} mb-0`}>Result</label>
          <span className={matched ? "inline-block text-xs font-semibold px-2.5 py-1 rounded-full bg-green-500/15 text-green-700 dark:text-green-400" : "inline-block text-xs font-semibold px-2.5 py-1 rounded-full bg-zinc-950/10 dark:bg-white/10 text-zinc-950/70 dark:text-white/70"}>
            {matched ? "Match found" : "No match"}
          </span>
        </div>

        {error ? <p className="text-sm text-red-600 dark:text-red-400">
            Invalid pattern: {error}
          </p> : <>
            <div className={`${inputClass} whitespace-pre-wrap break-words bg-zinc-950/2 dark:bg-white/2`}>
              {parts.map((part, i) => typeof part === "string" ? <span key={i}>{part}</span> : <mark key={i} className="rounded bg-yellow-300/60 dark:bg-yellow-400/40 text-inherit px-0.5">
                    {part.text}
                  </mark>)}
            </div>
            <p className={captionClass}>
              Highlighted text shows what the pattern matched.
            </p>
          </>}
      </div>
    </div>;
};

A regular expression (regex) is a pattern that describes text to match. Instead of searching for one exact string, a regex describes a shape: "a word boundary, then some letters, then a boundary", "any digit repeated three times", "one of these three words". Anywhere you're asked for a regex pattern, it's evaluated with JavaScript's regex engine, the same one described here.

## How it works

A pattern is built from a small set of building blocks, combined together.

### Literal characters

Most characters just match themselves. The pattern `cat` matches the text `cat` wherever it appears.

### Character classes

A character class matches one character out of a set.

| Syntax           | Matches                               |
| ---------------- | ------------------------------------- |
| `[abc]`          | `a`, `b`, or `c`                      |
| `[^abc]`         | Any character except `a`, `b`, or `c` |
| `[a-z]`          | Any lowercase letter                  |
| `\d`             | Any digit (`[0-9]`)                   |
| `\w`             | Any word character (`[A-Za-z0-9_]`)   |
| `\s`             | Any whitespace character              |
| `\D`, `\W`, `\S` | The negation of `\d`, `\w`, `\s`      |
| `.`              | Any character except a newline        |

### Quantifiers

A quantifier says how many times the thing before it can repeat.

| Syntax  | Meaning                |
| ------- | ---------------------- |
| `*`     | Zero or more           |
| `+`     | One or more            |
| `?`     | Zero or one (optional) |
| `{3}`   | Exactly 3              |
| `{2,4}` | Between 2 and 4        |
| `{2,}`  | 2 or more              |

Quantifiers are greedy by default: they match as much as possible before backing off. Adding `?` after a quantifier (`*?`, `+?`) makes it lazy instead, matching as little as possible.

### Anchors and boundaries

| Syntax | Meaning                                                                     |
| ------ | --------------------------------------------------------------------------- |
| `^`    | Start of the string (or line, with the `m` flag)                            |
| `$`    | End of the string (or line, with the `m` flag)                              |
| `\b`   | A word boundary: the edge between a word character and a non-word character |
| `\B`   | Not a word boundary                                                         |

`\bcat\b` matches `cat` as its own word, not the `cat` inside `catalog`.

### Groups and alternation

Parentheses `()` group part of a pattern together, so a quantifier can apply to the whole group and so the matched text can be extracted separately. The pipe `|` inside or around a group means "or": `cat|dog` matches either `cat` or `dog`.

### Escaping

Characters like `. * + ? ( ) [ ] { } ^ $ |` are special to regex. To match one of them literally, put a backslash in front of it: `\.` matches a literal period, `\(` matches a literal opening parenthesis.

## Flags

Flags go after the pattern and change how the whole match runs.

| Flag | Name        | Effect                                                                 |
| ---- | ----------- | ---------------------------------------------------------------------- |
| `d`  | indices     | Adds match indices to results                                          |
| `g`  | global      | Finds all matches instead of stopping at the first                     |
| `i`  | ignoreCase  | Case-insensitive matching                                              |
| `m`  | multiline   | `^`/`$` match at the start/end of each line, not just the whole string |
| `s`  | dotAll      | `.` also matches newlines                                              |
| `u`  | unicode     | Treats the pattern as a sequence of Unicode code points                |
| `v`  | unicodeSets | Newer, stricter version of `u`                                         |
| `y`  | sticky      | Matches only starting at the current position                          |

## Playground

Edit the pattern, flags and test message below. Matches are highlighted live.

<RegexPlayground />

## Examples

<CodeGroup>
  ```txt Alternation theme={null}
  cat|dog|bird
  ```

  ```txt Word boundary theme={null}
  \bword\b
  ```

  ```txt Digits only, start to end theme={null}
  ^\d+$
  ```

  ```txt Email-shaped text theme={null}
  [\w.+-]+@[\w-]+\.[a-zA-Z]{2,}
  ```

  ```txt A link theme={null}
  https?:\/\/\S+
  ```
</CodeGroup>

<Warning>
  Some patterns can run very slowly on certain input, particularly nested repetition like `(a+)+b`. If a pattern needs to run against arbitrary user-supplied text, test it against a long, unusual input in the playground above before relying on it.
</Warning>


## Related topics

- [Jail](/docs/tickets/jail.md)
