6
$\begingroup$

As explained in https://mathematica.stackexchange.com/a/72293/45020, using Shortest in StringCases does not actually return the shortest matching string. Instead it looks until some part matches (from left to right) and then it looks ahead and stops at the first complete match from that position. Of course this misses the true shortest string whenever this first match is not part of the shortest string. See for example

StringCases["This is not what I'm looking for. This is part of the 
    shortest match", Shortest["This is" ~~ any__ ~~ "match"]]

where the whole string gets matched while a stronger string match is available.

I need the reverse behavior. Where one searches from right to left (Alternatively I need to find the "true" Shortest substring, that would also work for my application).

I have a StringExpression that describes some pattern, say "out ["~~x: NumberString~~"]", which at some point is preceded by some strong "some_string" from which I want to extract the string between the end of the regular pattern and the closest preceding instance of "some_string". So for example

reverseShortestCase["some_string other stuff I don't want. some_string, the part I do want, out [3.1]", "some_string", "out ["~~x: NumberString~~"]"] 

should return "some_string, the part I do want, out [3.1]".

How should I go about doing this? I guess I can try to reverse the string and the patterns in order to search right to left? But this seems complicated if you want to be able to use things like NumberString.

The solutions proposed in the linked question do not seem to offer a solution for this case where there is no fixed begin and end string and instead a StringExpression needs to be used to describe these.

$\endgroup$
4
  • 2
    $\begingroup$ In your first example, what do you mean by "misses the true shortest string"? That StringCases call returns two matches to the input, which seems like the correct behavior. $\endgroup$ – Jason B. Mar 15 at 17:55
  • $\begingroup$ @Jason B. I wonder if the OP has seen that the StringCases returns a list of 2 strings, (and not the whole initial string) $\endgroup$ – andre314 Mar 15 at 17:58
  • $\begingroup$ @andre314 - or maybe a better example of their problem is StringCases["This is not what I'm looking for. This is part of the shortest match", Shortest["This is" ~~ any__ ~~ "match"]] which only has one match, and is not the intended match. $\endgroup$ – Jason B. Mar 15 at 18:00
  • $\begingroup$ So sorry, thoughtlessly altered the example for an aesthetics reason after writing (and testing it) accidentally destroying the whole point. $\endgroup$ – Kvothe Mar 15 at 18:34
5
$\begingroup$

I would generally prefer an approach that retains the ability to apply replacement rules (e.g. Jason B's ) but... another approach would be to find all matches using Overlaps->True and to then pick out the shortest one afterwards:

$string = "some_string other stuff I don't want. some_string, the part I do want, out [3.1]";
$pattern = "some_string"~~___~~"out ["~~x:NumberString~~"]";

StringCases[$string, $pattern, Overlaps -> True] // MinimalBy[StringLength]

(* {"some_string, the part I do want, out [3.1]"} *)

This brute-force approach might have some appeal if, for example, there were the possibility of multiple shortest matches and we wanted to apply complex logic pick one out or otherwise process the hits.

$\endgroup$
3
$\begingroup$

You can specify that the trigger "some_string" only occurs at the beginning of the pattern, and not in the middle:

In[49]:= 
pattern = 
  "some_string" ~~ Shortest[in__ /; StringFreeQ[in, "some_string"]] ~~
    "out [" ~~ NumberString ~~ "]";

In[50]:= StringCases["some_string other stuff I don't want. some_string, the part I do want, out [3.1]", pattern]

Out[50]= {"some_string, the part I do want, out [3.1]"}
```
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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