MATLAB: Replacing numerics in text using regular expressions.

numerics in stringsregexpregexpreplaceregular expressions

Hello, I am trying to figure out whether it is possible to dynamically replace numeric values in a long text block using regular expressions. Here is an example from a made-up xml file.
str = '<document><placemark><when>5</when><lat>41</lat></placemark><placemark><when>11</when></placemark></document>';
Now, I want to perform some numerical function on all of the outputs of <when>, lets say subtract 3 so that the string will read
'<document><placemark><when>2</when><lat>41</lat></placemark><placemark><when>8</when></placemark></document>';
I can already find the locations using
exp='<when>(\d+)</when>'
but I don't know how to
  1. extract the actual numerical value at that location,
  2. perform some arbitrary function on that value (subtraction, addition, division, anything)
  3. write that new value back into the string so it reads <when>newValue</when>
If I was certain that the number of characters would stay the same, I could do a for-loop with some pretty gross indexing. However, as in the above example the length of the charstring representing the numeric value might change as a result of my function (11 became 8).
I suspect there is either a really elegant regexp solution, or it is not possible at all. Hoping for the former. Cheers, Dan

Best Answer

Here is a small trick assuming that no character in your string interferes with formatSpec stuff:
pattern = '(?<=<when>)\d+' ;
values = str2double( regexp( str, pattern, 'match' )) ;
values = values - 3 ; % Some operation.
fSpec = regexprep( str, pattern, '%d' ) ; % Make input str a formatSpec ;)
newStr = sprintf( fSpec, values ) ;
Got to run, I will answer more tonight if you don't get a better answer.
Ok, got 5 more minutes, otherwise a good alternative was mentioned by Walter and is based on the fact that you can run MATLAB code within the replacement pattern:
repFun = @(s) sprintf( '%d', sscanf( s,'%d' ) - 3 ) ; % Update function.
newStr = regexprep( str, '(?<=<when>)\d+', '${repFun($0)}' ) ;
Finally a more "classical" approach, that matches and splits the input string, replaces the matches and rebuilds the output.
[numbers, parts] = regexp( str, '(?<=<when>)\d+', 'match', 'split' ) ;
numbers = arrayfun( @(x) sprintf( '%d', x ), str2double( numbers ) - 3, ...
'UniformOutput', false ) ;
buffer = [parts; [numbers, {''}]] ;
newStr = sprintf( '%s', buffer{:} ) ;