Skip to content

Fix regex replace losing whole-match/group backreferences and control-char escapes - #1154

Open
Faq wants to merge 1 commit into
dail8859:masterfrom
Faq:issue_1100
Open

Faq wants to merge 1 commit into
dail8859:masterfrom
Faq:issue_1100

Conversation

@Faq

@Faq Faq commented Sep 26, 2026

Copy link
Copy Markdown

QRegexSearch::SubstituteByPosition() built the replacement by re-running the find pattern against just the matched substring in isolation:

QString newString = match.captured();
newString.replace(match.regularExpression(), QByteArray(text, *length));

This broke in two ways. First, \r, \n, \t and \ in the replacement text were never translated to real characters, so e.g. replacing "Example" with "\0\r\n" produced the literal text "\0\r\n" instead of the matched text followed by a real line break. Second, re-matching the pattern against the isolated substring silently gave wrong results for anything relying on context outside the match itself, such as lookahead/lookbehind or anchors.

Rewrite it to build the replacement directly from the already-computed match's captured groups (\0 for the whole match, \1-\9 for capture groups, matching Notepad++'s own replace syntax) plus the same \n \r \t \ escapes the "Extended" search mode already supports, so regex replace has them too.

Add a QTest suite (tests/tst_QRegexSearch.cpp) covering whole-match substitution, reordered capture groups, the literal-text case, and a lookbehind pattern that the old implementation got wrong. Wire tests/ into CMake and run them via ctest in CI on every push.

Closes #1100

…-char escapes

QRegexSearch::SubstituteByPosition() built the replacement by re-running the
find pattern against just the matched substring in isolation:

    QString newString = match.captured();
    newString.replace(match.regularExpression(), QByteArray(text, *length));

This broke in two ways. First, \r, \n, \t and \\ in the replacement text
were never translated to real characters, so e.g. replacing "Example" with
"\0\r\n" produced the literal text "\0\r\n" instead of the matched text
followed by a real line break. Second, re-matching the pattern against the
isolated substring silently gave wrong results for anything relying on
context outside the match itself, such as lookahead/lookbehind or anchors.

Rewrite it to build the replacement directly from the already-computed
match's captured groups (\0 for the whole match, \1-\9 for capture groups,
matching Notepad++'s own replace syntax) plus the same \n \r \t \\ escapes
the "Extended" search mode already supports, so regex replace has them too.

Add a QTest suite (tests/tst_QRegexSearch.cpp) covering whole-match
substitution, reordered capture groups, the literal-text case, and a
lookbehind pattern that the old implementation got wrong. Wire tests/ into
CMake and run them via ctest in CI on every push.

Closes dail8859#1100
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regex problem: "Replace" does not work correctly

1 participant