2
$\begingroup$

For a string like

str = "This is a multi-line
string with
three lines total";

is there a way to search and filter it by lines so that

searchLines[str, "line"]

returns

This is a multi-line
three lines total

?

$\endgroup$
2
$\begingroup$

You could try something like:

ClearAll[searchLines];
searchLines[text_,search_]:=StringJoin@Riffle[StringCases[
    StringSplit[text,"\n"],
    ___~~search~~___
]/.{}->Nothing,"\n"];

Try on your case:

searchLines[str, "line"]
"This is a multi-line
three lines total"
|improve this answer|||||
$\endgroup$
  • $\begingroup$ Is there a way to add a paramater linesAround : NumberQ to the function that shows linesAround lines above and linesAround lines below the matching results? Sort of the like the -c option in grep? $\endgroup$ – George Mar 25 at 19:40
  • $\begingroup$ @George I have not looked at grep that much but can you give an explicit example of the output? $\endgroup$ – user13892 Mar 25 at 21:43
  • $\begingroup$ Sure. searchLines[str, "string", 1] on the str above should return str itself. $\endgroup$ – George Mar 25 at 21:51
1
$\begingroup$
str = "This is a multi-line
  string with
  three lines total";

fn = StringDelete["[]\n"]@*
   StringReplace[RegularExpression["(?m)^^((?!line).)*$$"] :> "[]"];

fn[str]

enter image description here

|improve this answer|||||
$\endgroup$
1
$\begingroup$

One approach is to use the "Lines" element for ImportString/ExportString.

filterLines[str_, patt_] :=
 ExportString[
  Select[
   ImportString[str, "Lines"],
   StringContainsQ[patt]],
  "Lines"]

filterLines[str, "line"]
(* 
"This is a multi-line
three lines total"
*)

Update

As Suba Thomas pointed out in comments, the ImportString/ExportString slows things down a lot. Using StringSplit and StringRiffle to perform the task is much faster:

filterLinesFaster[str_, patt_] :=
 StringRiffle[
  Select[StringSplit[str, "\n"], StringContainsQ[patt]],
  "\n"]    

First@RepeatedTiming[filterLines[str, "line"]]/
 First@RepeatedTiming[filterLinesFast[str, "line"]]
(* 9.*10^1 *)

However, when I tried this on a much longer string, the speed difference shrinks considerably:

SeedRandom[1337];
superStr =
  StringRiffle[
   RandomChoice[
    StringSplit[str, "\n"],
    {1000}],
   "\n"];

First@RepeatedTiming[filterLines[superStr, "line"]]/
 First@RepeatedTiming[filterLinesFast[superStr, "line"]]
(* 3.2 *)

Still slower than the "fast" solution (not to mention Suba's and user13892's approaches, which are faster still), but it's interesting how much it shrinks. I think ImportString and ExportString may set up temporary files or streams to work with, which may add a substantial constant time to the task.

Anyway, I've used the ImportString/ExportString pair a lot in my own code to turn strings to lists of lines and back, and, well, I won't do that anymore!

$\endgroup$
  • 1
    $\begingroup$ But it's rather slow? $\endgroup$ – Suba Thomas Mar 25 at 22:30
  • $\begingroup$ Is it? Didn't benchmark. $\endgroup$ – Pillsy Mar 26 at 11:07
  • 2
    $\begingroup$ Benchmarking with RepeatedTiming, shows this solution is ~150x slower than mine and ~120x slower than that of user13892. $\endgroup$ – Suba Thomas Mar 26 at 13:13
  • $\begingroup$ Thanks for going the extra mile and the additional analysis. $\endgroup$ – Suba Thomas Mar 26 at 15:50

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.