5

I'm trying to match lines that doesn't end with either . or ! and doesn't end with either ." or !" so it should match both

  • say "bye"
  • say "bye

but shouldn't match:

  • say "bye.
  • say "bye!
  • say "bye."
  • say "bye!"

I tried using positive and negative lookahead, trying to use them as AND as suggested in Regex AND operator, but I can't make it work, nor I'm sure it's feasible with lookaheads.

Vampire
  • 35,631
  • 4
  • 76
  • 102
Mauro
  • 135
  • 9
  • what will be desired result for strings like `say "bye".` should it match ? – Code Maniac Aug 17 '19 at 12:18
  • You can try something like this [`^(?!.*[.!]"?$).*$`](https://regex101.com/r/BvFegP/1/) – Code Maniac Aug 17 '19 at 12:46
  • It should match any line that doesn't end with . or ! or ." or !" so ```say "bye".``` shouldn't match. Your suggestion seems to work, I'll test it further and study it to understand what it does; thanks! – Mauro Aug 17 '19 at 12:52
  • Why do you think jEdit has no lookbehind? Of course it has, it has all that Java supports, lookbehind included. – Vampire Aug 19 '19 at 09:29
  • Because in jEdit's user guide's page about regex lookaheads are listed, but lookbehinds are not, so I thought it didn't had them: http://www.jedit.org/users-guide/regexps.html – Mauro Aug 19 '19 at 13:16
  • Not all details are listed, that's why the link above is provided. I added lookbehinds now to the docs for the next version though. – Vampire Aug 19 '19 at 13:46

3 Answers3

4

You can use

^(?!.*[.!]"?$).*$

enter image description here

Regex Demo

Note:- This matches empty line too as we use * which means match anything zero or more time, if you want to avoid empty lines to match you can use + quantifier which means match one or more time

Code Maniac
  • 37,143
  • 5
  • 39
  • 60
1

Just use a negative lookbehind. This matches exactly what you asked for: ^.*+(?<![.!]"?)$


^ - beginning of line
.*+ - any amount of characters, not giving up for backtracking
(?<! + ) - not preceded by
[.!] - dot or exclamation mark
"? - optional double-quote
$ - end of line

Vampire
  • 35,631
  • 4
  • 76
  • 102
  • And is much more efficient, the lookahead variant has to match the whole string twice. My version needs for a matching line that is x characters long always `6` steps in the regex engine, the lookahead variant needs `x * 2 + 6` steps. And my (now slightly improved) version needs for a non-matching line always `5` steps in the regex engine, the lookahead variant needs `10` steps. – Vampire Aug 19 '19 at 14:06
  • What does it mean "not giving up for backtracking"? – Mauro Aug 19 '19 at 15:58
  • 1
    It is a possessive quantifier, meaning once it matched something it won't give up characters to somehow find a match that makes the regex match. Read more about it at https://www.regular-expressions.info/possessive.html. Without the `+` for a non-matching line the engine would first match all characters and see whether then comes the end not preceded by dot or exclamation mark and optional double-quote. As this is not the case, the non-possessive `*` would give up one character and the same check is done. But as this can never match, we can use the possessive quantifier there. – Vampire Aug 19 '19 at 16:13
  • @Vampire where did you tested this regex this shows pattern error to me [`Regex Demo`](https://regex101.com/r/IMcn4E/2) – Code Maniac Aug 19 '19 at 17:47
  • @CodeManiac we are talking about Java regexes. With what you linked you can only test PHP, JavaScript, Python and Golang regexes. Each flavor has its specifics. For PHP you could for example write `^.*+(?<![.!]|[.!]")$` to have it valid. – Vampire Aug 19 '19 at 21:23
  • @Vampire oh i see i consider generally `perl` regex as one of most advanced regex so i didn't knew java has this functionality of dynamic length lookbehind, any recommended tool to test java regex performance online ? – Code Maniac Aug 20 '19 at 01:41
  • I used evaluation version of RegexBuddy as I was curious. You also have to keep in mind that PCRE (Perl Compatible Regular Expressions) are not the same as Perl regular expressions, especially when it comes to lookbehind. In Perl all options have to have the same length. In PCRE they may have different lengths (but obviously not with quantifiers). In Java the maximum size just has to be able to be calculated and be in some bounds, but you can e. g. use `{,9999999}` instead of `*` if you can estimate it is enough and so on. – Vampire Aug 20 '19 at 09:40
0

Ensure bye isn't succeeded by . or ! with a positive look-ahead for a negative character class, and then make the last " optional with the ? quantifier:

\bsay "bye(?=[^.!])"?
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • How can I make it work for any line, not just the example one? I tried ```^.+(?=[^.!])"?```, but it doesn't work. Edit: Code Maniac's suggestion seems to work, I'm testing it. – Mauro Aug 17 '19 at 12:48
  • I'm assuming `jEdit`'s search-replace feature has a "Replace All" function? – Mathias R. Jessen Aug 17 '19 at 12:52
  • Yes, it does have it. – Mauro Aug 17 '19 at 12:52
  • This version would not match a line ending on `bye` like the second example in the question, as it always needs one character after the `bye` that is not a dot or exclamation mark. Additionally the regex does not test for the end of string. – Vampire Aug 19 '19 at 14:08