Regex: Select only the first instance of search results / first match
-
Wouldn’t
(?s)\A.*?<tr>.*?</tr>
work to get only the first? -
I would normally say that you should have started a new topic, rather than reviving one from 4.5 years ago. But since 4.5 years later, you still haven’t learned the lesson that Guy taught you in 2016, maybe you should be in the same single topic – but it would be better to actually learn from the dozens of different regular expressions that we have provided for you over the last 4.5 years. This forum is not a regular expression help forum – this is a Notepad++ discussion forum, where regex are only a small part of the power of Notepad++.
In your new question, you state ,
I have many of
<tr></tr>
tags on a html page.That phrasing, in English, implies you have only one HTML page you are doing this to. If that’s really the case, then it’s really simple: take the simple regex you guessed, and hit FIND once and REPLACE once, and you are done. Or you could have just gone to the beginning of the document, and done a single search for
<tr>
and then manually done the replacement, which is even easier.But I doubt that’s your real situation. The only reason it would make sense to ask this question is if you were really doing a Find In Files > Replace All, in order to make this single change in multiple HTML files.
As Guy explained in 2016, if you only want to replace one instance per file (the first instance), you can do that by consuming the rest of the file in the single regex. That will work for small files, but if your files are too large, it will not work, because regex has only a certain amount of capture memory.
Fortunately, since then, assuming you have updated Notepad++, the developers have fixed the
\A
anchor, so the beginning-of-file check works – as @Alan-Kilborn showed in his recent reply.I will modify his (and the regex I was going to supply, which consumed all), to give an example replacement
If I have the simple file:
<html><body> <table> <tr> get rid of stuff including <embedded/> <tags/> </tr> <tr> keep stuff including <embedded/> <tags/> </tr> </table> </body> </html>
and I run
- FIND =
(?s)\A.*?<tr>\s*\K.*?(\s*</tr>)
REPLACE =new contents$1
MODE = regular expression
REPLACE ALL
then I get
<html><body> <table> <tr> new contents </tr> <tr> keep stuff including <embedded/> <tags/> </tr> </table> </body> </html>
This should work, even on long files. It should work the same if you’re using Find in Files instead of the single-file Replace dialog.
Compared to @Alan-Kilborn’s regex, I added the feature that it uses the
\K
reset to automatically keep everything up to the first<tr>
. I also kept the spaces after the<tr>
and the spaces before the</tr>
(where “spaces” include any space character, even newlines), so that way if you have<tr>blah</tr>
all on one line, your replacement will stay all on one line, but if you have the three-line version like you showed, it will stay as three lines.Because I captured the final spaces and
</tr>
, I had to include $1 in the replacement to re-instate that part of the text. But it could have been done with positive lookahead instead, meaning you wouldn’t need the group text in the replacement. TIMTOWTDI.Now that we’ve given you an answer that works for the situation you described, please take the following advice: Please remember that this isn’t a “give me a regex forum”. This isn’t even a paid support, where we are obligated to help you. This is a community to discuss Notepad++; we will answer the occasional Notepad++-related regex question, especially for new users who have never been exposed to regular expressions. But we expect people who have been around the forum for many years to participate in helping others, not just in getting free regex creation service. Please learn from the four and a half years of regular expression advice we have been providing you. Many, many times, we have linked you to the regular expression documentation. I will give you my boiler plate one more time, just in case you missed it the last however many times I’ve posted it here. But please understand that if you continue to show a disregard for our previous advice, and if you continue to just “request” that we craft regex for you, rather than truly participating in the forum, you will find fewer and fewer here who are willing to help you, and you might start noticing downvotes on your questions.
----
Do you want regex search/replace help? Then please be patient and polite, show some effort, and be willing to learn; answer questions and requests for clarification that are made of you. All example text should be marked as literal text using the
</>
toolbar button or manual Markdown syntax. To makeregex in red
(and so they keep their special characters like *), use backticks, like`^.*?blah.*?\z`
. Screenshots can be pasted from the clipboard to your post usingCtrl+V
to show graphical items, but any text should be included as literal text in your post so we can easily copy/paste your data. Show the data you have and the text you want to get from that data; include examples of things that should match and be transformed, and things that don’t match and should be left alone; show edge cases and make sure you examples are as varied as your real data. Show the regex you already tried, and why you thought it should work; tell us what’s wrong with what you do get. Read the official NPP Searching / Regex docs and the forum’s Regular Expression FAQ. If you follow these guidelines, you’re much more likely to get helpful replies that solve your problem in the shortest number of tries. - FIND =
-
This post is deleted! -
@PeterJones said in Regex: Select only the first instance of search results / first match:
<tr>
get rid of stuff including <embedded/> <tags/>
</tr>how about for the last instance? I should use
\z
isn’t it ?Like this:
(?s)\z.*?<tr>\s*\K.*?(\s*</tr>)
but this does not select the last instance. what did I do wrong
-
Think about the order of events. If
\z
means the end of the file, then\z.*?<tr>
means match zero or more characters after the end of the file followed by<tr>
. You cannot have zero or more characters followed by<tr>
after the end of the file. -
@PeterJones then how would it be correct?
-
You’ve already gotten the freebie from me on this question. You need to put more thought and effort into it.
Did you know, when I am helping anyone in this forum by coming up with a regular expression for their problem, that I don’t automatically know the solution off the top of my head? Do you know what I do? I break the problem down into little pieces, then translate each of those pieces into regex syntax, then I try the regex out; if it doesn’t work, I try to figure out why, and see if I can tweak my initial guess until it does work.
This same process will work for you, if you give it a try. I will even give you a boost, by stating your problem in the little pieces I would initially try
- find
<tr>
to</tr>
, without any contained<tr>
- followed by anything that’s not a
<tr>
- until the end of the file
Also, although it’s not highly Notepad++ related: if you are working on a large website, where you’re going to be frequently changing the template (I assume you are changing content in boilerplate that surrounds your HTML), then I highly recommend a system with a templating language, often implemented as a CMS (content management system), so that you don’t have to be modifying the same code multiple times. In the long run, that will be more efficient than coming up with a new regex for every template change.
I hope you take to heart the advice I’ve given you here.
- find
-
@PeterJones it is easy for a developer to understand code, but is not easy for a painter to understand the code… :)
-
it is easy for a developer to understand code, but is not easy for a painter to understand the code…
If you can’t stand the heat, get out of the kitchen.
Q: What’s the meaning of the phrase ‘If you can’t stand the heat, get out of the kitchen’?
A: Don’t persist with a task if the pressure of it is too much for you. The implication being that, if you can’t cope, you should leave the work to someone who can.
It’s getting embarrassing for you.
It’s almost an uncomfortable thing to witness.
:-( -
@Vasile-Caraus said in Regex: Select only the first instance of search results / first match:
@PeterJones it is easy for a developer to understand code, but is not easy for a painter to understand the code… :)
The whole point of CMS is to make it easier for painters to make a website, almost literally. If you cannot learn the CMS, then you should probably hire an expert.
Conversely, to your statement, if I go to my painter friend and ask him to paint me a picture to hang on the wall, he might do it for free one time, but after that, he’ll tell me “either learn to paint yourself, or pay me”.
You know enough about regex to come up with some guesses, so you already know a lot of the code, and you have been pointed to the documentation to be able to learn more. Try to piece together the bits you know in the right order for the problem you have. I have given you hints as to the right order. But at some point, you’re going to have to choose to learn, or find someone you can pay to do the development for your website.
Because expecting us to be your personal free regular-expression writers for 4.5years, without ever contributing anything else to this forum, is … Nevermind.
Good luck.
-
@Vasile-Caraus said in Regex: Select only the first instance of search results / first match:
your (?s)\b<tr>.+?</tr>\b is not working :(
Now that I’m on a PC I can see the original post (recent) from you stating your regex was “finding both <tr>…</tr>” was most likely false. Copying the examples I see that by using the
\b
, it actually prevented the regex from capturing the example text.So my first solution to add a
?
to your regex would have fixed the issueIF
your regex was actually capturing too much. My later statement to remove the\b
was in fact a correct move and would/should have resolved the issue. Except your statement about capturing the “first” only “<tr>…</tr>” was a bit troubling as you didn’t have the\A
anchor as @PeterJones stated was needed. I assumed (wrongly possibly) that you possibly had a large html file in which you wanted to find (and replace?) the first “<tr>…</tr>” and if using ONLY the “Find Next” or “Replace” button when cursor was at start of an open file would have found the first set.I guess I have learnt a valuable lesson, don’t provide answers when not able to independently confirm OP statements. I was on an android tablet late in the evening and about to go to bed when I saw this post and assumed it was an easy fix given the statement about it selecting too much text.
Terry
PS thanks Alan for coming to my rescue ;-))
-
@PeterJones said in Regex: Select only the first instance of search results / first match:
<tr>
if someone knows how to select only the last instance of my request, please write the solution, not for me, but for those 36 k who have read this topic, and will read it from now on. Don’t do it for me, do it for other that search this solution.
-
Future readers,
Since there historically have been a lot of reads on this discussion, it will be good to have the both the “first match” and “last match” versions of the
<tr>
question posed earlier, since it was brought up. Also, I had forgotten that there was an advanced concept needed for the find-the-last that wasn’t needed in the regex for find-the-first, which even after reading the docs isn’t obvious. So for teaching purposes, here is how I went about solving the problem.I started by saying, "I want from
<tr>
to</tr>
, then as few characters as possible between the</tr>
and the end. I knew that wasn’t enough, but it was a starting point for my regex. I translated that into(?s)<tr>.*?</tr>.*?\z
– which searches for literal text, then as few as possible of any character, then literal text, then as few as possible of any character followed by end of file. That gets you close, but isn’t quite there, because “as few as possible” doesn’t guarantee that it won’t contain other<tr>
, because of the way regex engines work.There’s a useful idiom of “multiple characters in a row, as long as they don’t contain some unwanted string”. It actually took me a while to re-find this, because I lost my bookmark (and don’t use it often):
((?!UNWANTED).)*
, which can be described as: do a lookahead(?!...)
where the text after here cannot match what’s inside here – in this case, the literalUNWANTED
, but it could be an arbitrary regex itself – but that lookahead doesn’t consume any characters, so now we want to match exactly one character (I might call that, “capture one character if the UNWANTED lookahead isn’t found”); repeat this sequence 0 or more times. For the purposes of the dummy example proposed by the previous questioner,((?!<tr>).)*
says “find as many characters as you can, as long as they don’t contain<tr>
”.We can use this in place of both of the
.*
in the regex above:(?s)<tr>((?!<tr>).)*?</tr>((?!<tr>).)*?\z
. This gets us really close…But we’ve matched all of the text from the beginning of the final
<tr>
to the end of the document. That will make it harder to replace just the contents of the<tr>...</tr>
pair. It could be done with groups, but in this nested mass of parentheses, getting the right group number will be confusing. Instead, I will convert the first<tr>
to a lookbehind(?<=<tr>)
, which says that<tr>
must come immediately before our match, but not be in the match. And I convert the ending</tr>
as well as everything that comes after that into a lookahead, so it’s not included in our match, either. That brings our expression to(?s)(?<=<tr>)((?!</tr>).)*?(?=</tr>((?!<tr>).)+?\z)
– which, at first glance, looks really confusing; but when it was built up step-by-step, it’s not so bad.If I add in the
(?x)
(or,(?sx)
to combine it), I can add some space and document it:(?xs) (?<=<tr>) ((?!</tr>).)*? (?=</tr>((?!<tr>).)+?\z) ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ | | `- this must come after our match | `- this is what we really want to match; | the <tr> is literal, the rest is syntax `- this must come before our match; the <tr> is literal, the rest is syntax
With this FIND expression, what you put in REPLACE just needs to be the literal new content for your final
<tr>
entry.so with the text
<other>stuff</other> <tr>first</tr> <tr>second</tr> <tr>third</tr> <tr>4</tr> <tr>5</tr> <tr>6</tr> <tr>last</tr> <other>stuff</other>
FIND NEXT will just select
last
, and if you do a REPLACE (or REPLACE ALL), it will just replace that content.Personally, I would not expect a fresh newbie to regexes to get very far on this problem, but someone who has been using regular expressions for a few years, I would expect to get at least as far as
(?s)<tr>.*?</tr>.*?\z
, so their question would be “I want to find the last<tr>...</tr>
pair in a document and replace its contents; I tried(?s)<tr>.*?</tr>.*?\z
because I thought that would find as little as possible between the open and close tag, and as little as possible between the close tag and the end of file, but it’s still grabbing all the<tr>
pairs rather than just the last one; can you help me find what I’m doing wrong?”.It all looks difficult… but the more you use regular expressions, and the more you try to figure out new combinations of the pieces you know, the more you will understand how the bits and pieces work together. Every time you come across a need for a new complicated search-and-replace expression, start with the pieces you know, look at the docs, try to add in the new bits you are trying to learn; ask specific questions (“here’s what I tried and why, but it didn’t work” or “here’s what I’ve tried so far, but I don’t know how to edit this regex so that XXXX” (where XXXX is specific) is infinitely better than “here’s my text; make a regex for me”), and every time you learn something new, put it in your mental bag of tricks. (Sometimes, even for people like me who use regex a lot, the bag of tricks overflows, and you’ll have to re-figure out something you’ve done before; sometimes, the end result will look the same, but other times it will end up looking quite different… which is good, because you’ve learned a new way of doing it that you didn’t know before… and maybe this one will stick with you better than the one you forgot before.)
-
Nice treatment, Peter. +1 (or more).
It’s an analysis by a thinking human being that has demonstrated he is capable of learning, adapting, and growing – wouldn’t it be great if everyone was like that?
Thank you on behalf of 32.6K readers (if I may be so bold as to speak for them) for this and your other thoughtful and thorough contributions to the Notepad++ user Community, and of course the N++ user manual. -
BTW, I solved it on my own as well, for my own “pleasure”.
But I wasn’t going to post it, punishing, I guess, the 32.6K readers that were on the edge of their seat waiting for it – at the expense of those that I didn’t really want to have it. But since you let the cat out of the bag, perhaps it is instructive to see a different approach:(?s)<tr>.*</tr>.*?<tr>\K.+?(?=</tr>.*?\z)
It seems to work; maybe there are holes.
-
@Alan-Kilborn said in Regex: Select only the first instance of search results / first match:
BTW, I solved it on my own as well, for my own “pleasure”.
I also had, using @PeterJones solution for the “first” instance, removing JUST 1 character. Maybe mine also has holes.
(?s)\A.*<tr>\s*\K.*?(\s*</tr>)
So turning a non-greedy regex into a greedy one. It firstly grabs everything, then backs up until the <tr>…</tr> sequence is true. Even the\A
sequence could be removed IF the cursor were in the first position of the open file.Terry
-
@Terry-R and @Alan-Kilborn ,
Those are so much simpler than mine! Congrats! 🎉👏👍
Anyway, I am still glad I presented my solution, as it hopefully shows future readers a thought process that can arrive at a working regex, even if it’s not the simplest or most efficient.
-
@PeterJones said in Regex: Select only the first instance of search results / first match:
…so much simpler…
Well, maybe.
But nothing is going to beat your discussion of your thought process.
An important factor in a good solution.I’ve always thought of the
((?!UNWANTED).)*
construct as somewhat “expensive”, but maybe that’s just because it “feels” complicated, but it would take a true regex genius like @guy038 to discuss that.Nice one as well!
-
I was experimenting with your regex a bit and I noticed that not only did it match the text inside the final <tr></tr> pair, but it also matched the </tr> tag as well?
Peter’s and my regexes only matched what was inside; not sure if you were solving something Vasile wanted or not with that – not going back to read/revisit it! – but I took the liberty of tweaking yours a bit so it matches what ours does:
(?s)\A.*<tr>\K.+?(?=</tr>)
and that appears to be the shortest matching regex thus far.
-
@Alan-Kilborn said in Regex: Select only the first instance of search results / first match:
I was experimenting with your regex a bit and I noticed that not only did it match the text inside the final <tr></tr> pair, but it also matched the </tr> tag as well?
As I said it was from @PeterJones solution for the first instance. Thus in his post:
FIND = (?s)\A.?<tr>\s\K.?(\s</tr>)
REPLACE = new contents$1
MODE = regular expression
REPLACE ALL
then I getSo the replacement text would have been
new contents$1
, again same as the first instance solution. Sorry forgot to mention that.Terry