Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vim substitution with regex

New to regex and I need to pattern match on some dates to change the format.

I'm going from mm/dd/yy to yyyy-mm-dd where there are no entries prior to 2000.

What I'm unfamiliar with is how to group things to use their respective references of \1, \2, etc.

Would I first want to match on mm/dd/yy with something like ( \d{2} ) ( \/\d{2} ) ( \/\d{2} ) or is it as easy as \d\d/\d\d/\d\d ?

Assuming my first grouping is partially the right idea, I'm looking to do something like:

:%s/old/new/g
:%s/ ( \d{2} ) ( \/\d{2} ) ( \/\d{2} ) / ( 20+\3) - (\3) - (\1) /g

EDIT: Sorry, the replace is going to a yyyy-mm-dd format with hyphens, not the slash.

like image 761
Henry David Avatar asked Nov 02 '12 18:11

Henry David


3 Answers

I was going to comment on another answer but it got complicated.

Mind the magic setting. If you want unescaped parens to do grouping, you need to include \v somewhere in your pattern. (See :help magic).

You can avoid escaping the slashes if you use something other than slashes in the :s command.

You are close. :) You don't want all of those spaces though as they'll require spaces in the same places to match.

My solution, where I use \v so I don't need to escape the parens and exclamation points so I can use slashes in my pattern without escaping them:

:%s!\v(\d{2})/(\d{2})/(\d{2})!20\3-\2-\1!g

This will match "inside" items that start or end with three or more digits though, too. If you can give begin/end criteria then that'd possibly be helpful. Assuming that simple "word boundary" conditions work, you can use <>:

:%s!\v<(\d{2})/(\d{2})/(\d{2})>!20\3-\2-\1!g

To critique yours specifically (for learning!):

:%s/ ( \d{2} ) ( \/\d{2} ) ( \/\d{2} ) / ( 20+\3) - (\3) - (\1) /g
  • Get rid of the spaces since presumably you don't want them!
  • Your grouping needs either \( \) or \v to work
  • You also need \{2} unless you use \v
  • You are putting the slashes in groups two and three which means they'll show up in the replacement too
  • You don't want the parentheses in the output!
  • You're substituting text directly; you don't want the + after the 20 in the output
like image 144
dash-tom-bang Avatar answered Sep 19 '22 12:09

dash-tom-bang


Try this:

:%s/\(\d\{2}\)\/\(\d\{2}\)\/\(\d\{2}\)/20\3-\2-\1/g

The bits you're interested in are: \(...\) - capture; \d - a digit; \{N} - N occurrences; and \/ - a literal forward slash.

So that's capturing two digits, skipping a slash, capturing two more, skipping another slash, and capturing two more, then replacing it with "20" + the third couplet + "-" + the second couplet + "-" + the first couplet. That should turn "dd/mm/yy" into "20yy-mm-dd".

like image 25
Brian Gerard Avatar answered Sep 18 '22 12:09

Brian Gerard


ok, try this one:

 :0,$s#\(\d\{1,2\}\)/\(\d\{1,2\}\)/\(\d\{1,2\}\)#20\3-\2-\1#g

I've removed a lot of the spaces, both in the matching section and the replacement section, and most of parens, because the format you were asking for didn't have it.

Some things of note:

  • With vi you can change the '/' to any other character, which helps when you're trying to match a string with slashes in it.. I usually use '#' but it doesn't have to be.
  • You've got to escape the parens, and the curly braces
  • I use the :0,$ instead of %s, but I think it has the same meaning -- apply the following command to every row between row 0 and the end.
like image 36
Gus Avatar answered Sep 20 '22 12:09

Gus