The time complexity of the regular expression is O(n²)
because the regex engine attempts to match the pattern at each position inside the string. It's important to note that the regex engine scans the input string from left to right, trying to find a match at every position, and the pattern sequences are also evaluated from left to right. For example, when processing [\r\n]+
, the regex engine starts by trying to match it at the beginning of the string. If no CR/LF characters are found, the pattern processing at that location stops, and the engine moves on to the next position within the string, repeating the process until a match is found. Only then does it check for $
.
Therefore, the pattern [\r\n]+$
does not directly locate the end of the string and backtrack to consume line breaks; instead, the regex engine evaluates each position in the string for line breaks and then checks for the end of the string. This can lead to poor performance with large strings.
Some regex engines offer the option to search for matches from the end of the string, such as in .NET (using RegexOptions.RightToLeft
) or in Python's PyPi regex
module (with regex.REVERSE
or (?r)
). Unfortunately, this feature is not available in JavaScript.
One possible alternative is to match any characters other than line breaks followed by line breaks, capturing them. However, keeping a long string within a capturing group may not be efficient. Therefore, using string manipulation methods might be more suitable in such scenarios rather than relying solely on regular expressions like
.replace(/^([\r\n]*[^\r\n]+(?:[\r\n]+[^\r\n]+)*)[\r\n]+$/, '$1')
(or
.replace(/^((?:[\r\n]*[^\r\n]+)+)[\r\n]+$/, '$1')
), which require significantly fewer steps to complete a match compared to the
[\r\n]+$
pattern, but may not offer the best performance in practice.