Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have big XML file

<obj param="2542">
<obj param="2333">
<obj param="6433">

I need to increase all "param" values by some number. I can match numbers that I need with regexp search in many editors, but how to apply some math to the replacement?

share|improve this question
1  
What operating system? Would you be happy with a shell script instead (which would be fairly easy)? Anyway, I'm sure vim and emacs can do this kind of thing, but it might require writing a script rather than using a single command. –  frabjous Sep 27 '10 at 18:20
    
@frabjous under windows. I would rather not write anything if possible. But if you post a shell script I am sure it will be useful to many. –  serg Sep 27 '10 at 18:46

4 Answers 4

up vote 13 down vote accepted

After a bit more poking around, it turns out vim can do it with a single command, without scripting. For example, to add 50 to all numbers following <obj param=" you could use:

:%s@<obj param="\(\d\+\)@\='<obj param="' . (submatch(1) + 50)@g

Let me break that down.

: is the general way to enter/signify command-line mode in vim.

% means within the scope of the whole document; you could put in a number range, e.g., 1,50 to just do it within the first 50 lines instead.

s is shorthand for substitute (you can write the whole word out if you prefer)

@ is the delimiter; you could use any other character so long as it's not in what you're searching for. Just use it three times. (The syntax is similar to sed.)

Everything up to the next occurrence of the delimiter @ is the reg ex pattern to search for, in this case <obj param=" followed by \d\+, which is any number of digits. The \( and \) are there to set this entire sequence of digits as a single reg ex group which would match backreferences like \1, or vim's submatch command.

Then the delimiter @ marks that what follows as the replacement text.

The \= at the beginning here means to substitute the result of some evaluated expression, rather than a reg ex pattern or string, which is key here.

Then we have '<obj param="' for the start of the replacement text. The . which follows is vim's function for concatenating strings.

submatch(1) is a vim built-in function which can be used only within a substitution command, and returns the string which is the same as the regex \1; you would use submatch(2) for the equivalent of regex's \2 and so on. (\0 is the whole matched pattern, but we don't want that here.) The \( and \) in the search pattern are used to mark off what counts as submatch(1).

Hence (submatch(1) + 50) gives the result of adding 50 to the number which the digits following <obj param=" in the search pattern form.

The delimiter @ is again used to mark off the end of the replacement text.

The flag g is used to make the substitutions global; you could omit this if you only wanted to replace the first instance on each line.

You can probably work out from there how to add different numbers, or subtract, or divide, etc.

share|improve this answer
    
Thanks a ton for this! –  Vijay Dev Apr 27 '11 at 11:38
    
This is great, I had a GPX file that was corrupt because the longitude values were utterly invalid. Luckily, latitude was OK so using a map I was able to work out what the first correct value should be, do some maths, and work out that I needed to subtract 4294.567548 from each value to get it right. The only stumbling block I had was that this method didn't work on a floating point number, so I just did each side of the decimal point as seperate substitutions, one -4294 and one -567548. –  stuffe Nov 8 at 21:23

You can use vim to do that for you. Just open your file and record a macro. Example: Search for any number

/[0-9]{1,}

then press q and a (store macro in register a). After that, press Ctrl-X (increasing the number by 1) and press n (for next search result) afterwards. After you have done that, press q again to save the macro. Now you can apply the macro to the next number by pressing @+a. This will change the current number and jumps to the next one. By repeating that or using x@a, you can repeat that x times.

Well, that description may not be sufficient to show how it can be done. Just refer to a tutorial describing the macro mechanism in vim.

share|improve this answer
    
What if I need to change the number not by 1 but by a million? Or maybe use some multiplication/division? –  serg Sep 27 '10 at 18:16
1  
It's easy if you want to add a constant x to every number: x+Ctrl+a. Do you need to add different numbers? –  evnu Sep 27 '10 at 22:02

The emacs editor could do it - or you could do it with a very simple python program.

share|improve this answer

In Emacs (since version 23): use \, to execute arbitrary Lisp code in regexp replacement. For example, to square the numbers you could use

M-x replace-regexp
param="\([0-9]+\)"
param="\,(* \#1 \#1)"

In Vim: start your replacement text with \= (see :help sub-replace-special). For example, to square the numbers:

s!param="\([0-9]\+\)"!\='param="'.submatch(1)*submatch(1).'"'!

Several editors allow you to do this kind of things with macros: define a macro that 1. looks for the next match and 2. performs the replacement (using an external tool for arithmetic if necessary); repeat the macro as many times as you have matches.

share|improve this answer
    
Underrated answer. Short and you cover 2 editors +general case. –  derekv May 30 '12 at 18:55

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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