C#与JavaScript中正则表达式匹配行尾/换行的差异及BBCode解析器迁移问题咨询
Great question! The difference you're seeing comes down to two key factors: how each regex engine handles the $ anchor, and the left-to-right priority of regex branches. Let's break this down clearly.
Why the Behavior Differs
1. The $ Anchor Has Different Default Behavior
- JavaScript: The
$anchor strictly matches the absolute end of the input string. For your input"line1\n", the string ends with a newline character (\n). The pattern^.*$tries to match from the start (^) to the absolute end ($), but since.doesn't match newlines by default,.*can only get as far as"line1"—and since that's not the absolute end of the string (there's still the\n), the^.*$branch fails. - C#: By default, the
$anchor matches either the absolute end of the string or the position just before a trailing newline character. So for"line1\n",$happily matches right after"line1"(before the\n), making the^.*$branch succeed. Since regex branches are tried left-to-right, the engine stops here and doesn't check the second branch.
2. Branch Matching Priority
Both engines evaluate regex branches from left to right: the first branch that fully matches the input gets selected. In JavaScript, the first branch fails, so it falls back to the newline-preserving second branch. In C#, the first branch succeeds immediately, so the newline is never captured.
How to Make C# Regex Behave Like JavaScript
You have a few solid options to align the behavior:
Option 1: Reorder the Branches (Your Known Fix)
Swap the branches to put the newline-matching pattern first:
var firstLinePattern = new Regex(@"(^.*\r?\n|^.*$)");
This prioritizes matching lines with trailing newlines first. Only if that fails (e.g., a final line without a newline) does it fall back to the ^.*$ branch. This works because we ensure the exact match you want for newline-terminated lines is checked first.
Option 2: Use \z for Strict End Matching
In C#, \z is the anchor that strictly matches the absolute end of the string (identical to JavaScript's $). Replace $ with \z in your first branch:
var firstLinePattern = new Regex(@"(^.*\z|^.*\r?\n)");
Now, ^.*\z will only match if .* reaches the very end of the string (no trailing newline). For "line1\n", this branch fails, so the engine moves to the second branch, preserving the newline character.
Option 3: Enable ECMAScript Compatibility Mode
C# lets you enable ECMAScript-compliant regex behavior with RegexOptions.ECMAScript. This changes the $ anchor to behave exactly like JavaScript's—only matching the absolute end of the string:
var firstLinePattern = new Regex(@"(^.*$|^.*\r?\n)", RegexOptions.ECMAScript);
With this flag, your original regex will work in C# the same way it does in JavaScript, no changes to the pattern itself needed.
内容的提问来源于stack exchange,提问作者Alexander Mihailov

