How to search a match on a line and then make a change on the next line?

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

How to search a match on a line and then make a change on the next line?

boB Stepp
I am relatively new to using regex pattern matching techniques to search for and
replace text.  Today I have been trying to get a better mastery of these
techniques.  But I am currently stumped on the following text search and
replace:

# Home address fields:
# field address STANDARD FIELD -- CANNOT EDIT!
# field address2 STANDARD FIELD -- CANNOT EDIT!
pobox = "PO Box", string
# field city STANDARD FIELD -- CANNOT EDIT!
# field state STANDARD FIELD -- CANNOT EDIT!
# field zip STANDARD FIELD -- CANNOT EDIT!
# field country STANDARD FIELD -- CANNOT EDIT!

# Work address fields:
field work_address = Address, string
field work_address2 = Address2, string
pobox = "PO Box", string
field work_city = City, string
field work_state = State, string
field work_zip = Zipcode, string
field work_country = Country, string

# Other address fields:
field other_address = Address, string
field other_address2 = Address2, string
pobox = "PO Box", string
field other_city = City, string
field other_state = State, string
field other_zip = Zipcode, string
field other_country = Country, string

In the above I want to change the lines starting with "pobox" to match the
format of the line above it, e.g., "field pobox", "field work_pobox" and
"field other_pobox", respectively.  My best effort so far to do this is:

:g/\(field [[:alnum:]_]*\)address2/+s/pobox/\1pobox/

My current understanding (flawed though it is) feels that this command
should do the trick, but it doesn't; instead, it highlights the three
instances of "pobox" and says that it has made three changes without
visibly changing anything.  It is as if "\1" is not storing anything.
What I _think_ the above command is doing is:

1) Search globally and find each line that has the string "field <one or
more alphanumeric characters or underline>address2".

2) Store the above strings in "\1" which I should be able to use in the
replacement string to follow.

3) Advance to the next line with "+".

4) Search on this line for the string "pobox".

5) Replace that instance with "<contents of "\1">pobox".

What am I misunderstanding?

--
Wishing you only the best,

boB Stepp

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/20200514033806.GD7019%40Dream-Machine1.
Reply | Threaded
Open this post in threaded view
|

Re: How to search a match on a line and then make a change on the next line?

Tony Mechelynck
On Thu, May 14, 2020 at 5:38 AM boB Stepp <[hidden email]> wrote:

>
> I am relatively new to using regex pattern matching techniques to search for and
> replace text.  Today I have been trying to get a better mastery of these
> techniques.  But I am currently stumped on the following text search and
> replace:
>
> # Home address fields:
> # field address STANDARD FIELD -- CANNOT EDIT!
> # field address2 STANDARD FIELD -- CANNOT EDIT!
> pobox = "PO Box", string
> # field city STANDARD FIELD -- CANNOT EDIT!
> # field state STANDARD FIELD -- CANNOT EDIT!
> # field zip STANDARD FIELD -- CANNOT EDIT!
> # field country STANDARD FIELD -- CANNOT EDIT!
>
> # Work address fields:
> field work_address = Address, string
> field work_address2 = Address2, string
> pobox = "PO Box", string
> field work_city = City, string
> field work_state = State, string
> field work_zip = Zipcode, string
> field work_country = Country, string
>
> # Other address fields:
> field other_address = Address, string
> field other_address2 = Address2, string
> pobox = "PO Box", string
> field other_city = City, string
> field other_state = State, string
> field other_zip = Zipcode, string
> field other_country = Country, string
>
> In the above I want to change the lines starting with "pobox" to match the
> format of the line above it, e.g., "field pobox", "field work_pobox" and
> "field other_pobox", respectively.  My best effort so far to do this is:
>
> :g/\(field [[:alnum:]_]*\)address2/+s/pobox/\1pobox/
>
> My current understanding (flawed though it is) feels that this command
> should do the trick, but it doesn't; instead, it highlights the three
> instances of "pobox" and says that it has made three changes without
> visibly changing anything.  It is as if "\1" is not storing anything.
> What I _think_ the above command is doing is:
>
> 1) Search globally and find each line that has the string "field <one or
> more alphanumeric characters or underline>address2".
>
> 2) Store the above strings in "\1" which I should be able to use in the
> replacement string to follow.
>
> 3) Advance to the next line with "+".
>
> 4) Search on this line for the string "pobox".
>
> 5) Replace that instance with "<contents of "\1">pobox".
>
> What am I misunderstanding?
>
> --
> Wishing you only the best,
>
> boB Stepp

