20
\$\begingroup\$

Goal

Using the programming language of your choice, write the shortest program to eliminate comments from a string representing a C program.


Input

The string can be taken as any form of input, but it may also be taken as a variable.


Instructions

Two different kinds of comments are to be removed:

  • multiline comments, starting with /* and ending with */
  • single line comments, starting with // and ending with Linux-style line breaks (LF, \n)

Comments within strings are not to be deleted. For the purpose of this challenge, you only need to consider "-delimited strings. In particular, you can ignore the possibility of '-delimited character literals. You may also ignore trigraphs and line continuations (/\<LF>*...).


Examples

Input:

#include <stdio.h>

int main(int argc, char** argv)
{
    // this comment will be removed
    if (argc > 1) {
        printf("Too many arguments.\n");   // this too will be removed
        return 1;
    }
    printf("Please vist http://this.will.not.be.removed.com\n");
    printf("/* This will stay */\n");
    printf("\"/* This will stay too */\"\n");
    printf("//and so will this\\");
    // but not this
    printf("just \"ano//ther\" test.");
    return 0;
}

Output:

#include <stdio.h>

int main(int argc, char** argv)
{

    if (argc > 1) {
        printf("Too many arguments.\n");   
        return 1;
    }
    printf("Please vist http://this.will.not.be.removed.com\n");
    printf("/* This will stay */\n");
    printf("\"/* This will stay too */\"\n");
    printf("//and so will this\\");

    printf("just \"ano//ther\" test.");
    return 0;
}

Input:

/*
    this shall disappear
*/
#include <string>
int main(int argc, char** argv)
{
    string foo = ""/*remove that!**/;
    // Remove /* this
    int butNotThis = 42;
    // But do */ remove this
    int bar = 4 /*remove this*/* 3; // but don't remove that 3. */
    return 0;//just a comment
}/*end of the file has been reached.*/

Output:

#include <string>
int main(int argc, char** argv)
{
    string foo = "";

    int butNotThis = 42;

    int bar = 4 * 3; 
    return 0;
}
\$\endgroup\$
10
  • 1
    \$\begingroup\$ From where that printf("\"/* This will stay too */\"\n"); appeared in the should become code? \$\endgroup\$
    – manatwork
    Apr 1, 2015 at 11:00
  • \$\begingroup\$ Oops, sorry... it was just a typo. Thanks for noticing! \$\endgroup\$ Apr 1, 2015 at 11:05
  • \$\begingroup\$ Do whitespaces count? There are 4 spaces in front of // this comment will be removed which just disappeared. Any rule for that? \$\endgroup\$
    – manatwork
    Apr 1, 2015 at 11:08
  • 1
    \$\begingroup\$ I don't know any of the listed languages that well, so some kind of a self-contained spec would be nice, together with more examples. \$\endgroup\$
    – Zgarb
    Apr 1, 2015 at 13:03
  • \$\begingroup\$ @manatwork: whitespace removal is not mandatory \$\endgroup\$ Apr 1, 2015 at 13:03

8 Answers 8

15
\$\begingroup\$

Shell + coreutils + gcc compiler collection, 31 bytes

This answer may seem a bit loopholey, but I didn't see anything specifically banning it in the question.

Rather than using clumsy regular expressions, why not use the tool that was built for the job. It should have no problem giving correct results:

cpp -fpreprocessed -o- -|sed 1d

Takes input from STDIN and output to STDOUT. Normally ccp will do all preprocessing (header files, macro expansion, comment removal, etc), but with the -fpreprocessed option, it will skip most of the steps, but it will still remove comments. In addition, cpp adds a line like # 1 "<stdin>" to the beginning of the output, so the sed is there to delete it.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ "-fpreprocessed is implicit if the input file has one of the extensions .i, .ii or .mi". might you be able to save some bytes by saving the file in something like a.i instead of using the flag? \$\endgroup\$ Apr 1, 2015 at 20:20
  • \$\begingroup\$ @MartinBüttner Yes, I noticed that in the manual too. So I would expect something like cat>i.i;cpp -o- i.i|sed 1d to be equivalent. But full preprocessing ensues (e.g. full contents of stdio.h are inserted). Possible gcc bug??? Well perhaps I'll check the cpp source when I get a mo'. \$\endgroup\$ Apr 1, 2015 at 20:41
  • \$\begingroup\$ You can remove the |sed 1d if you add the -P option. Note that (as allowed by the question), as it expects pre-processed code, it won't handle trigraphs or line continuations properly. \$\endgroup\$
    – sch
    Jul 22, 2016 at 6:48
  • \$\begingroup\$ I am getting problems with this code. It spews out "# 234" with varying numbers after the hashtag, here and there. \$\endgroup\$
    – john-jones
    Mar 31, 2020 at 21:11
