3
\$\begingroup\$

This was developed in response to a question on Stack Overflow, Removing comments using regex.

What the OP needed was a regular expression to use in C# to remove comnents from lines of a file. All comments were specified as beginning with a double slash // and everything after that is ignored. Comments may also begin at the start of a line, resulting in the line being blank. Quotes, double or single, when properly closed, will shield any double slashes from triggering a comment. There was no indication that the target file was a C# source file, only that the processing file was C#.

Taking advantage of the C# environment given by the problem enabled the use of conditional matching. These constructs are, to my knowledge, only supported in the .NET and PCRE engines, so were acceptable to the OP's use case, but not in many other cases.

When used the pattern will return the non-comment portion of the line(s). It will return a newline for lines of code that are either blank, or have a comment beginning in the first column. This keeps the line numbers the same in the original version and the stipped version if the results are written to a new file. Makes comparison easier.

The patterns given here are as they need to be seen by the regex engine. I am not a C# programmer, so I don't know all the nuances of escaping quoted strings. Getting the pattern into the code, such that all the backslashes and quotes are seen properly by the regex engine was explicitly left to the OP.

The pattern was built and tested under the ignore white-space adn multi-line options, rather than the other way around, so the commented version is the original and the single line version was created from it. The comments were intended to aid the OP in understanding the pattern, so they are rather verbose.

In my testing it passed every pathological test I could throw at it, and I believe it is sound.

The test file I created is:

variable = "5 // \ \\"not // here " variable = 6 / /3 + '7 // \"\' not // here ' and then some //real comment
FullLine here
;Second Line here
;third line here
";fourth line here"
Fifth Line here /* comment */
Sixth line here /* comment */ syntax error?
variable = "5 // \\ \"not // here " variable = 6 //3 + '7 // \"\' not // here ' and then some //real comment
variable = "5 // \\ \"not // here " variable = 6 / /3 + '7 // \"\' not // here ' and then some //real comment
variable = "some//thing" and "some\" thing" ////actual comment
a    //// some comment
d /// other comment
/*** one-liner, c-style block comment  ***/
variable = 'some//thing' and "some\" thing" ////actual comment
c #// later comment
/******
* c-style block comment two
* Multi-line -1 //extra non-comment
* Multi-line 2  # extra non-comment
*******/
variable = "some thing" and "some \\ thing else" ////actual comment
    /// some comment
/******
* c-style block comment two
* Multi-line -3 //extra non-comment
* Multi-line 4  # extra non-comment
*******/ more syntax errors?
variable = "some \"thing" ////actual comment
// other comment
variable = 'some//thing' ////actual comment
// later comment
variable = 'some thing' ////actual comment
//last comment
"qwe\" qwe" \\asd
variable = 'some \'thing' ////actual comment
Variable = 4 no comment
Variable = 45 no comment /* is this a c-legal comment */
Variable = 46 no comment /* is this a c-legal comment */
variable = "5 // not here "
variable = "5 // \\ \"not // here " variable = 6 / /3 + '7 // \"\' not // here ' and then some //real comment //nestes

The single-line version is:

^((?:(?:(?:[^"'/\n]|/(?!/))*)(?("(?=(?:\\\\|\\"|[^"])*"))(?:"(?:\\\\|\\"|[^"])*")|(?('(?=(?:\\\\|\\'|[^'])*'))(?:'(?:\\\\|\\'|[^'])*')|(?(/)|.))))*)

The commented version is:

^( # Start the only capturing group
    (?: # A non-capturing group to allow for repeating the logic
        (?: # Capture either of the two options below
            [^"'/\n] # Capture everything not a single quote, double quote, a slash, or a newline
            | # OR
            /(?!/) # Capture a slash not followed by a slash [slash an negative look-ahead slash]
        )* # As many times as possible, even if none
        (?(" # Start a conditional match for double-quoted strings
                (?=(?:\\\\|\\"|[^"])*") # Followed by a properly closed double-quoted string
            ) # Then
            (?:"(?:\\\\|\\"|[^"])*") # Capture the whole double-quoted string
            | # Otherwise
            (?(' # Start a conditional match for single-quoted strings
                (?=(?:\\\\|\\'|[^'])*') # Followed by a properly closed single-quoted string
                ) # Then
                (?:'(?:\\\\|\\'|[^'])*') # Capture the whole double-quoted string
                | # Otherwise
                (?([^/]) # If next character is not a slash
                .) # Capture that character, it is either a single quote, or a double quote not part of a properly closed
            ) # end the conditional match for single-quoted strings
        ) # End the conditional match for double-quoted strings
    )* # Close the repeating non-capturing group, capturing as many times as possible, even if none
) # Close the only capturing group

I'm looking to see if there is some optimisation possible, of some trap I've fallen into that will create either a false postive, or miss a match that should be made. Not that my test file includes comments in the /* ... */ style, and they are not stripped by the pattern, nor did I want them to be, as the OP made no mention of doing so.

\$\endgroup\$
  • 1
    \$\begingroup\$ It's a perl faq: perldoc.perl.org/… \$\endgroup\$ – Toto Jul 7 '17 at 7:47
  • \$\begingroup\$ @Toto That pattern is aimed at the /*....*/ (which are not targeted, as per the OP's problem) style comments. When used on the test file and targeting // style comments, it fails so miserably as to be pointless. \$\endgroup\$ – Gypsy Spellweaver Jul 7 '17 at 23:42
  • \$\begingroup\$ Some people, when confronted with a problem, think “I know, I'll use regular expressions.” Now they have two problems. \$\endgroup\$ – P. Roe Jun 18 '18 at 16:48
  • \$\begingroup\$ @P.Roe So said Jamie Zawinski, supposedly. Sometimes considered as an axiom, by those who don't do well with regex. Sometimes considered a reminder to examine other tools when considering that one. Perhaps, when all you have is a hammer, everything looks like a nail. See more in this blog. In this case, the tool and the problem were defined, and only the wielding of that tool was was my work. \$\endgroup\$ – Gypsy Spellweaver Jun 19 '18 at 8:29

Your Answer

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

Browse other questions tagged or ask your own question.