I would do that with a macro, as follows (starting from Normal mode): (untested)
0. Go to top of file:
        gg
1. Start recording into register q:
        qq
2. Search for "pobox" as a full word starting in column 1:
        /^pobox\><CR>
(where ^ is a real spacing circumflex and <CR> means "Hit Enter")
3. Go to previous line
        k
4. Visually mark characterwise until but not including "addr"
        v/.\zeaddr
5. Yank the current visual selection (cursor goes back to column 1)
        y
6. Go to next line
        j
7. Put before
        P
8. End recording
        q
9. Repeat what we just recorded until we cannot:
        9999@q

(Use a higher count if you have more than 10,000 sections in the file.)


Best regards,
Tony.

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/CAJkCKXtwrzjpy7nDFvPM7FW5BqoRb2ku7sah5CJuhr2Fsk5eDA%40mail.gmail.com.
Reply | Threaded
Open this post in threaded view
|

Re: How to search a match on a line and then make a change on the next line?

Tim Chase
In reply to this post by boB Stepp
On 2020-05-13 22:38, boB Stepp wrote:
> I am relatively new to using regex pattern matching techniques to
> search for and replace text.  Today I have been trying to get a
> better mastery of these techniques.  But I am currently stumped on
> the following text search and replace:
>
> # Home address fields:
> # field address STANDARD FIELD -- CANNOT EDIT!
> # field address2 STANDARD FIELD -- CANNOT EDIT!
> pobox = "PO Box", string


So based on my reading of your description & regex, you want this to
become

field pobox

rather than

# field pobox

(with the leading hash+space)?

> In the above I want to change the lines starting with "pobox" to
> match the format of the line above it, e.g., "field pobox", "field
> work_pobox" and "field other_pobox", respectively.  My best effort
> so far to do this is:
>
> :g/\(field [[:alnum:]_]*\)address2/+s/pobox/\1pobox/

I'd go with something like

 :%s/\(\<field \+\w*\)address2.*\n\s*\zs\zepobox\>/\1

which captures the "field" and optional prefix, then sets the start &
end of the match with \zs and \ze so that the replacement-capture
gets inserted there.

-tim




--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/20200514080038.74e0b9aa%40bigbox.attlocal.net.
Reply | Threaded
Open this post in threaded view
|

Re: How to search a match on a line and then make a change on the next line?

boB Stepp
On Thu, May 14, 2020 at 08:00:38AM -0500, Tim Chase wrote:

>On 2020-05-13 22:38, boB Stepp wrote:
>> I am relatively new to using regex pattern matching techniques to
>> search for and replace text.  Today I have been trying to get a
>> better mastery of these techniques.  But I am currently stumped on
>> the following text search and replace:
>>
>> # Home address fields:
>> # field address STANDARD FIELD -- CANNOT EDIT!
>> # field address2 STANDARD FIELD -- CANNOT EDIT!
>> pobox = "PO Box", string
>
>
>So based on my reading of your description & regex, you want this to
>become
>
>field pobox
>
>rather than
>
># field pobox
>
>(with the leading hash+space)?
>
>> In the above I want to change the lines starting with "pobox" to
>> match the format of the line above it, e.g., "field pobox", "field
>> work_pobox" and "field other_pobox", respectively.  My best effort
>> so far to do this is:
>>
>> :g/\(field [[:alnum:]_]*\)address2/+s/pobox/\1pobox/
>
>I'd go with something like
>
> :%s/\(\<field \+\w*\)address2.*\n\s*\zs\zepobox\>/\1
>
>which captures the "field" and optional prefix, then sets the start &
>end of the match with \zs and \ze so that the replacement-capture
>gets inserted there.

Thank you, this works.  I had to look up some of the flags you used before
I understood what you are doing.  In my particular example I found I did
not need to use "\+", "\<" and "\>" to get the desired result.  But I suspect that you are using
these to protect against scenarios I am not considering.  Would you mind
elaborating on why you included those additional flags?

As I am trying to understand the "why"s of all of this, it would be helpful
for my understanding if someone would explain why my regex did not work as
expected.  I am still not seeing it.

--
Wishing you only the best,

boB Stepp

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/20200514172709.GA17993%40Dream-Machine1.
Reply | Threaded
Open this post in threaded view
|

Re: How to search a match on a line and then make a change on the next line?