11
\$\begingroup\$

Retina, 35 + 1 + 2 = 38 bytes

This program consists of two files, hence I've included a 1-byte penalty for the second file.

//.*|/\*[\s\S]*?\*/|("(\\.|[^"])*")
$1

This is a simple regex replacement, using the .NET flavour (although this would work the same in most other flavours).

The idea is to match both comments and strings, but only write the match back if it was a string. By matching the strings explicitly, they are skipped when searching for comments.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ This works surprisingly well in PHP: regex101.com/r/kB5kA4/1 \$\endgroup\$ Apr 1, 2015 at 14:47
  • 1
    \$\begingroup\$ @IsmaelMiguel Yes, I didn't use anything feature specific. The only reason I picked .NET is because Retina allows me to write regex-only programs without any overhead of calling something like preg_replace. \$\endgroup\$ Apr 1, 2015 at 15:01
  • \$\begingroup\$ I'm aware of that. You've used it quite a lot before. If I'm correct, it was created by you. It was for the curious. And also, you now have a test-suite where you can test whatever changes come into this question (I predict many) \$\endgroup\$ Apr 1, 2015 at 15:10
  • \$\begingroup\$ Nice! This regular expression even works with other programming languages (when slashes are escaped). \$\endgroup\$ Apr 2, 2015 at 11:46
  • \$\begingroup\$ I used your regex technique to improve a third party library I work with: Dojo Toolkit \$\endgroup\$
    – mbomb007
    Aug 9, 2018 at 13:57
3
\$\begingroup\$

Java 365

String a(String s){String o="";int m=1;for(int i=0;i<s.length();i++){String u=s.substring(i,Math.min(i+2,s.length()));char c=s.charAt(i);switch(m){case 1:m=u.equals("/*")?5:u.equals("//")?4:c=='"'?3:1;break;case 3:m=c=='"'?1:c=='\\'?2:3;break;case 2:m=3;break;case 4:m=c=='\n'?1:4;continue;case 5:m=u.equals("*/")?1:5;i+=m==1?1:0;continue;}o+=m<4?c:"";}return o;}}

Ungolfed

public static final int DEFAULT = 1;
public static final int ESCAPE = 2;
public static final int STRING = 3;
public static final int ONE_LINE_COMMENT = 4;
public static final int MULTI_LINE_COMMENT = 5;

String clear(String s) {
    String out = "";
    int mod = DEFAULT;
    for (int i = 0; i < s.length(); i++) {
        String substring = s.substring(i, Math.min(i + 2 , s.length()));
        char c = s.charAt(i);
        switch (mod) {
            case DEFAULT: // default
                mod = substring.equals("/*") ? MULTI_LINE_COMMENT : substring.equals("//") ? ONE_LINE_COMMENT : c == '"' ? STRING : DEFAULT;
                break;
            case STRING: // string
                mod = c == '"' ? DEFAULT : c == '\\' ? ESCAPE : STRING;
                break;
            case ESCAPE: // string
                mod = STRING;
                break;
            case ONE_LINE_COMMENT: // one line comment
                mod = c == '\n' ? DEFAULT : ONE_LINE_COMMENT;
                continue;
            case MULTI_LINE_COMMENT: // multi line comment
                mod = substring.equals("*/") ? DEFAULT : MULTI_LINE_COMMENT;
                i += mod == DEFAULT ? 1 : 0;
                continue;
        }
        out += mod < 4 ? c : "";
    }

    return out;
}
\$\endgroup\$
0
2
\$\begingroup\$

Python2 - 163 134 bytes

import re
def f(s):
 for x in re.findall(r'("[^\n]*"(?!\\))|(//[^\n]*$|/(?!\\)\*[\s\S]*?\*(?!\\)/)',s,8):s=s.replace(x[1],'')
 print s

As you can see here, the regex consists of 2 alternating capturing groups. The first one captures all the quoted strings. The second one all the comments.

All we need to do, is removing everything captured by the 2nd group.

Example:

