如何删除/ *…* /下的所有字符,包括/ *和* /?


12

我曾经尝试过sed和awk,但是它不能正常工作,因为该字符涉及“ /”,它已经在命令中作为分隔符了。

请让我知道如何实现这一目标。

下面是一个示例Example.We要删除注释的部分,即 /*.....*/

/*This is to print the output
data*/
proc print data=sashelp.cars;
run;
/*Creating dataset*/
data abc;
set xyz;
run;

-bash-4.1 $ sed's,/ *。** / ,, g'test.sas下面是我得到的输出,第一条评论仍然存在。/ *这将打印输出数据* / proc打印数据= sashelp.cars; 跑; 数据abc; 设置xyz; 跑;
Sharique Alam

1
感谢您的修改。如果还包括所需的输出,那就更好了。还应在问题中(而不是在评论中)包括您尝试过的内容以及失败的方式。
terdon

2
包含注释或注释定界符的字符串文字应该怎么办?(例如INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');
zwol

1
相关(对不起,我无法抗拒!):codegolf.stackexchange.com/questions/48326/…–
ilkkachu

我用其他解决方案更新了我的帖子,请重新检查是否对您有利。
Luciano Andress Martini

Answers:


22

我想我找到了一个简单的解决方案!

cpp -P yourcommentedfile.txt 

一些更新:

用户ilkachu的报价(来自用户评论的原始文本):

我在gcc的选项上做了一些尝试-fpreprocessed将禁用大多数指令和宏扩展(显然#define和#undef除外)。添加-dD也会将定义保留在其中;和std = c89可用于忽略新样式//注释。即使使用它们,cpp也会用空格替换注释(而不是删除它们),并折叠空格和空行。

但是我认为,在大多数情况下,如果禁用宏扩展等功能,这仍然是合理且简单的解决方案,但我认为您会获得良好的结果...-是的,您可以将其与shell脚本结合使用以获得更好的效果... 以及更多...


1
使用C预处理器可能是最可靠的解决方案。由于预处理器可能是C注释最强大的解析器。聪明。
grochmal '16

14
但是,cpp除了删除注释(处理#include,扩展宏,包括内置宏...)以外
它还

3
@LucianoAndressMartini,不,tail -n +7只会删除前7行,不会阻止#include处理或宏扩展。echo __LINE__ | cpp例如尝试。或者echo '#include /dev/zero' | cpp
斯特凡Chazelas

2
-P如果执行此操作,则可能要使用mode。(这可以消除使用的需要tail。)
zwol16年

3
我打得有点与海湾合作委员会的选项:-fpreprocessed将禁用大多数指令和宏扩展(除了#define#undef明显)。添加-dD也将定义保留在其中;并且std=c89可以用来忽略新样式的//注释。即使使用它们,也cpp用空格替换注释(而不是删除它们),并折叠空格和空行。
ilkkachu 2016年

10

我曾经想出过这一点,我们可以对其进行完善:

perl -0777 -pe '
  BEGIN{
    $bs=qr{(?:\\|\?\?/)};
    $lc=qr{(?:$bs\n|$bs\r\n?)}
  }
  s{
    /$lc*\*.*?\*$lc*/
    | /$lc*/(?:$lc|[^\r\n])*
    | (
         "(?:$bs$lc*.|.)*?"
       | '\''$lc*(?:$bs$lc*(?:\?\?.|.))?(?:\?\?.|.)*?'\''
       | \?\?'\''
       | .[^'\''"/?]*
      )
  }{$1 eq "" ? " " : "$1"}exsg'

处理更多的极端情况。

请注意,如果删除注释,则可以更改代码的含义(1-/* comment */-1解析的方式如1 - -1while 1--1(如果删除注释,则将得到解析)会给您一个错误)。最好用空格字符代替注释(就像我们在此处所做的那样),而不是完全删除它。

上面的代码应在此有效的ANSI C代码上正常工作,例如,尝试包含一些特殊情况:

#include <stdio.h>
int main()
{
  printf(“%d%s%c%c%c%c%c%s%s%d \ n”,
  1-/ *评论* /-1,
  / \
*评论* /
  “ / *不是评论* /”,
  / *多行
  评论* /
  '“'/ *评论* /,'”',
  '\','“'/ *评论* /,
  '\
\
“',/ *评论* /
  “ \\
“ / *不是评论* /”,
  “ ?? /” / *不是注释* /“,
  '??''+'“'/ *”注释“ * /);
  返回0;
}

给出以下输出:

#include <stdio.h>
int main()
{
  printf(“%d%s%c%c%c%c%c%s%s%d \ n”,
  1- -1

  “ / *不是评论* /”,

  '“','”',
  '\','“',
  '\
\
“,  
  “ \\
“ / *不是评论* /”,
  “ ?? /” / *不是注释* /“,
  '??''+'“');
  返回0;
}

编译和运行时,两者都打印相同的输出。

您可以将其与的输出进行比较,gcc -ansi -E以了解预处理器将对其进行处理。该代码也是有效的C99或C11代码,但是gcc默认情况下禁用trigraphs支持,因此gcc除非您指定标准(例如gcc -std=c99gcc -std=c11或添加-trigraphs选项),否则它将无法使用。

它也适用于此C99 / C11(非ANSI / C90)代码:

//评论
/ \
/评论
//多行\
评论
“ //不是评论”

(与gcc -E/ gcc -std=c99 -E/相比gcc -std=c11 -E

ANSI C不支持// formcomment。//在ANSI C中无效,因此不会出现在ANSI C中。一个做作的情况下//可以真正出现在ANSI C(为指出存在的,你可能会发现有趣的讨论的其余部分)是当字符串化操作者在使用中。

这是有效的ANSI C代码:

#define s(x) #x
s(//not a comment)

在2004年的讨论中,gcc -ansi -E确实将其扩展到"//not a comment"。但是今天,它gcc-5.4返回了一个错误,所以我怀疑我们会发现使用这种结构的很多C代码。

sed相当于GNU的可能是:

lc='([\\%]\n|[\\%]\r\n?)'
sed -zE "
  s/_/_u/g;s/!/_b/g;s/</_l/g;s/>/_r/g;s/:/_c/g;s/;/_s/g;s/@/_a/g;s/%/_p/g;
  s@\?\?/@%@g;s@/$lc*\*@:&@g;s@\*$lc*/@;&@g
  s:/$lc*/:@&:g;s/\?\?'/!/g
  s#:/$lc*\*[^;]*;\*$lc*/|@/$lc*/$lc*|(\"([\\\\%]$lc*.|[^\\\\%\"])*\"|'$lc*([\\\\%]$lc*.)?[^\\\\%']*'|[^'\"@;:]+)#<\5>#g
  s/<>/ /g;s/!/??'/g;s@%@??/@g;s/[<>@:;]//g
  s/_p/%/g;s/_a/@/g;s/_s/;/g;s/_c/:/g;s/_r/>/g;s/_l/</g;s/_b/!/g;s/_u/_/g"

如果您的GNU sed太旧而无法支持-E-z,则可以将第一行替换为:

sed -r ":1;\$!{N;b1}

与此输出=>回波-e “BEGIN / *注释* / COMMAND / * COM \市民明白* / END”测试:perl的溶液与多线问题
بارپابابا

@Babby,为我工作。我在测试用例中添加了多行注释和结果输出。
斯特凡Chazelas

与当今相比,最好的东西是gcc -std=c11 -E -P-ansi只是的另一个名称-std=c90)。
zwol

@zwol,其想法是能够处理为任何C / C ++标准(c90,c11或其他)编写的代码。严格来说,这是不可能的(请参阅我的第二个人工示例)。该代码仍然试图处理C90结构(像??'),因此我们有比较cpp -ansi那些和C99 / C11 ......一个(像// xxx),因此我们用比较cpp(或cpp -std=c11...)
斯特凡Chazelas

@zwol,我将测试用例分开,以试图澄清一点。似乎三字母组合仍在C11中,因此我的第二个测试用例不是标准C。
斯特凡Chazelas

6

sed

更新

/\/\*/ {
    /\*\// {
        s/\/\*.*\*\///g;
        b next
    };

    :loop;
    /\*\//! {
        N;
        b loop
    };
    /\*\// {
        s/\/\*.*\*\//\n/g
    }
    :next
}

支持所有可能的内容(多行注释,[或和]之后的数据,);

 e1/*comment*/
-------------------
e1/*comment*/e2
-------------------
/*comment*/e2
-------------------
e1/*com
ment*/
-------------------
e1/*com
ment*/e2
-------------------
/*com
ment*/e2
-------------------
e1/*com
1
2
ment*/
-------------------
e1/*com
1
2
ment*/e2
-------------------
/*com
1
2
ment*/e2
-------------------
跑:
$ sed -f command.sed FILENAME

e1
-------------------
e1e2
-------------------
e2
-------------------
e1

-------------------
e1
e2
-------------------

e2
-------------------
e1

-------------------
e1
e2
-------------------

e2
-------------------

在数据之后开始发表评论将无效,例如proc print data 2nd /*another comment is here*/
mazs

@mazs更新,检查它
بارپابابا

这不会处理字符串文字中的注释,这实际上可能很重要,具体取决于SQL所做的工作
zwol 2016年

4
 $ cat file | perl -pe 'BEGIN{$/=undef}s!/\*.+?\*/!!sg'

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

删除空白行(如果有):

 $ cat file | perl -pe 'BEGIN{$/=undef}s!/\*.+?\*/\n?!!sg'

编辑-Stephane的较短版本:

 $ cat file | perl -0777 -pe 's!/\*.*?\*/!!sg'

好吧,我同意terdon:让我们看看预期的输出。
汉斯·舒

顺便说一句:如果包含“ / * foo * / run; / * bar * /”的一行会发生什么?应该只是“运行”吗??
汉斯·舒

大!然后我的解决方案起作用。注意我使用非贪婪:“。+?”
汉斯·舒

2
-0777一个较短的方式做BEGIN{$/=undef}
斯特凡Chazelas

1
也许 也可以用有效的评论.*?代替.+?if /**/
ilkkachu

2

使用SED命令而不使用脚本的解决方案

这个给你:

sed 's/\*\//\n&/g' test | sed '/\/\*/,/\*\//d'

注意:除非您安装,否则这在OS X上不起作用gnu-sed。但是它可以在Linux Distros上运行。


1
您可以使用-i选项就地编辑文件,而不是将输出重定向到新文件。或更安全-i.bak的备份文件
Rahul

1
它也不适用于所有情况,请尝试在同一行中添加注释,然后观察会发生什么。/ * test * /我想我们也需要perl来轻松解决这个问题。
Luciano Andress Martini

正是@Rahul,感谢您的提及。我只是想使其更简单。
FarazX

我很遗憾地说它不能在同一行中发表评论。
Luciano Andress Martini

@LucianoAndressMartini现在可以了!
FarazX

1

sed一次只操作一行,但是输入中的某些注释跨越多行。根据/unix//a/152389/90751,您可以首先使用tr将换行符转换为其他字符。然后sed可以将输入作为单行处理,然后tr再次使用以恢复换行符。

tr '\n' '\0' | sed ... | tr '\0' \n'

我使用了空字节,但是您可以选择输入文件中未出现的任何字符。

*在正则表达式中有特殊含义,因此需要转义\*以匹配文字*

.*贪婪的 -它将匹配最长的文本,包括更多*//*。这意味着第一个评论,最后一个评论以及介于两者之间的所有内容。为了限制这一点,请.*使用更严格的模式替换:注释可以包含非“ *”的任何内容,还可以包含“ *”以及非“ /”的任何内容。多个*s的运行也必须考虑在内:

tr '\n' '\0' | sed -e 's,/\*\([^*]\|\*\+[^*/]\)*\*\+/,,g' | tr '\0' '\n'

这将删除多行注释中的所有换行符,即。

data1 /* multiline
comment */ data2

会变成

data1  data2

如果这不是您想要的,sed可以告诉您保留其中一个换行符。这意味着选择一个可以匹配的换行符。

tr '\n' '\f' | sed -e 's,/\*\(\(\f\)\|[^*]\|\*\+[^*/]\)*\*\+/,\2,g' | tr '\f' '\n'

\f不能保证特殊字符以及使用可能没有匹配任何内容的反向引用都不能保证在所有sed实现中都能按预期工作。(我确认它可以在GNU sed 4.07和4.2.2上使用。)


您能否让MNE知道它将如何工作。我尝试了如下操作。tr'\ n''\ 0'| sed -e's,/ *([^ *] \ | * \ + [^ * /])** \ + / ,, g'test.sas | tr'\ 0''\ n'我得到如下:/ *这是打印输出数据* / data abcdf; 设置cfgtr; 跑; proc打印数据= sashelp.cars; 跑; 数据abc; 设置xyz; 跑;
Sharique Alam

@ShariqueAlam您已将test.sas管道放在管道的中间,因此sed直接从管道中读取数据,第一个tr无效。您需要使用cat test.sas | tr ...
JigglyNaga

0

使用一行sed删除注释:

sed '/\/\*/d;/\*\//d' file

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.