9
$\begingroup$

I have a long string list:

string = {"E:\\job\\a\\000251.png", "E:\\job\\a\\000252.png",
  "E:\\job\\a\\000253.png", "E:\\job\\a\\000254.png",
  "E:\\job\\a\\000255.png", "E:\\job\\a\\000256.png"}

Now, I want to add 2 to the file name base when the file base name is an even number. I mean, I hope to get such new string list:

string = {"E:\\job\\a\\000251.png", "E:\\job\\a\\000254.png",
  "E:\\job\\a\\000253.png", "E:\\job\\a\\000256.png",
  "E:\\job\\a\\000255.png", "E:\\job\\a\\000258.png"}

How to implement it?

$\endgroup$

4 Answers 4

4
$\begingroup$

It seams that there has not been an answer involved with RegularExpression yet, which is quite efficient when dealing with patterns of strings.

StringReplace[string, x : RegularExpression["\\d*[02468]\\."] :> ToString[NumberForm[ToExpression[x] + 2, 5, NumberPadding -> "0"]]]
{"E:\\job\\a\\000251.png",
 "E:\\job\\a\\000254.png",
 "E:\\job\\a\\000253.png",
 "E:\\job\\a\\000256.png",
 "E:\\job\\a\\000255.png", 
 "E:\\job\\a\\000258.png"}

Update

As @yode pointed out in the comment, IntegerString is better here on the right-hand side of :>.

$\endgroup$
8
  • $\begingroup$ Doesn't work for "E:\\job\\a\\000298.png". $\endgroup$
    – Carl Woll
    Commented Sep 15, 2018 at 5:08
  • $\begingroup$ @CarlWoll How about now? $\endgroup$ Commented Sep 15, 2018 at 5:11
  • $\begingroup$ Now it doesn't work for "E:\\job\\a\\000998.png". $\endgroup$
    – Carl Woll
    Commented Sep 15, 2018 at 5:14
  • $\begingroup$ @CarlWoll ah, your are right $\endgroup$ Commented Sep 15, 2018 at 5:17
  • 2
    $\begingroup$ StringReplace[string, x : RegularExpression["\\d*[02468](?=\\.)"] :> IntegerString[ToExpression[x] + 2, 10, 6]] is more concise.. $\endgroup$
    – yode
    Commented Sep 15, 2018 at 11:06
7
$\begingroup$

You could create a helper function (the point of the helper function is to avoid calling FromDigits twice):

incString[s_] := With[{r = FromDigits[s]},
    If[EvenQ[r],
        IntegerString[r+2, 10, StringLength[s]],
        s
    ]
]

and then use this helper function in StringReplace:

StringReplace[
    string,
    i:DigitCharacter..~~".png" :> incString[i]<>".png"
]

{"E:\job\a\000251.png", "E:\job\a\000254.png", \ "E:\job\a\000253.png", "E:\job\a\000256.png", "E:\job\a\000255.png", \ "E:\job\a\000258.png"}

$\endgroup$
4
$\begingroup$
StringReplace[string, 
 a : NumberString ~~ "." /; EvenQ[FromDigits[a]] :> 
   StringPadLeft[IntegerString[FromDigits[a] + 2], StringLength@a, "0"] ~~ "."] 

{"E:\job\a\000251.png",
"E:\job\a\000254.png",
"E:\job\a\000253.png",
"E:\job\a\000256.png",
"E:\job\a\000255.png",
"E:\job\a\000258.png"}

Borrowing the three-argument IntegerString idea from Carl's answer with an alternative replacement rule:

StringReplace[string, a : NumberString ~~ "." :> With[{b = FromDigits @ a}, 
   IntegerString[b + 2 (1 - Mod[b, 2]), 10, StringLength @ a]] ~~ "."]

same result

$\endgroup$
3
  • $\begingroup$ Maybe more concise StringReplace[string, a : DigitCharacter ~~ "." /; EvenQ[FromDigits[a]] :> StringJoin[ToString[FromDigits[a] + 2]] ~~ "."] $\endgroup$
    – yode
    Commented Sep 14, 2018 at 20:23
  • $\begingroup$ see here please. $\endgroup$
    – yode
    Commented Sep 14, 2018 at 20:27
  • $\begingroup$ @yode, if you don't have elements like "E:\job\a\000258.png" in your string list your more concise version would work too. $\endgroup$
    – kglr
    Commented Sep 14, 2018 at 20:53
3
$\begingroup$

Here is an approach without StringReplace. It is one function f that first destructures the file-name and has two more definitions: one for files with an even number and one for all others.

ClearAll[f]
f[str_String] := f[{str, DirectoryName[str], FileBaseName[str], FileExtension[str]}];

f[{str_, dir_, name_, ext_} /; ToExpression[name, InputForm, EvenQ]] :=
   With[{
     num = IntegerString[FromDigits[name] + 2, 10, StringLength[name]]
   }, FileNameJoin[{dir, num <> "." <> ext}]];

f[{str_, __}] := str

Now better test it properly since my last version was completely screwed up. I need to make Linux filenames from your strings since otherwise, Mathematica's file functions don't work here. On your Windows machine, this should not be necessary.

string = {"E:\\job\\a\\000251.png", "E:\\job\\a\\000252.png", 
  "E:\\job\\a\\000253.png", "E:\\job\\a\\000254.png", 
  "E:\\job\\a\\000255.png", "E:\\job\\a\\000256.png"};
string = StringReplace[string, {"E:\\" :> "/", "\\" :> "/"}]

f/@string//Column
(* 
/job/a/000251.png
/job/a/000254.png
/job/a/000253.png
/job/a/000256.png
/job/a/000255.png
/job/a/000258.png 
*)
$\endgroup$
8
  • $\begingroup$ I have the greatest admiration for your contributions. I am trying to follow your answer but I believe there is an error in the second definition of f. num is defined as a local constant in With but never used in the output. I would also appreciate it if you would show how to use f for the given example. $\endgroup$ Commented Sep 14, 2018 at 23:16
  • $\begingroup$ @JackLaVigne Oh my goodness. There was so much wrong in my code because I didn't clear f for testing. Thank you for paying attention! Would you test if it works on windows with the original definition of strings? $\endgroup$
    – halirutan
    Commented Sep 14, 2018 at 23:28
  • $\begingroup$ Works fine in Windows. I was thinking I had to Map the function f`` over string`. Thank you for the update and example. It is clear to me now. $\endgroup$ Commented Sep 14, 2018 at 23:32
  • $\begingroup$ It doesn't work for "/job/a/000258.png" where f returns "/job/a/0002510.png". $\endgroup$
    – Carl Woll
    Commented Sep 14, 2018 at 23:35
  • $\begingroup$ @CarlWoll If you are on Windows, then this seems to be expected. Filename separators are OS specific and Mathematica's functions don't work if you are on Windows and try Unix files. $\endgroup$
    – halirutan
    Commented Sep 14, 2018 at 23:37

Your Answer

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

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