Any text editor that can apply some math to replacement text?

15

12

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?

serg

Posted 2010-09-27T16:54:21.650

Reputation: 1 160

1What 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 – 2010-09-27T18:20:46.077

@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 – 2010-09-27T18:46:19.910

Answers

18

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.

frabjous

Posted 2010-09-27T16:54:21.650

Reputation: 9 044

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 – 2014-11-08T21:23:10.703

7

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.

Gilles 'SO- stop being evil'

Posted 2010-09-27T16:54:21.650

Reputation: 58 319

Underrated answer. Short and you cover 2 editors +general case. – derekv – 2012-05-30T18:55:11.810

1

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.

evnu

Posted 2010-09-27T16:54:21.650

Reputation: 445

What if I need to change the number not by 1 but by a million? Or maybe use some multiplication/division? – serg – 2010-09-27T18:16:27.927

1It's easy if you want to add a constant x to every number: x+Ctrl+a. Do you need to add different numbers? – evnu – 2010-09-27T22:02:41.270