19
\$\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\$
  • 1
    \$\begingroup\$ From where that printf("\"/* This will stay too */\"\n"); appeared in the should become code? \$\endgroup\$ – manatwork Apr 1 '15 at 11:00
  • \$\begingroup\$ Oops, sorry... it was just a typo. Thanks for noticing! \$\endgroup\$ – Mathieu Rodic Apr 1 '15 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 '15 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 '15 at 13:03
  • \$\begingroup\$ @manatwork: whitespace removal is not mandatory \$\endgroup\$ – Mathieu Rodic Apr 1 '15 at 13:03
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\$
  • 1
    \$\begingroup\$ This works surprisingly well in PHP: regex101.com/r/kB5kA4/1 \$\endgroup\$ – Ismael Miguel Apr 1 '15 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\$ – Martin Ender Apr 1 '15 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\$ – Ismael Miguel Apr 1 '15 at 15:10
  • \$\begingroup\$ Nice! This regular expression even works with other programming languages (when slashes are escaped). \$\endgroup\$ – Mathieu Rodic Apr 2 '15 at 11:46
  • \$\begingroup\$ I used your regex technique to improve a third party library I work with: Dojo Toolkit \$\endgroup\$ – mbomb007 Aug 9 '18 at 13:57
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\$
  • 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\$ – Martin Ender Apr 1 '15 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\$ – Digital Trauma Apr 1 '15 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 '16 at 6:48
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\$
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\$
  • \$\begingroup\$ Maybe you could reduce the bytes count by using a ternary operator? \$\endgroup\$ – Mathieu Rodic Mar 22 '17 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\$
  • \$\begingroup\$ If this answer doesn't meet the requirements, it should either be fixed or deleted. \$\endgroup\$ – mbomb007 May 23 '18 at 20:02

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.