2

Would you be able to help me in finding out how I can use the Notepad++ Find/Replace function to replace a specific character between specific words. Please see the example below, I would like to find all hyphens between the words 'START' and 'END' and replace them with spaces. All other hyphens in the document would not be replaced.

I've been trying to solve this using examples of regular expressions I googled but unfortunately I've had no success so far.

Here is what I have:

START-Hyphens-should-be-replaced-here-01-END
OTHER-no-changes-here-02-WORD
START-Hyphens-should-also-be-replaced-here-03-END
OTHER-no-changes-here-either-04-TEXT

Here is what I would like to have:

START Hyphens should be replaced here 01 END
OTHER-no-changes-here-02-WORD
START Hyphens should also be replaced here 03 END
OTHER-no-changes-here-either-04-TEXT
  • Hm... I'd actually recommend avoiding regex and just parsing it character-by-character in a scripting language, if possible. – Bob Dec 7 '13 at 11:36
  • A regex solution would be more feasible if you can guarantee that there will only ever be one occurrence of START and END per line, and that it will not span lines (i.e. there is never a newline between START and END). – Bob Dec 7 '13 at 11:55
  • Hi Bob. Yes, I can guarantee this. Would you know how to do this with regex? Thank you :) – Piotr Berebecki Dec 7 '13 at 13:27
5

I would recommend you abandon regex entirely - it is simply too much trouble to get it to work in cases like this. Not everything can be solved easily with regex! In this case, most scripting languages can do this fairly easily. I've written a piece of JavaScript for you, here - just enter your delimiters and input, and click submit.

As for an explanation: it splits by the start delimiter, then for each 'block' it splits by the end delimiter. We end up with a block of text that is in between a start and end, and perform a replacement on that. Then we join the split blocks back together. That's not the most efficient way, but it's a bit easier than processing individual characters.

xkcd - Perl Problems


Old, partially working, regex solution lies below. I heavily recommend you do not use it.

Quick answer: don't use Notepad++, use this JS snippet (enter your text at the bottom right and click submit). Replace START and END in the script to the bottom left as necessary.

Alternatively, you can use PowerShell, which is native to Windows (Vista+):

Get-Content input.txt | %{ $_ -replace "(?<=START.*?)-(?=.*?END)", " " } | Out-File output.txt

Replace input.txt and output.txt accordingly. Note: they must be different files.

This particular solution only works without bugs if you have only one occurrence of START per line and one occurrence of END per line and they do not span lines - so we can deal with each line independently.


Given the guarantees that only one match will be needed per line, and each match is contained within a line, matching for (?<=START.*?)-(?=.*?END) and replacing with a single space would work - except Notepad++ doesn't seem to like this particular pattern. It supports lookbehinds and lookaheads (full PCRE, apparently), but there's something about this that trips it up.

An alternative, which is similar to what Johannes has already answered, is to use a simple pattern: (START.*?)-(.*?END) and replace it with \1 \2. The problem here is this will only match one hyphen per line at a time. This quickly becomes tedious.

Here, we try to use a more complete scripting language. JavaScript is a good alternative. However, it doesn't support lookbehinds. On the plus side, we can run things in a loop, so the previous tedious solution is actually viable. I've created an example here.

  • Not sure why you prefer to use lookbehinds over the solution I got - they only slow things down, as the engine needs to backtrack a lot. Agreed with the scripting-part though ;) – Johannes H. Dec 7 '13 at 14:02
  • @JohannesH. Thing is, with lookbehinds working properly you don't need to loop. Yes, the engine needs to backtrack a lot - but is that any worse than having to repeat the entire match the same number of times? – Bob Dec 7 '13 at 14:04
  • Notepad++ only does one replacement per line anyway. (or, to be more precise, it continues its search for matches after the end of the current match, and does not try to match the pattern to anything it already matched). – Johannes H. Dec 7 '13 at 14:07
  • @JohannesH. The beauty of lookbehinds and lookaheads is they are technically not part of the match - so that answer would only match the hyphen itself. Then the search should continue immediately after the hyphen, never matching any other characters. I'm not sure why it doesn't work in NP++ - it works fine in .NET (and Expresso, where I tested). – Bob Dec 7 '13 at 14:09
  • 1
    @JohannesH. Aha! Apparently variable-length lookbehinds only work in .NET and Perl 6, (not even Perl 5, which PCRE is mostly based on) of the major regex implementations. So Notepad++ doesn't support that. – Bob Dec 10 '13 at 8:51
2

A suitable regexp would be (START[^\-]*)-(.*END), replace that with \1 \2 and use "Replace All" MULTIPLE TIMES (will only replace one - on each line per click).

  • 2
    You need to use .*? (lazy match), else it will end on the last occurrence of END, which will be a problem if END occurs more than once. Edit: you should probably use *? in the first capture too, otherwise that would probably do the same. – Bob Dec 7 '13 at 14:01
  • Oh. You're right, I missed that. Will edit it in (although your answer is way more complete anyway) – Johannes H. Dec 7 '13 at 14:04
  • Ah, never mind about the multiple END thing - even a lazy match doesn't fix it! My answer is similarly broken. It would be necessary to specifically exclude END in the middle of the match, which is very difficult in regex, if it is at all possible. – Bob Dec 7 '13 at 14:25
  • @Bob Excluding END can be done using a negative lookahead I guess, or using ([^E]|E[^N]|EN[^D]|END[^\b]) (erm... untested). (Oh and I removed the lazy match again, if it doesn't work anyway) – Johannes H. Dec 7 '13 at 14:28
  • Yea, I just went with a full scripted solution. It actually works in all cases. A regex one would be interesting, but probably not entirely practical. Oh hey, (?<=START([^E]|E[^N]|EN[^D])*?)-(?=([^E]|E[^N]|EN[^D])*?END) works. Kinda. – Bob Dec 7 '13 at 14:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.