3
$\begingroup$

I have a problem with a list of strings where I want to change the first integer occurrence in each entry. The lines all look like this:

"    1ICE     MW    1   0.138   0.181   0.348"    

I am interested in changing the integer after the MW, namely adding 3 to it, but leaving the white spaces intact, like this:

 "    1ICE     MW    4   0.138   0.181   0.348" 

Because I want to leave the white spaces intact, I cannot simply Stringsplit, and because not every entry looks the same (the number before ICE increases, and the amount of white spaces then can change) I cannot just simply replace the "1" with "4". E.g. other entries could look like

"  128ICE     MW  509   0.916   0.921   1.697"

So they would have a different amount of whitespace before the integer I want to change and at the start. Is there a way to do this with some kind of pattern recognition?

$\endgroup$
4
  • $\begingroup$ It is not the first integer, what about 1ICE? $\endgroup$ Commented Nov 21, 2017 at 13:21
  • 4
    $\begingroup$ ClearAll[f]; f = StringReplace[#, (ws1 : Whitespace ~~ n : NumberString ~~ ws2 : Whitespace) /; IntegerQ[ToExpression[n]] :> StringJoin[ws1, ToString[3 + ToExpression[n]], ws2]] &;? $\endgroup$ Commented Nov 21, 2017 at 13:43
  • $\begingroup$ @Kuba sorry for the misunderstanding, I meant the first integer occurence that is only surrounded by whitespace (and not immediately followed by letters). $\endgroup$ Commented Nov 21, 2017 at 13:52
  • $\begingroup$ @kglr thanks a lot, that seems to work :) (it only produces a small error, namely that if it is applied to a case where the number I want to shift changes to get an additional digit, e.g. 9 -> 12, I still retain the additional whitespace that would not be needed in this file format for 2-digit numbers) But I can fix that manually :) $\endgroup$ Commented Nov 21, 2017 at 13:58

2 Answers 2

3
$\begingroup$

First I will answer what has not been asked. This assumes that the format of the incoming data is known. List processing functions allow effective manipulation of such data. There is a ColumnSpans option to further facilitate this interpretation.

str = "    23ICE     MW    1   0.138   2   0.348";

Normal@SemanticImportString[
  str, {"String", "String", "Number", "Number", "Number", "Number"}]


{{"23ICE", "MW", 1, 0.138, 2, 0.348}}

Here is a step-by-step string-manipulation solution.

str = "    23ICE     MW    1   0.138   2   0.348";
pholders = 
 StringReplace[str, (WordCharacter | PunctuationCharacter) .. :> "x"];
pos = StringPosition[pholders, "x"];
v = MapAt[ToString[ToExpression@# + 4] &, 
  t = StringSplit[str, Whitespace], 
  Position[t, _?(IntegerQ@*ToExpression)]];
StringReplacePart[pholders, v, pos]

"    23ICE     MW    5   0.138   6   0.348"

Explanations:

1- Each contiguous cluster of non Whitespace in the string is replaced by x as placeholders.

2- The position of each placeholder is recorded.

3- The string is split at Whitespace and the task is MapedAt the desired locations. In this instance, all integers are augmented by 4. This can be modified easily.

4- StringReplacePart puts the new calculated values into place holders.

5- Whitespace is retained.

$\endgroup$
3
$\begingroup$

Using StringSplit and ReplacePart:

 str = "    23ICE     MW    1   0.138   2   0.348";
 With[{ds = StringSplit[str, s : Whitespace :> s]},
 p = Select[DigitQ]@ds;
 rep = ToString /@ (ToExpression[p] + 4);
 pos = Position[ds, Alternatives @@ p];
 StringJoin@ReplacePart[ds, Thread[pos -> rep]]
 ]
"    23ICE     MW    5   0.138   6   0.348"

If you only want the first occurrence, you proceed as follows:

With[{ds = StringSplit[str, s : Whitespace :> s]},
 p = FirstCase[_?DigitQ]@ds;
 rep = ToString@(ToExpression[p] + 4);
 pos = Position[ds, p];
 StringJoin@ReplacePart[ds, pos -> rep]
 ]
"    23ICE     MW    5   0.138   2   0.348"

Or using StringExtract and MapAt:

With[{ds = StringSplit[str, s : Whitespace :> s], n = 3},
StringJoin@MapAt[ToString[ToExpression[#] + n] &, #1, #2] & @@ 
{ds, FirstPosition[#, {_}]} &@
StringExtract[#, Except[DigitCharacter] .. -> All] &@ds
]
"    23ICE     MW    5   0.138   2   0.348"
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.