Remover comentários de linha única e multilinha da string

19

Objetivo

Usando a linguagem de programação de sua escolha, escreva o programa mais curto para eliminar comentários de uma sequência que representa um programa em C.


Entrada

A string pode ser usada como qualquer forma de entrada, mas também pode ser usada como uma variável.


Instruções

Dois tipos diferentes de comentários devem ser removidos:

  • comentários multilinhas , começando /*e terminando com*/
  • comentários de linha única , começando //e terminando com quebras de linha no estilo Linux (LF, \n)

Os comentários nas cadeias não devem ser excluídos. Para o objetivo desse desafio, você só precisa considerar "cadeias delimitadas. Em particular, você pode ignorar a possibilidade de 'literais de caracteres delimitados. Você também pode ignorar trigramas e continuações de linha ( /\<LF>*...).


Exemplos

Entrada:

#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;
}

Resultado:

#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;
}

Entrada:

/*
    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.*/

Resultado:

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

    int butNotThis = 42;

    int bar = 4 * 3; 
    return 0;
}
Mathieu Rodic
fonte
11
De onde isso printf("\"/* This will stay too */\"\n");apareceu no deve se tornar código?
Manatwork
Opa, desculpe ... foi apenas um erro de digitação. Obrigado por perceber!
Mathieu Rodic
Os espaços em branco contam? Existem 4 espaços na frente dos // this comment will be removedquais desapareceram. Alguma regra para isso?
precisa saber é
11
Eu não conheço nenhum dos idiomas listados tão bem, então algum tipo de especificação independente seria bom, junto com mais exemplos.
Zgarb
@manatwork: remoção de espaços em branco não é obrigatório
Mathieu Rodic

Respostas:

11

Retina , 35 + 1 + 2 = 38 bytes

Este programa consiste em dois arquivos, portanto, incluí uma penalidade de 1 byte para o segundo arquivo .

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

Esta é uma substituição simples de regex, usando o sabor .NET (embora isso funcione da mesma forma na maioria dos outros sabores).

A idéia é combinar os comentários e as strings, mas apenas escreva a correspondência de volta se for uma string. Ao corresponder as strings explicitamente, elas são ignoradas ao procurar comentários.

Martin Ender
fonte
11
Isso funciona surpreendentemente bem em PHP: regex101.com/r/kB5kA4/1
Ismael Miguel
11
@IsmaelMiguel Sim, eu não usei nada específico. A única razão pela qual escolhi o .NET é porque o Retina me permite escrever programas somente regex sem nenhuma sobrecarga de chamar algo parecido preg_replace.
Martin Ender
Eu estou ciente disso. Você já usou bastante isso antes. Se eu estiver correto, foi criado por você. Foi para os curiosos. E também, você tem agora um teste de banho onde você pode testar todas as mudanças que vêm a este pergunta (eu prevejo muitos)
Ismael Miguel
Agradável! Essa expressão regular ainda funciona com outras linguagens de programação (quando barras são escapadas).
Mathieu Rodic
Eu usei a sua técnica regex para melhorar um terceiro biblioteca que eu trabalho com: Dojo Toolkit
mbomb007
15

Coleção de compiladores Shell + coreutils + gcc, 31 bytes

Essa resposta pode parecer um pouco incompleta, mas não vi nada especificamente banindo-a na pergunta.

Em vez de usar expressões regulares desajeitadas, por que não usar a ferramenta criada para o trabalho. Não deve ter nenhum problema em fornecer resultados corretos:

cpp -fpreprocessed -o- -|sed 1d

Leva a entrada de STDIN e a saída para STDOUT. Normalmente ccp, todo o pré-processamento é feito (arquivos de cabeçalho, expansão de macro, remoção de comentários, etc.), mas com a -fpreprocessedopção, ele ignora a maioria das etapas, mas ainda remove os comentários. Além disso, o cpp adiciona uma linha como # 1 "<stdin>"no início da saída, então sedexiste para excluí-la.

Trauma Digital
fonte
11
"-fpreprocessed está implícito se o arquivo de entrada tiver uma das extensões .i, .iiou .mi". você poderá salvar alguns bytes salvando o arquivo em algo como, em a.ivez de usar o sinalizador?
Martin Ender
@ MartinBüttner Sim, notei isso também no manual. Então, eu esperaria algo como cat>i.i;cpp -o- i.i|sed 1dser equivalente. Mas o pré-processamento completo ocorre (por exemplo, o conteúdo completo do stdio.h é inserido). Possível bug do gcc ??? Bem, talvez eu verifique a fonte do cpp quando receber um mês.
Digital Trauma
Você pode remover o |sed 1dse você adicionar a -Popção. Observe que (conforme permitido pela pergunta), como espera um código pré-processado, ele não manipulará trigramas ou continuações de linha corretamente.
SCH
3

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;
}
Ilya Gazman
fonte
2

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

Como você pode ver aqui , o regex consiste em 2 grupos de captura alternados. O primeiro captura todas as strings citadas. O segundo todos os comentários.

Tudo o que precisamos fazer é remover tudo o que foi capturado pelo 2º grupo.

Exemplo:

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;
}
pepp
fonte
1

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 + algumas anotações:

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
]
draegtun
fonte
1

PHP

Convertendo a resposta de @Martin Ender para php:

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

agora $strperdeu comentários de uma e várias linhas. Isso é útil para remover comentários nos dados JSON antes de alimentar json_decode().

centuriano
fonte
Talvez você possa reduzir a contagem de bytes usando um operador ternário?
Mathieu Rodic
0

C # (262 caracteres):

A partir desta resposta SO muito boa :

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);
vrluckyin
fonte
-1

JS (ES6), 47 caracteres (wip)

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

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

Inspirado pelos meus minificadores codegolfed: http://xem.github.io/miniMinifier/

ainda não lida com comentários em strings ...

Estou curioso para ver se é possível conseguir isso em expressões regulares JS.

xem
fonte
Se essa resposta não atender aos requisitos, ela deverá ser corrigida ou excluída.
Mbomb007 23/05