Bagaimana saya bisa menghapus semua karakter yang berada di bawah / * ... * / termasuk / * & * /?

12

Saya memang mencoba sed dan awk, tetapi tidak berfungsi karena karakter melibatkan "/" yang sudah ada di perintah sebagai pembatas.

Tolong beri tahu saya bagaimana saya bisa mencapai ini.

Di bawah ini adalah contoh Contoh. Kami ingin menghapus bagian komentar yaitu /*.....*/

/*This is to print the output
data*/
proc print data=sashelp.cars;
run;
/*Creating dataset*/
data abc;
set xyz;
run;
Sharique Alam
sumber
-bash-4.1 $ sed, / *. ** / ,, g 'test.sas Di bawah ini adalah ouput yang saya dapatkan, komentar pertama masih ada. / * Ini untuk mencetak data keluaran * / proc print data = sashelp.cars; Lari; data abc; atur xyz; Lari;
Sharique Alam
1
Terima kasih atas hasil editnya. Akan lebih baik jika Anda memasukkan output yang Anda inginkan juga. Sertakan juga apa yang Anda coba dan bagaimana gagal dalam pertanyaan, bukan di komentar.
terdon
2
Apa yang harus terjadi pada string literal yang berisi komentar atau pembatas komentar? (mis. INSERT INTO string_table VALUES('/*'), ('*/'), ('/**/');)
zwol
1
Terkait (maaf saya tidak bisa menolak!): Codegolf.stackexchange.com/questions/48326/…
ilkkachu
Saya memperbarui posting saya dengan solusi lain, harap periksa kembali jika sekarang baik untuk Anda.
Luciano Andress Martini

Jawaban:

22

Saya pikir saya menemukan solusi yang mudah!

cpp -P yourcommentedfile.txt 

BEBERAPA PEMBARUAN:

Kutipan dari ilkachu pengguna (teks asli dari komentar pengguna):

