ฉันจะลบอักขระทั้งหมดที่อยู่ภายใต้ / * … * / รวมถึง / * & * / ได้อย่างไร


12

ฉันลอง sed และ awk แต่มันไม่ทำงานเนื่องจากตัวอักษรเกี่ยวข้องกับ "/" ซึ่งมีอยู่แล้วในคำสั่งเป็นตัวคั่น

โปรดแจ้งให้เราทราบว่าฉันจะทำสิ่งนี้ได้อย่างไร

ด้านล่างเป็นตัวอย่างเราต้องการลบส่วนที่ถูกใส่ความคิดเห็น /*.....*/

/*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 ด้านล่างคือ ouput ที่ฉันได้รับความคิดเห็นแรกยังคงอยู่ที่นั่น / * นี่คือการพิมพ์ข้อมูลออก * / 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

คำตอบ:


22

ฉันคิดว่าฉันพบทางออกที่ง่าย!

cpp -P yourcommentedfile.txt 

อัปเดตบางส่วน:

อ้างอิงจากผู้ใช้ilkachu (ข้อความต้นฉบับจากความคิดเห็นของผู้ใช้):

ฉันเล่นบิตด้วยตัวเลือกสำหรับ gcc: -fpreprocessedจะปิดใช้งานคำสั่งส่วนใหญ่และการขยายแมโคร (ยกเว้น #define และ #undef ชัด) การเพิ่ม-dDจะทำให้คำจำกัดความเป็นเกินไป และstd = c89สามารถใช้เพื่อละเว้นสไตล์ / ความคิดเห็นใหม่ แม้จะอยู่กับพวกเขา cpp จะแทนที่ความคิดเห็นด้วยช่องว่าง (แทนที่จะลบออก) และยุบช่องว่างและบรรทัดว่าง

แต่ฉันคิดว่ามันยังคงสมเหตุสมผลและเป็นทางออกที่ง่ายสำหรับกรณีส่วนใหญ่ถ้าคุณปิดการใช้งานส่วนขยายของแมโครและสิ่งอื่น ๆ ที่ฉันคิดว่าคุณจะได้รับผลลัพธ์ที่ดี ... - และใช่คุณสามารถรวมเข้ากับเชลล์สคริปต์ ... และอีกมากมาย ...


1
การใช้ตัวประมวลผลล่วงหน้า C น่าจะเป็นทางออกที่แข็งแกร่งที่สุด เนื่องจากตัวประมวลผลล่วงหน้าน่าจะเป็นตัวแยกวิเคราะห์ที่แข็งแกร่งที่สุดของความคิดเห็น C ฉลาด.
grochmal

14
แต่cppจะทำอะไรได้มากกว่าการลบความคิดเห็น (กระบวนการ#includeขยายมาโครรวมถึง
บิวด์อิน

3
@LucianoAndressMartini ไม่ใช่tail -n +7จะเพิ่งลบ 7 บรรทัดแรกมันจะไม่ป้องกันการ#includeประมวลผลหรือการขยายแมโคร ลองecho __LINE__ | cppตัวอย่าง หรือecho '#include /dev/zero' | cpp
Stéphane Chazelas

2
คุณอาจต้องการใช้-Pโหมดถ้าคุณทำเช่นนี้ (สิ่งนี้อาจลดความจำเป็นในการใช้tail)
zwol

3
ฉันเล่นบิตด้วยตัวเลือกสำหรับ gcc: -fpreprocessedจะปิดใช้งานคำสั่งส่วนใหญ่และการขยายแมโคร (ยกเว้น#defineและ#undefชัดเจน) การเพิ่ม-dDจะทำให้มีการกำหนดไว้เช่นกัน และstd=c89สามารถใช้เพื่อละเว้น//ความคิดเห็นสไตล์ใหม่ แม้จะอยู่กับพวกเขาก็ตามcppแทนที่ความคิดเห็นด้วยช่องว่าง (แทนที่จะลบออก) และยุบช่องว่างและบรรทัดว่าง
ilkkachu

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 - -1ในขณะที่1--1(ซึ่งคุณจะได้รับหากคุณลบความคิดเห็น) จะทำให้เกิดข้อผิดพลาด) เป็นการดีกว่าที่จะแทนที่ความคิดเห็นด้วยอักขระเว้นวรรค (ดังที่เราทำที่นี่) แทนที่จะเป็นการลบออกทั้งหมด

ข้างต้นควรทำงานอย่างถูกต้องในรหัส ANSI C ที่ถูกต้องเช่นอินสแตนซ์ที่พยายามรวมมุมเล็ก ๆ น้อย ๆ :

#include <stdio.h>
int หลัก ()
{
  printf ("% d% s% c% c% c% c% c% c% s% s% d \ n",
  1 - / * comment * / - 1,
  / \
* ความคิดเห็น * /
  "/ * ไม่ใช่ความคิดเห็น * /"
  / * multiline
  ความคิดเห็น * /
  '' '/ * ความคิดเห็น * /,' "',
  '\' ',' "'/ * ความคิดเห็น * /,
  '\
\
"', / * ความคิดเห็น * /
  "\\
"/ * ไม่ใช่ความคิดเห็น * /"
  "?? /" / * ไม่ใช่ความคิดเห็น * / "
  '??' '+' "'/ *" ความคิดเห็น "* /);
  กลับ 0
}

ซึ่งให้ผลลัพธ์นี้:

#include <stdio.h>
int หลัก ()
{
  printf ("% d% s% c% c% c% c% c% c% s% s% d \ n",
  1- -1

  "/ * ไม่ใช่ความคิดเห็น * /"

  '' ',' "',
  '\' ',' "',
  '\
\
"'  
  "\\
"/ * ไม่ใช่ความคิดเห็น * /"
  "?? /" / * ไม่ใช่ความคิดเห็น * / "
  '??' '+' "');
  กลับ 0
}

ทั้งการพิมพ์เอาต์พุตเดียวกันเมื่อคอมไพล์และรัน

คุณสามารถเปรียบเทียบกับผลลัพธ์ของgcc -ansi -Eเพื่อดูว่าตัวประมวลผลล่วงหน้าทำอะไรได้บ้าง รหัสนั้นยังเป็นรหัส C99 หรือ C11 ที่ถูกต้องอย่างไรก็ตามgccปิดใช้งานการสนับสนุน trigraph ตามค่าเริ่มต้นดังนั้นจึงไม่สามารถใช้งานได้gccเว้นแต่คุณจะระบุมาตรฐานเช่นgcc -std=c99หรือgcc -std=c11หรือเพิ่ม-trigraphsตัวเลือก)

นอกจากนี้ยังทำงานกับรหัส C99 / C11 (ไม่ใช่ ANSI / C90) นี้:

// ความคิดเห็น
/ \
/ ความคิดเห็น
// multiline \
คิดเห็น
"// ไม่ใช่ความคิดเห็น"

(เปรียบเทียบกับgcc -E/ gcc -std=c99 -E/ gcc -std=c11 -E)

ANSI C ไม่สนับสนุน// formความคิดเห็น //ไม่ถูกต้องใน ANSI C ดังนั้นจะไม่ปรากฏที่นั่น กรณีที่วางแผนไว้อย่างหนึ่งซึ่ง//อาจปรากฏขึ้นจริงใน ANSI C (ดังที่ระบุไว้ที่นั่นและคุณอาจพบว่าส่วนที่เหลือของการสนทนาที่น่าสนใจ) คือเมื่อตัวดำเนินการstringifyใช้งานอยู่

นี่คือรหัส 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}

วิธีแก้ปัญหา perl มีปัญหากับหลายบรรทัด: ทดสอบด้วยผลลัพธ์นี้ => echo -e "BEGIN / * ความคิดเห็น * / คำสั่ง / * com \ nment * / END"
بارپابابا

@ แบบบี้ใช้ได้สำหรับฉัน ฉันได้เพิ่มความคิดเห็นแบบหลายบรรทัดและผลลัพธ์ที่ได้ในกรณีทดสอบของฉัน
Stéphane Chazelas

สิ่งที่ดีที่สุดในการเปรียบเทียบกับปัจจุบันคือgcc -std=c11 -E -P( -ansiเป็นเพียงชื่ออื่น-std=c90)
zwol

@zwol แนวคิดจะสามารถจัดการโค้ดที่เขียนขึ้นสำหรับมาตรฐาน C / C ++ ใด ๆ (c90, c11 หรืออื่น ๆ ) พูดอย่างเคร่งครัดมันเป็นไปไม่ได้ (ดูตัวอย่างที่ 2 ของฉันที่วางแผนไว้) รหัสยังคงพยายามที่จะจัดการกับโครงสร้าง C90 (เช่น??') ดังนั้นเราจึงเปรียบเทียบกับcpp -ansiสำหรับเหล่านั้นและ C99 / C11 ... หนึ่ง (เหมือน// xxx) ดังนั้นเราจึงเปรียบเทียบกับcpp(หรือcpp -std=c11... )
Stéphane Chazelas

@zwol ฉันได้แบ่งกรณีทดสอบในความพยายามที่จะชี้แจงเล็กน้อย ดูเหมือนว่า trigraph ยังคงอยู่ใน C11 ดังนั้นกรณีทดสอบครั้งที่สองของฉันไม่ได้เป็นมาตรฐาน C อยู่ดี
Stéphane Chazelas

6

ด้วยsed:

UPDATE

/\/\*/ {
    /\*\// {
        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

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: ให้ดูผลลัพธ์ที่คาดหวัง
Hans Schou

BTW: จะเกิดอะไรขึ้นกับบรรทัดเดียวที่มี: "/ * foo * / run; / * bar * /" ควรที่จะ "รัน;" ?
Hans Schou

ที่ดี! จากนั้นโซลูชันของฉันทำงาน หมายเหตุฉันใช้ความโลภ: ". +?"
Hans Schou

2
ดู-0777ว่าเป็นวิธีที่สั้นลงในการทำอะไรBEGIN{$/=undef}
Stéphane Chazelas

1
บางที .*?แทน.+?ถ้า/**/เป็นความเห็นที่ถูกต้องมากเกินไป
ilkkachu

2

โซลูชันโดยใช้คำสั่ง SED และไม่มีสคริปต์

อยู่นี่ไง:

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

NBนี้ไม่ได้ทำงานบน OS X gnu-sedเว้นแต่คุณจะติดตั้ง แต่มันทำงานบน Linux Distros


1
คุณสามารถใช้-iตัวเลือกในการแก้ไขไฟล์ในสถานที่แทนการเปลี่ยนเส้นทางการส่งออกไปยังไฟล์ใหม่ หรือปลอดภัยมากขึ้น-i.bakในการสำรองไฟล์
Rahul

1
มันไม่ได้ผลสำหรับทุกกรณีเช่นกันลองใส่ความคิดเห็นในบรรทัดเดียวกันและดูว่าเกิดอะไรขึ้น ... / * ทดสอบ * / ฉันคิดว่าเราจะต้องได้รับ perl ด้วยเช่นกันในวิธีที่ง่าย
ลูเซียโน 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'

ฉันใช้ null null แต่คุณสามารถเลือกอักขระที่ไม่ปรากฏในไฟล์อินพุตของคุณได้

*มีความหมายพิเศษในการแสดงออกปกติดังนั้นจึงจะต้องมีการหลบหนีเป็นเพื่อให้ตรงกับตัวอักษร\**

.*คือโลภ - มันจะตรงกับข้อความที่ยาวที่สุดที่เป็นไปได้รวมทั้งอื่น ๆ อีกมากมายและ*/ /*นั่นหมายถึงความคิดเห็นแรกความคิดเห็นล่าสุดและทุกสิ่งในระหว่าง หากต้องการ จำกัด สิ่งนี้ให้แทนที่.*ด้วยรูปแบบที่เข้มงวดยิ่งขึ้น: ความคิดเห็นสามารถมีสิ่งที่ไม่ใช่ "*" และ "*" ตามด้วยสิ่งใดก็ตามที่ไม่ใช่ "/" การทำงานของหลาย*s นั้นจะต้องมีการคำนึงถึง:

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

นี่จะเป็นการลบ linebreaks ใด ๆ ในความคิดเห็นหลายบรรทัดเช่น

data1 /* multiline
comment */ data2

จะกลายเป็น

data1  data2

หากนี่ไม่ใช่สิ่งที่ต้องการก็sedสามารถบอกให้รักษาหนึ่งใน linebreaks นี่หมายถึงการเลือกอักขระการแทนที่ linebreak ที่สามารถจับคู่ได้

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' และฉันได้ดังต่อไปนี้: / * นี่คือการพิมพ์ข้อมูลออก * / ข้อมูล abcdf; ตั้ง cfgtr; วิ่ง; proc print data = sashelp.cars; วิ่ง; abc ข้อมูล ตั้ง xyz; วิ่ง;
Sharique Alam

@ShariqueAlam คุณใส่test.sasตรงกลางของท่อตรงนั้นดังนั้นsedอ่านจากโดยตรงและอันแรกtrไม่มีผล คุณต้องใช้cat test.sas | tr ...
JigglyNaga

0

ใช้หนึ่งบรรทัดเพื่อลบความคิดเห็น:

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

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.