Use named Pattern
to extract a part of matched substring:
StringCases[#, str : Shortest[StartOfString ~~ ___] ~~ "!" :> str] & /@ s
{{{"This is small string 2 "}}, {{"There is string 5 "}}, {{"This is String n "}}}
or
StringCases[#, str : Shortest[StartOfString ~~ ___] ~~ "!" :> str] &@Flatten@ s
{{"This is small string 2 "}, {"There is string 5 "}, {"This is String n "}}
As Martin notes in the comments, another approach is to capture Longest
sequence of characters from a negated character class. In this case we don't have to use a named pattern which introduce additional overhead, hence this approach should be more efficient. Since string patterns by default are greedy, Longest
can be omitted in this case:
StringCases[#, StartOfString ~~ Except["!"] ...] &
Translating StringExpression
s into equivalent regular expressions sometimes gives substantial increase in performance: this is what Mathematica always does under the hood, but not always in the optimal way. Here is verbatim semantic translation:
StringCases[#, RegularExpression["^[^!]*"]] &
(read here on how to find out what a regex Mathematica generates from a StringExpression
).
And another regex without capturing group and without use of a negated character class:
StringCases[#, RegularExpression["^.*?(?=!)"]] &
Note that this last regex can't be expressed as a combination of usual Wolfram Language patterns because it uses a positive lookahead zero-length assertion which has no equivalent in the world of Wolfram Language symbolic pattern objects.
Performance comparison
Here is a timing comparison of the all suggested solutions (on a very large number of short strings):
$HistoryLength = 0;
s1 = ConstantArray["There is string 5 ! Or is it 2?", 2*10^5];
{First@AbsoluteTiming[# /@ s1], #} & /@ {
StringCases[#, str : Shortest[StartOfString ~~ ___] ~~ "!" :> str] &,
StringCases[#, RegularExpression["^(.*?)!"] -> "$1"] &,
StringCases[#, StartOfString ~~ Except["!"] ...] &,
StringCases[#, RegularExpression["^[^!]*"]] &,
StringCases[#, RegularExpression["^.*?(?=!)"]] &,
StringDelete[#, "!" ~~ ___] &, StringExtract[#, "!" -> 1] &,
StringTake[#, StringPosition[#, "!", 1][[1, 1]] - 1] &,
StringDrop[#, {StringPosition[#, "!", 1][[1, 1]], -1}] &} //
Grid[#, Frame -> All, Alignment -> Left, FrameStyle -> Directive[Thin, LightGray]] &
MaxMemoryUsed[]
102886352
Another comparison suggested by Martin (on a string where it takes a while to find the "!"
):
$HistoryLength = 0;
s2 = StringRepeat["There is string 5 ", 10^5] <> "! Or is it 2?";
results = {};
{(AppendTo[results, #2]; #1) & @@ AbsoluteTiming[#@s2], #} & /@ {
StringCases[#, str : Shortest[StartOfString ~~ ___] ~~ "!" :> str][[1]] &,
StringCases[#, RegularExpression["^(.*?)!"] -> "$1"][[1]] &,
StringCases[#, StartOfString ~~ Except["!"] ...][[1]] &,
StringCases[#, RegularExpression["^[^!]*"]][[1]] &,
StringCases[#, RegularExpression["^.*?(?=!)"]][[1]] &,
StringDelete[#, "!" ~~ ___] &,
StringExtract[#, "!" -> 1] &,
StringTake[#, StringPosition[#, "!", 1][[1, 1]] - 1] &,
StringDrop[#, {StringPosition[#, "!", 1][[1, 1]], -1}] &} //
Grid[#, Frame -> All, Alignment -> Left, FrameStyle -> Directive[Thin, LightGray]] &
SameQ @@ results
MaxMemoryUsed[]
True
82707472
The conclusion: Martin's solution via negated character class with greedy quantifier outperforms others in general: RegularExpression["^[^!]*"]
. See this dedicated Q&A about why the equivalent string expression StartOfString ~~ Except["!"] ...
is two orders of magnitude slower.