Python 2.7.9 (default, Dec 11 2014, 04:42:00) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> def f(s):
...  for x in re.findall(r'("[^\n]*"(?!\\))|(//[^\n]*$|/(?!\\)\*[\s\S]*?\*(?!\\)/)',s,8):s=s.replace(x[1],'')
...  print s
... 
>>> code = r'''#include <stdio.h>
... 
... int main(int argc, char** argv)
... {
...     // this comment will be removed
...     if (argc > 1) {
...         printf("Too many arguments.\n");   // this too will be removed
...         return 1;
...     }
...     printf("Please vist http://this.will.not.be.removed.com\n");
...     printf("/* This will stay */\n");
...     printf("\"/* This will stay too */\"\n");
...     printf("//and so will this\\");
...     // but not this
...     printf("just \"ano//ther\" test.");
...     return 0;
... }
... /*
...     this shall disappear
... */
... #include <string>
... int main(int argc, char** argv)
... {
...     string foo = ""/*remove that!**/;
...     // Remove /* this
...     int butNotThis = 42;
...     // But do */ remove this
...     int bar = 4 /*remove this*/* 3; // but don't remove that 3. */
...     return 0;//just a comment
... }/*end of the file has been reached.*/'''
>>> f(code)
#include <stdio.h>

int main(int argc, char** argv)
{

    if (argc > 1) {
        printf("Too many arguments.\n");   
        return 1;
    }
    printf("Please vist http://this.will.not.be.removed.com\n");
    printf("/* This will stay */\n");
    printf("\"/* This will stay too */\"\n");
    printf("//and so will this\\");

    printf("just \"ano//ther\" test.");
    return 0;
}

#include <string>
int main(int argc, char** argv)
{
    string foo = "";

    int butNotThis = 42;

    int bar = 4 * 3; 
    return 0;
}
\$\endgroup\$
1
\$\begingroup\$

Rebol - 151

f: func[t][Q:{"}W: complement charset Q parse t[any[[Q any["\\"|"\"Q | W]Q]|[a:[["//"to[lf | end]]|["/*"thru"*/"]]b:(remove/part a b):a skip]| skip]]t]

Ungolfed + some annotations:

f: func [t] [
    Q: {"}
    W: complement charset Q     ;; any char thats not a double quote

    ; rule to parse t (c program) - it can be ANY of 
    ;     1. string 
    ;     2. OR comment (if so then remove)
    ;     3. OR pass thru

    parse t [
        any [
            ;; 1. String rule
            [Q any ["\\" | "\" Q | W] Q]

            ;; 2. OR comments rule
            | [
                a:  ;; mark beginning of match
                [
                    ;;    // comment    OR  /* comment */
                    ["//" to [lf | end]] | ["/*" thru "*/"]
                ]
                b:  ;; mark end of match 
                (remove/part a b) :a skip   ;; remove comment
            ]

            ;; 3. OR allow thru (so not a String or Comment)
            | skip
        ]
    ]

    t
]
\$\endgroup\$
1
\$\begingroup\$

PHP

Converting @Martin Ender's answer for php:

$str = preg_replace_callback('/\/\/.*|\/\*[\s\S]*?\*\/|("(\\.|[^"])*")/m', 
  function($matches){
     if(\is_array($matches) && (\count($matches) > 1)){
        return $matches[1];
     }else{
        return '';
     }
  }, $str);

now $str has lost single- and multi-line comments. This is useful for stripping comments in JSON data before feeding to json_decode().

\$\endgroup\$
1
  • \$\begingroup\$ Maybe you could reduce the bytes count by using a ternary operator? \$\endgroup\$ Mar 22, 2017 at 10:28
0
\$\begingroup\$

C# (262 chars):

From this very good SO answer:

string a(string i){return Regex.Replace(i, @"/\*(.*?)\*/|//(.*?)\r?\n|""((\\[^\n]|[^""\n])*)""|@(""[^""]*"")+", m => { var v = m.Value; if (v.StartsWith("/*") || v.StartsWith("//")) return v.StartsWith("//") ? "\r\n" : ""; return v; }, RegexOptions.Singleline);
\$\endgroup\$
-1
\$\begingroup\$

JS (ES6), 47 chars (wip)

DEMO: http://codepen.io/anon/pen/dPEMro

a=b=>b.replace(/(\/\*[^]*?\*\/|\/\/.*)\n?/g,"")

Inspired by my codegolfed minifiers: http://xem.github.io/miniMinifier/

doesn't handle comments in strings yet...

I'm curious to see if it's possible to achieve that in JS regexes.

\$\endgroup\$
1
  • \$\begingroup\$ If this answer doesn't meet the requirements, it should either be fixed or deleted. \$\endgroup\$
    – mbomb007
    May 23, 2018 at 20:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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