English

Text & everyday tools · Text Toolkit

Why ^ and $ match only once: regex flags in browser find and replace

· How it works

regular-expressions find-and-replace text-cleanup

A multiline list showing one whole-text anchor and repeated explicit newline boundaries
Original ToolAcre vector illustration

Explains the JavaScript regex flags — global, multiline and dotAll — and why a find-and-replace box compiled without multiline anchors ^ and $ to the whole text, with patterns that work instead.

The pattern that only fixed the first line — what happens when you expect ^ to mean 'start of each line'

A pasted list may contain the same unwanted prefix on every row, yet searching for `^prefix` removes it only from the first row. The surprising result comes from the pattern configuration, not from inconsistent data. In ToolAcre, `^` identifies the start of the complete text because the regular expression is compiled without multiline mode.

The matching `$` anchor follows the same rule at the other edge: it identifies the end of the complete text, not every line ending inside it. A line break remains part of the input, but it does not become another anchor position. Check the reported replacement count before importing the cleaned list; a count of one exposes the mismatch immediately.

The g flag — why 'replace every match in one pass' depends on the global flag being set

ToolAcre always constructs its search pattern with the global `g` flag. That choice tells JavaScript to collect every non-overlapping match rather than stop after the first one. The tool first calls `match` to determine the count, then passes the same global pattern to `replace`, so the displayed number and replacement pass use one matching rule.

Global matching cannot create positions that the pattern does not recognize. With `^prefix`, there is only one eligible whole-text start, so `g` still finds one match. With a pattern that can occur on several rows, `g` allows all occurrences to be replaced. Separate these questions when debugging: anchors decide where a match may begin, while global mode decides whether searching continues.

The m flag — how multiline changes ^ and $ from whole-text anchors to line anchors, and why this tool leaves it off

The multiline `m` flag changes how `^` and `$` are interpreted, allowing them to recognize positions around line terminators as well as the outer text boundaries. ToolAcre does not add that flag. Its compiler uses `g` for case-sensitive searches and `gi` when case sensitivity is cleared, with no user control for additional flags.

Leaving multiline unavailable keeps the interface small, but it means patterns copied from an editor configured with `gm` may behave differently here. Do not assume that a familiar expression carries its flags with it. JavaScript flag letters are supplied when the RegExp is created, and this tool rejects unsupported inline flag syntax rather than silently enabling another mode.

The s flag — why a dot does not match a newline by default, and how to match across lines without it

A dot in this tool does not match a line break because the compiler also omits the dotAll `s` flag. A pattern such as `BEGIN.*END` can match when both markers are on one line, but it stops at the first line break when the markers span several rows. Adding global mode does not alter what the dot itself can consume.

When a cross-line match is genuinely required, write the permitted characters explicitly. A class such as `[\s\S]*?` can span whitespace and non-whitespace while remaining reluctant, although broad patterns deserve testing on a small sample first. The compiler catches invalid syntax, but it does not protect the tab from an expensive expression with severe backtracking behavior.

Matching newlines explicitly — using \n in the pattern to target line starts and ends when multiline is unavailable

For repeated line starts without multiline mode, match either the beginning of the text or a newline: `(^|\n)prefix`. The first alternative handles the first row, and the second handles later rows by consuming their preceding newline. Parentheses capture whichever boundary matched, giving the replacement a way to retain the structure that separated the lines.

This technique assumes line-feed separators in the pattern. The text library recognizes CRLF, LF, and lone CR when performing dedicated line operations, but find and replace searches the original string directly. If pasted content uses another line-ending form, normalize it first or adapt the expression deliberately; otherwise later rows may remain untouched even though they look identical on screen.

Match line boundaries explicitly with (^|\n) and preserve the captured separator

Suppose the input is `ID: apple`, `ID: pear`, and `ID: plum` on separate lines. Enable Regex, find `(^|\n)ID: `, and replace with `$1`. On the first row, `$1` is the empty start position; on later rows, it is the captured newline. The labels disappear while all three row boundaries remain in place.

Read the replacement count before accepting the result. Three rows should produce three replacements in this example. If the count is one, inspect the actual pattern and line endings rather than repeating the operation. Undo is available after a replacement, so a small trial can confirm both matching and reconstruction before the same expression touches a longer import list.

What this does not cover — the u, i and y flags, and inserting newlines into the replacement, which a single-line field cannot do

The implementation also uses `i` when case sensitivity is turned off, but it exposes no controls for `u`, `y`, `m`, or `s`. This article does not assign behavior to those unavailable modes beyond explaining the missing multiline and dotAll effects needed for the example. Patterns from another JavaScript environment must be reviewed against the flags ToolAcre actually compiles.

The source proves that replacement text is passed to JavaScript `String.replace`, so substitutions such as `$1`, `$&`, and `$$` retain their JavaScript meanings. It does not establish every interaction offered by the rendered field from the files used here. In particular, inserting physical newlines through the interface should be tested rather than inferred from the outline alone.

Other flags and replacement-field behavior are outside this tool implementation

Treat flags as part of a regular expression, even when an interface displays only the pattern body. In ToolAcre, every search is global, optional case-insensitive mode adds `i`, and multiline and dotAll are absent. Those facts explain why replacement continues across ordinary matches while `^`, `$`, and dot retain their default whole-text and single-line constraints.

For a prefixed pasted list, `(^|\n)prefix` with `$1` is the practical workaround because it names the boundary and preserves it. Start with representative rows, confirm the count, inspect the resulting line structure, and only then process the full dataset. That procedure turns a puzzling one-match result into a reviewable cleanup step before import.