Regular expressions
A regular expression (regex) is a short pattern that describes text instead of spelling it out: "the word Harry, but not Harrys", "any two words in quotes", "three or more spaces in a row". Marginalia uses them in two places:
-
Search and replace of the story and of a lorebook, and the matching commands, when Search by regex is on. The pattern finds the text, the replacement can reuse pieces of it.
-
The Regex filter of a lorebook entry, which decides when the entry goes into the prompt.
The dialect is the one of Java. This page shows what you will use most; the complete reference is linked at the end.
You do not need regexes for plain words. With Search by regex off the text is searched exactly as typed, so a
. or a ( in it is just a dot or a bracket. Turn the regex on only when a single fixed text is not enough.
The building blocks
Brackets make a group, and a vertical bar inside a group is "or": (Mr|Mrs) Dursley finds Mr Dursley and
Mrs Dursley, and \b(Voldemort|You-Know-Who)\b finds either name. A ?, * or + after a group applies to all of
it: (very )+good finds very good and very very good.
The characters . ? * + ^ $ | \ ( ) [ ] { } have a meaning of their own. To search for one of them as itself,
put a \ in front of it: to find Really?! write Really\?!.
By default a regex ignores nothing: harry does not find Harry. Put (?i) at the start to ignore the case:
(?i)harry finds Harry, HARRY and harry. (The Search and replace windows lock Case sensitive while the regex
is on, so this is how you switch it off there; the lorebook Regex filter always ignores the case.)
A match of no length at all (a pattern like a* that can match nothing) is ignored by Search and replace.
Typical uses in a story
Each row is something you may want to do to a book. Search is what you type into Search for, Replace what you type into Replace with.
To make several spellings one name, search \b(Voldemort|You-Know-Who|He-Who-Must-Not-Be-Named)\b and replace it with
Tom Riddle.
Taking the match into the replacement
Everything a regex puts in round brackets is a group, numbered from the left by its opening bracket: the replacement
$1 is the text of the first group, $2 of the second, $0 all of the match. In (\w+), (\w+) the first group is the
word before the comma, the second the word after it, so $2 $1 swaps them: Potter, Harry becomes Harry Potter.
You can name a group, (?<last>\w+), (?<first>\w+), and use the name in the replacement: ${first} ${last}.
A $ or a \ that should stay as itself in the replacement has to be escaped: to put $5 into the text, write \$5.
Without the regex the replacement is always taken exactly as typed.
If the pattern is wrong (a bracket that is never closed) or the replacement names a group the pattern does not have, a notification says so and nothing happens; a search never changes anything.
Match only in a context
A lookahead (?=...) and a lookbehind (?<=...) look at the text around the match without taking it, so only the
part outside them is replaced:
Lazy and greedy
* and + take as much as they can: in He said "yes" and "no" the pattern ".*" matches the whole "yes" and "no".
Add a ? (".*?"), or say what is not allowed ("[^"]*"), to stop at the first closing quote.
The search treats every line on its own: . does not cross a line break, and ^ and $ match at the start and the end
of every line of a part, not only of the whole text. To match a line break write \n.
In a lorebook entry filter
The filter of a lorebook entry set to Regex is matched against the request for the part, anywhere in it. It ignores
the case, and . does match line breaks there, so varro.*harbor finds Varro and the harbor even on different lines.
Typical filters:
\bvarro\b- Varro as a whole word.\b(varro|captain)\b- Varro or the captain.varro.*(harbor|docks)- Varro and then, later, the harbor or the docks.dragons?- dragon or dragons.(?<!no )magic- magic, unless it stands right after no.
An invalid regex never activates the entry, and the reason is written to the log. See entries and activation for the rest of how an entry is chosen.
In the commands
/search-replace-parts and /search-replace-lore-entries take
the regex as the third argument (true), after the search and the replacement. The command line splits on spaces, so a
pattern or a replacement with a space is put in double quotes. A backslash in front of a letter is passed on as it is
(\b, \d), but \\ becomes a single backslash and \" a quote, so a literal backslash in the pattern is \\\\.
/search-replace-parts "\bHarry\b" Henry true
/search-replace-parts "(\w+), (\w+)" "$2 $1" true
/search-replace-parts "(?i)\bharry\b" Henry true
The commands show no list of matches: they ask you whether to replace the number of matches found. When you are not sure about a pattern, try it first in the Search and replace window, which shows every match as it is and as it will be.
Tips
-
Try a pattern with Search first and open a few of the rows: the window shows each match with its surroundings, as it is and as it will be. Replace only when the result is what you wanted.
-
Start simple and add to the pattern step by step; a pattern that finds too much usually needs
\baround a word, or?to stop a*. -
A backup before a big replace is cheap (
/take-backup), and a replace cannot be stopped once it has started. -
Changed summaries: when the replace touches the text that a summary was made from, the window warns that the summary would become outdated; see Search and replace.
Further reading
This page covers the basics. Java's full syntax, with every construct, flag and character class, is in the
documentation of java.util.regex.Pattern,
and the replacement syntax ($1, ${name}, \$) in
Matcher.appendReplacement.