5
$\begingroup$

Suppose that I have a string str. str contains headings -- which are written in commented lines -- and data. For example:

str = "# Heading 1
# Heading 2
@ Heading 3
@ Heading 4
Data 1
Data 2
Data 3";

where # and @ denote commented lines.

Now, I would like to write a function removeComments to remove all lines in str that are commented. That is, I would like to remove all lines in str that start with the character # or the character @.

I have come up with one way to do this, I think. My procedure is to first split str into lines using StringSplit with the "\n" delimiter; then use DeleteCases to delete all lines in that list which begin with either # or @; and, finally, convert the list back to a string using Riffle and StringJoin. So, I have the following:

removeComments[str_String] := Module[{result},
  result = StringSplit[str, "\n"];
  result = 
   DeleteCases[
    result, _?(StringMatchQ[#, ("#" ~~ ___) | ("@" ~~ ___)] &)];
  result = StringJoin[Riffle[result, "\n"]];
  result
  ]
removeComments[str]

which gives the following correct output (a string):

Data 1
Data 2
Data 3

However, now suppose that one of my data items (for example, a single space in front of Data 2) has one or more leading spaces:

str = "# Heading 1
# Heading 2
@ Heading 3
@ Heading 4
Data 1
 Data 2
Data 3";

removeComments[str]

In this case, I get the incorrect output:

Data 1
Data 3

That is, Data 2 is omitted altogether. Something is clearly wrong with my approach, but I haven't been able to find my mistake (although it is probably obvious). Could you please help me find what is wrong? Thanks for your time.

$\endgroup$
6
  • $\begingroup$ Do you wish to keep the leading space before "Data 2" or do you wish to discard it? $\endgroup$
    – Mr.Wizard
    Aug 30 '13 at 21:50
  • $\begingroup$ @Mr.Wizard Sorry; I was not clear. I would like to keep the leading space before Data 2. $\endgroup$
    – Andrew
    Aug 30 '13 at 21:51
  • $\begingroup$ @Mr.Wizard No problem! :-) Thank you for your time and help. $\endgroup$
    – Andrew
    Aug 30 '13 at 22:31
  • $\begingroup$ Andrew, I notice that you didn't Accept my answer. Is that an oversight or do you find it lacking? Can I improve it? $\endgroup$
    – Mr.Wizard
    Sep 4 '13 at 11:21
  • $\begingroup$ @Mr.Wizard Sorry! It was just an oversight on my part; I have used your help but forgot to Accept. Your answer has been extremely helpful to me! Thank you! $\endgroup$
    – Andrew
    Sep 4 '13 at 13:07
9
$\begingroup$

Corrected answer

My original answer (see edit history) was not correct, unless all of your data lines are contiguous.

str = "# Heading 1\n# Heading 2\n@ Heading 3\n@ Heading 4\nData 1\n Data 2\nData 3\n@ \
Heading 5\nData 4";

StringReplace[str, Shortest[StartOfLine ~~ "@" | "#" ~~ ___ ~~ "\n"] :> ""]
"Data 1\n Data 2\nData 3\nData 4"

Or:

"" <> StringSplit[str, Shortest[StartOfLine ~~ "@" | "#" ~~ ___ ~~ "\n"]]
"Data 1\n Data 2\nData 3\nData 4"

And the RE equivalent:

First @ StringPattern`PatternConvert[Shortest[StartOfLine ~~ "@" | "#" ~~ ___ ~~ "\n"]]
"(?ms)^[@#].*?\n"
"" <> StringSplit[str, RegularExpression["(?ms)^[@#].*?\n"]]
"Data 1\n Data 2\nData 3\nData 4"
$\endgroup$
4
  • $\begingroup$ Thanks! Yes, this works as desired, in that all data is retained (including the leading space before Data 2). $\endgroup$
    – Andrew
    Aug 30 '13 at 21:55
  • 1
    $\begingroup$ @Andrew Okay, great. I added the RE equivalent if you enjoy such things. $\endgroup$
    – Mr.Wizard
    Aug 30 '13 at 21:56
  • 2
    $\begingroup$ Although it hasn't been specified clearly I think it might be more robust to use ___ instead of just __ to also discard empty comment lines, which otherwise well might cause problems when trying to interpret the remaining lines. The RE equivalent would be .* instead of .+ $\endgroup$ Sep 1 '13 at 11:38
  • $\begingroup$ @Albert Good point; I'll change it. $\endgroup$
    – Mr.Wizard
    Sep 1 '13 at 11:39
3
$\begingroup$

This answer only discusses the implied question of why the OP's code fails. I think this should be made clear because it puzzled me until Mr.Wizard and rm -rf enlightened me. I would like keep others from falling into the same trap.

I was studying the code posted in this question, trying to learn why it failed, when I ran into the following.

StringMatchQ[" ", "@"]

True

I found this very surprising and posted a question on it for a very short period. Mr.Wizard and rm -rf quickly pointed out that @ was a wild card character and that my code should be

StringMatchQ[" ", "\\@"]

False

This same mistake also occurs in the code posted in the question above. With the following minor change, the OP's code works.

removeComments[str_String] := 
  Module[{result},
    result = StringSplit[str, "\n"];
    result = 
      DeleteCases[result, _?(StringMatchQ[#, ("#" ~~ ___) | ("\\@" ~~ ___)] &)];
    StringJoin[Riffle[result, "\n"]]]

removeComments[
  "# Heading 1\n# Heading 2\n@ Heading 3\n@ Heading 4\nData 1\n Data 2\nData 3"]

Data 1
$\ $Data 2
Data 3

$\endgroup$
4
  • 1
    $\begingroup$ +1 for providing the other half of the answer; I enjoy problem solving more than debugging so I skipped this (important) part. $\endgroup$
    – Mr.Wizard
    Aug 30 '13 at 23:24
  • 1
    $\begingroup$ Incidentally the pattern could be condesed like this: StringMatchQ[#, "#" | "\\@" ~~ ___] $\endgroup$
    – Mr.Wizard
    Aug 31 '13 at 1:38
  • $\begingroup$ What is an rm -rf? $\endgroup$
    – Hector
    Aug 31 '13 at 1:57
  • $\begingroup$ @Hector. It's a kind of moderator :-) $\endgroup$
    – m_goldberg
    Aug 31 '13 at 1:59

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.