Tim Chase
On 2020-05-14 12:27, boB Stepp wrote:
> On Thu, May 14, 2020 at 08:00:38AM -0500, Tim Chase wrote:
> > :%s/\(\<field \+\w*\)address2.*\n\s*\zs\zepobox\>/\1
>
> Thank you, this works.  I had to look up some of the flags you used
> before I understood what you are doing.  In my particular example I
> found I did not need to use "\+", "\<" and "\>" to get the desired
> result.  But I suspect that you are using these to protect against
> scenarios I am not considering.  Would you mind elaborating on why
> you included those additional flags?

The space followed by \+ requires at least one but possibly more than
one space between "field" and the start of the field-name.  So it
takes into consideration

  field   work_address2 STANDARD FIELD -- note multiple spaces

The \< prevents strange situations like

  greenfield work_address

by ensuring that "field" stands alone without junk at the beginning.
Unlikely, but handy to specify up front.  Likewise the \>  prevents
it from finding something like "pobox2".  Which you might or might
not want.  Adjust according to your requirements.

> As I am trying to understand the "why"s of all of this, it would be
> helpful for my understanding if someone would explain why my regex
> did not work as expected.  I am still not seeing it.
> :g/\(field [[:alnum:]_]*\)address2/+s/pobox/\1pobox/

As best I understand, the items captured in the g// portion don't
carry over for reuse in the s// replacement (the s// resets the
capture groups). Otherwsie, I believe it would work since

  :g/\(field [[:alnum:]_]*\)address2/+

correctly finds the first lines and

  s/pobox/XYZ/

correctly does a substitution, merely lacking access to the captured
\1 group.  If you add some brackets around the \1 you'll see the
effect:

  :g/\(field [[:alnum:]_]*\)address2/+s/pobox/[\1]pobox/

you'll see resulting lines like

  []pobox

to show that the contents of \1 are empty.

Hope that helps,

-tim




--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/20200514125229.55461682%40bigbox.attlocal.net.
Reply | Threaded
Open this post in threaded view
|

Re: How to search a match on a line and then make a change on the next line?

boB Stepp
On Thu, May 14, 2020 at 12:52:29PM -0500, Tim Chase wrote:

>On 2020-05-14 12:27, boB Stepp wrote:
>> On Thu, May 14, 2020 at 08:00:38AM -0500, Tim Chase wrote:
>> > :%s/\(\<field \+\w*\)address2.*\n\s*\zs\zepobox\>/\1
>>
>> Thank you, this works.  I had to look up some of the flags you used
>> before I understood what you are doing.  In my particular example I
>> found I did not need to use "\+", "\<" and "\>" to get the desired
>> result.  But I suspect that you are using these to protect against
>> scenarios I am not considering.  Would you mind elaborating on why
>> you included those additional flags?
>
>The space followed by \+ requires at least one but possibly more than
>one space between "field" and the start of the field-name.  So it
>takes into consideration
>
>  field   work_address2 STANDARD FIELD -- note multiple spaces
>
>The \< prevents strange situations like
>
>  greenfield work_address
>
>by ensuring that "field" stands alone without junk at the beginning.
>Unlikely, but handy to specify up front.  Likewise the \>  prevents
>it from finding something like "pobox2".  Which you might or might
>not want.  Adjust according to your requirements.

Ah!  All of that makes sense now and it is something that I should be
practicing.  Thank you!

>> As I am trying to understand the "why"s of all of this, it would be
>> helpful for my understanding if someone would explain why my regex
>> did not work as expected.  I am still not seeing it.
>> :g/\(field [[:alnum:]_]*\)address2/+s/pobox/\1pobox/
>
>As best I understand, the items captured in the g// portion don't
>carry over for reuse in the s// replacement (the s// resets the
>capture groups). Otherwsie, I believe it would work since
>
>  :g/\(field [[:alnum:]_]*\)address2/+
>
>correctly finds the first lines and
>
>  s/pobox/XYZ/
>
>correctly does a substitution, merely lacking access to the captured
>\1 group.  If you add some brackets around the \1 you'll see the
>effect:
>
>  :g/\(field [[:alnum:]_]*\)address2/+s/pobox/[\1]pobox/
>
>you'll see resulting lines like
>
>  []pobox
>
>to show that the contents of \1 are empty.