Saya bermain sedikit dengan opsi untuk gcc: -fpreprocessed akan menonaktifkan sebagian besar arahan dan ekspansi makro (kecuali #define dan #undef tampaknya). Menambahkan -dD akan meninggalkan definisi juga; dan std = c89 dapat digunakan untuk mengabaikan gaya baru // komentar. Bahkan dengan mereka, cpp menggantikan komentar dengan spasi (alih-alih menghapusnya), dan menciutkan spasi dan baris kosong.

Tapi saya pikir itu masih masuk akal dan solusi mudah untuk sebagian besar kasus, jika Anda menonaktifkan ekspansi makro dan hal-hal lain saya pikir Anda akan mendapatkan hasil yang baik ... - dan ya Anda dapat menggabungkannya dengan skrip shell untuk menjadi lebih baik ... dan banyak lagi...

Luciano Andress Martini
sumber
1
Menggunakan preprosesor C kemungkinan merupakan solusi yang paling kuat. Karena preprocessor kemungkinan merupakan parser paling kuat dari komentar C. Pintar.
grochmal
14
Tetapi cppakan melakukan lebih banyak daripada menghapus komentar (proses #include, perluas makro, termasuk yang builtin ...)
Stéphane Chazelas
3
@LucianoAndressMartini, tidak, tail -n +7hanya akan menghapus 7 baris pertama, itu tidak akan mencegah #includepemrosesan atau ekspansi makro. Coba echo __LINE__ | cppmisalnya. Atauecho '#include /dev/zero' | cpp
Stéphane Chazelas
2
Anda mungkin ingin menggunakan -Pmode jika Anda melakukan ini. (Ini dapat menghilangkan kebutuhan untuk menggunakan tail.)
zwol
3
Saya bermain sedikit dengan opsi untuk gcc: -fpreprocessedakan menonaktifkan sebagian besar arahan dan ekspansi makro (kecuali #definedan #undefternyata). Menambahkan -dDakan meninggalkan definisi juga; dan std=c89dapat digunakan untuk mengabaikan //komentar gaya baru . Bahkan dengan mereka, cppganti komentar dengan spasi (alih-alih menghapusnya), dan runtuh spasi dan baris kosong.
ilkkachu
10

Saya pernah membuat ini yang bisa kita perbaiki:

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'

untuk menangani beberapa kasus sudut lainnya.

Perhatikan bahwa jika Anda menghapus komentar, Anda dapat mengubah arti kode ( 1-/* comment */-1diuraikan seperti 1 - -1sementara 1--1(yang akan Anda dapatkan jika Anda menghapus komentar) akan memberi Anda kesalahan). Lebih baik mengganti komentar dengan karakter spasi (seperti yang kita lakukan di sini) daripada sepenuhnya menghapusnya.

Contoh di atas harus berfungsi dengan baik pada kode ANSI C yang valid ini, misalnya yang mencoba memasukkan beberapa kasus sudut:

#sertakan <stdio.h>
int main ()
{
  printf ("% d% s% c% c% c% c% c% s% s% s% d \ n",
  1 - / * komentar * / - 1,
  / \
* komentar * /
  "/ * bukan komentar * /",
  / * multiline
  komentar * /
  '"' / * komentar * /, '"',
  '\' ',' "'/ * komentar * /,
  '\
\
"', / * komentar * /
  "\\
"/ * bukan komentar * /",
  "?? /" / * bukan komentar * / ",
  '??' '+' "'/ *" komentar "* /);
  return 0;
}

Yang memberikan hasil ini:

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

  "/ * bukan komentar * /",

  '"', '"',
  '\' ',' "',
  '\
\
"',  
  "\\
"/ * bukan komentar * /",
  "?? /" / * bukan komentar * / ",
  '??' '+' "');
  return 0;
}

Keduanya mencetak output yang sama saat dikompilasi dan dijalankan.

Anda dapat membandingkan dengan output gcc -ansi -Euntuk melihat apa yang akan dilakukan oleh pra-prosesor. Kode itu juga merupakan kode C99 atau C11 yang valid, namun gccmenonaktifkan dukungan trigraph secara default sehingga tidak akan berfungsi gcckecuali jika Anda menentukan standar suka gcc -std=c99atau gcc -std=c11atau tambahkan -trigraphsopsi).

Ini juga berfungsi pada kode C99 / C11 (non-ANSI / C90) ini:

// komentar
/ \
/ komentar
// multiline \
komentar
"// bukan komentar"

(bandingkan dengan gcc -E/ gcc -std=c99 -E/ gcc -std=c11 -E)

ANSI C tidak mendukung // formkomentar. //tidak berlaku di ANSI C sehingga tidak akan muncul di sana. Satu kasus yang dibuat-buat di mana //mungkin benar-benar muncul di ANSI C (seperti yang disebutkan di sana , dan Anda mungkin menemukan sisa diskusi yang menarik) adalah ketika operator pengikat sedang digunakan.

Ini adalah kode C ANSI yang valid:

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

Dan pada saat diskusi tahun 2004, gcc -ansi -Ememang memperluas ke "//not a comment". Namun hari ini, gcc-5.4mengembalikan kesalahan pada itu, jadi saya ragu kita akan menemukan banyak kode C menggunakan konstruksi semacam ini.

sedSetara GNU dapat berupa:

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"

Jika GNU Anda sedterlalu tua untuk didukung -Eatau -z, Anda dapat mengganti baris pertama dengan:

sed -r ":1;\$!{N;b1}
Stéphane Chazelas
sumber
solusi perl memiliki masalah dengan multi-jalur: uji dengan output ini => echo -e "BEGIN / * komentar * / PERINTAH / * com \ nment * / END"
بارپابابا
@ Bob, bekerja untuk saya. Saya telah menambahkan komentar multi-line dan hasil yang dihasilkan dalam kasus pengujian saya.
Stéphane Chazelas
Hal terbaik untuk dibandingkan dengan saat ini adalah gcc -std=c11 -E -P( -ansihanya nama lain untuk -std=c90).
zwol
@ zwol, idenya adalah untuk dapat menangani kode yang ditulis untuk standar C / C ++ (c90, c11 atau lainnya). Sebenarnya, itu tidak mungkin (lihat contoh buatan saya yang ke-2). Kode masih mencoba menangani konstruksi C90 (seperti ??'), maka kami membandingkannya dengan konstruksi C90 cpp -ansi/ C11 ... satu (suka // xxx), maka kami membandingkannya dengan cpp(atau cpp -std=c11...)
Stéphane Chazelas
@ zwol, saya sudah membagi test case dalam upaya untuk mengklarifikasi sedikit. Sepertinya trigraph masih dalam C11, jadi test case kedua saya bukan C standar.
Stéphane Chazelas
6

dengan sed:

MEMPERBARUI

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

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

mendukung semua yang mungkin (komentar multi baris, data setelah [atau dan] sebelum,);

 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
-------------------
Lari:
$ sed -f command.sed FILENAME

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

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

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

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

e2
-------------------
بارپابابا
sumber
tidak akan berfungsi untuk komentar yang dimulai setelah data, sepertiproc print data 2nd /*another comment is here*/
mazs
@mazs diperbarui, periksa
بارپابابا
Ini tidak menangani komentar di dalam string literal, yang mungkin sebenarnya penting, tergantung pada apa yang SQL lakukan
zwol
4
 $ cat file | perl -pe 'BEGIN{$/=undef}s!/\*.+?\*/!!sg'

 proc print data=sashelp.cars;
 run;

 data abc;
 set xyz;
 run;

Hapus baris kosong jika ada:

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

Edit - versi lebih pendek dari Stephane:

 $ cat file | perl -0777 -pe 's!/\*.*?\*/!!sg'
Hans Schou
sumber
baik, saya setuju dengan terdon: Mari kita lihat output yang diharapkan.
Hans Schou
BTW: Apa yang harus terjadi pada satu baris berisi: "/ * foo * / run; / * bar * /"? Haruskah itu hanya "dijalankan;" ?
Hans Schou
Bagus! Maka solusi saya bekerja. Catatan saya menggunakan non-serakah: ". +?"
Hans Schou
2
Lihat -0777sebagai cara yang lebih singkatBEGIN{$/=undef}
Stéphane Chazelas
1
Mungkin .*?bukan .+?jika /**/adalah komentar yang valid juga.
ilkkachu
2

Solusi dengan menggunakan perintah SED dan tanpa Script

Anda disini:

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

NB Ini tidak berfungsi pada OS X, kecuali jika Anda menginstal gnu-sed. Tetapi ini bekerja pada distro Linux.

FarazX
sumber
1
Anda dapat menggunakan -iopsi untuk mengedit file di tempat alih-alih mengarahkan output ke file baru. atau jauh lebih aman -i.bakuntuk membuat cadangan file
Rahul
1
Ini tidak berfungsi untuk semua kasus juga, cobalah untuk memberikan komentar di baris yang sama dan perhatikan apa yang terjadi ... Contoh set xy \; / * test * / Saya pikir kita perlu perl terlalu memecahkan ini dengan cara yang mudah.
Luciano Andress Martini
@Rahul tepatnya, terima kasih telah menyebutkan. Saya hanya ingin membuatnya lebih sederhana.
FarazX
Saya sangat menyesal mengatakan bahwa itu tidak berfungsi untuk komentar di baris yang sama.
Luciano Andress Martini
@LucianoAndressMartini Sekarang berhasil!
FarazX
1

sedberoperasi pada satu baris pada satu waktu, tetapi beberapa komentar di input span beberapa baris. Sesuai /unix//a/152389/90751 , Anda dapat menggunakan pertama-tama truntuk mengubah pemisah baris menjadi beberapa karakter lain. Kemudian seddapat memproses input sebagai satu baris, dan Anda gunakan trlagi untuk mengembalikan jeda baris.

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

Saya telah menggunakan byte nol, tetapi Anda dapat memilih karakter apa pun yang tidak muncul di file input Anda.

*memiliki arti khusus dalam ekspresi reguler, sehingga perlu melarikan diri \*untuk mencocokkan dengan literal *.

.*adalah serakah - itu akan cocok dengan teks yang mungkin terpanjang, termasuk lebih */dan /*. Itu berarti komentar pertama, komentar terakhir, dan semua yang ada di antaranya. Untuk membatasi ini, ganti .*dengan pola yang lebih ketat: komentar dapat berisi apa pun yang bukan "*", dan juga "*" diikuti oleh apa pun yang bukan "/". Proses banyak *juga harus diperhitungkan:

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

Ini akan menghapus semua linebreak di komentar multiline, yaitu.

data1 /* multiline
comment */ data2

akan menjadi

data1  data2

Jika ini bukan yang diinginkan, seddapat dikatakan untuk menjaga salah satu linebreaks. Ini berarti memilih karakter pengganti linebreak yang dapat dicocokkan.

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

Karakter khusus \f, dan penggunaan referensi-belakang yang mungkin tidak cocok dengan apa pun, tidak dijamin berfungsi sebagaimana dimaksud dalam semua sedimplementasi. (Saya mengkonfirmasi itu berfungsi pada GNU sed 4.07 dan 4.2.2.)

JigglyNaga
sumber
Bisakah Anda memberi tahu saya bagaimana cara kerjanya. Saya mencoba seperti di bawah ini. tr '\ n' '\ 0' | sed -e's, / * ([^ *] \ | * \ + [^ * /]) ** \ + / ,, g 'test.sas | tr '\ 0' '\ n' dan saya dapat seperti di bawah ini: / * Ini untuk mencetak data keluaran * / data abcdf; atur cfgtr; Lari; data cetak proc = sashelp.cars; Lari; data abc; atur xyz; Lari;
Sharique Alam
@ShariqueAlam Anda telah meletakkannya test.sasdi tengah-tengah jalur pipa di sana, jadi bacalah sedlangsung, dan yang pertama trtidak berpengaruh. Anda harus menggunakancat test.sas | tr ...
JigglyNaga
0

menggunakan sed satu baris untuk menghapus komentar:

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

proc print data=sashelp.cars;
run;
data abc;
set xyz;
run;
pengguna5337995
sumber