Hmm.  That is what I was speculating -- that "\1" was empty -- and you have
proven that plus giving me another little trouble shooting technique.  But
an example in the book "Learning the vi and Vim Editors, 7th ed." by Arnold
Robbins, Elbert Hannah and Linda Lamb made me believe what I was trying was
doable.  In chapter 6:  Global Replacement on page 82 the authors give this
snippet of text:

mgibox routine;
mgrbox routine;
mgabox routine;

In this exercise we want to replace "box" with "square".  The authors give
two ways to accomplish this.  One of them is:

:g/mg\([ira]]\)box/s//mg\1square/g

Here where the line found is also the line to change (unlike mine where I
want to change the next line) the "\1" register is accessible.  I tried my
case with "g" on the end, but it did not make any difference.  Obviously
there is still something here I am not comprehending properly.

--
Wishing you only the best,

boB Stepp

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/20200514183246.GB17993%40Dream-Machine1.
Reply | Threaded
Open this post in threaded view
|

Re: How to search a match on a line and then make a change on the next line?

Tim Chase
On 2020-05-14 13:32, boB Stepp wrote:

>>  :g/\(field [[:alnum:]_]*\)address2/+s/pobox/[\1]pobox/
>>
>>you'll see resulting lines like
>>
>>  []pobox
>>
>>to show that the contents of \1 are empty.  
>
> Hmm.  That is what I was speculating -- that "\1" was empty -- and
> you have proven that plus giving me another little trouble shooting
> technique.  But an example in the book "Learning the vi and Vim
> Editors, 7th ed." by Arnold Robbins, Elbert Hannah and Linda Lamb
> made me believe what I was trying was doable.  In chapter 6:
> Global Replacement on page 82 the authors give this snippet of text:
>
> mgibox routine;
> mgrbox routine;
> mgabox routine;
>
> In this exercise we want to replace "box" with "square".  The
> authors give two ways to accomplish this.  One of them is:
>
> :g/mg\([ira]]\)box/s//mg\1square/g
>
> Here where the line found is also the line to change (unlike mine
> where I want to change the next line) the "\1" register is
> accessible.

In this case it's not whether the line is the same but that the
pattern is the same.  By (re)using the pattern from the :g in the :s
by using an empty pattern here:

  s//replacement/flags

it (re)captures the portion captured from the same pattern found in
the :g part of the command.  So if you happened to have two adjacent
lines both containing "capture", you could do something like

  :g/\(capture\)/+s//replacement with \1 in it/

(which has the "+" relative-offset to the line after the initial
match) to replace the one on the 2nd line with "replacement with
capture in it".

Hopefully that sheds a bit of light on matters?

-tim





--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/20200514142007.34ce5a94%40bigbox.attlocal.net.
Reply | Threaded
Open this post in threaded view
|

Re: How to search a match on a line and then make a change on the next line?

boB Stepp
On Thu, May 14, 2020 at 02:20:07PM -0500, Tim Chase wrote:
>On 2020-05-14 13:32, boB Stepp wrote:

>> Hmm.  That is what I was speculating -- that "\1" was empty -- and
>> you have proven that plus giving me another little trouble shooting
>> technique.  But an example in the book "Learning the vi and Vim
>> Editors, 7th ed." by Arnold Robbins, Elbert Hannah and Linda Lamb
>> made me believe what I was trying was doable.  In chapter 6:
>> Global Replacement on page 82 the authors give this snippet of text:
>>
>> mgibox routine;
>> mgrbox routine;
>> mgabox routine;
>>
>> In this exercise we want to replace "box" with "square".  The
>> authors give two ways to accomplish this.  One of them is:
>>
>> :g/mg\([ira]]\)box/s//mg\1square/g
>>
>> Here where the line found is also the line to change (unlike mine
>> where I want to change the next line) the "\1" register is
>> accessible.
>
>In this case it's not whether the line is the same but that the
>pattern is the same.  By (re)using the pattern from the :g in the :s
>by using an empty pattern here:
>
>  s//replacement/flags
>
>it (re)captures the portion captured from the same pattern found in
>the :g part of the command.  So if you happened to have two adjacent
>lines both containing "capture", you could do something like
>
>  :g/\(capture\)/+s//replacement with \1 in it/
>
>(which has the "+" relative-offset to the line after the initial
>match) to replace the one on the 2nd line with "replacement with
>capture in it".
>
>Hopefully that sheds a bit of light on matters?

I now see the light!  Thanks, Tim!

--
Wishing you only the best,

boB Stepp

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/20200514220308.GD17993%40Dream-Machine1.