行コメントを除去するプログラム
投稿者 shu  (社会人)
投稿日時
2011/2/14 22:48:58
こんなでしょうか?
Imports System.Text.RegularExpressions
Imports System.IO
Imports System.Text
Private Sub ~
'--- ファイル読みはとりあえずそのまま
Dim read As New System.IO.StreamReader("TextFile1.txt", System.Text.Encoding.GetEncoding("shift_jis"))
Dim text As String = read.ReadToEnd()
read.Close()
'--- 行頭 + スペースの繰り返し + // + 何らかの文字列 + 行末 用パターン
Dim reg As New Regex("^\s*//.*$")
'--- 1行づつ読むためのStringReader(ファイル読み込みストリーム自体を使ってもいいかも)
Dim stm As New StringReader(text)
'--- 結果用のStringBuilder
Dim strOut As New StringBuilder
'--- 行データ格納用
Dim strLine As String
'--- とりあえず1行読む
strLine = stm.ReadLine()
Do While strLine IsNot Nothing
'--- パターンにマッチしない行だけ出力用StringBuilderに連結
If Not reg.Match(strLine).Success Then
strOut.AppendLine(strLine)
End If
'--- 次の行をよむ
strLine = stm.ReadLine
Loop
stm.close
'--- 結果をTextBox1へ表示
TextBox1.Text = strOut.ToString
End Sub
Imports System.Text.RegularExpressions
Imports System.IO
Imports System.Text
Private Sub ~
'--- ファイル読みはとりあえずそのまま
Dim read As New System.IO.StreamReader("TextFile1.txt", System.Text.Encoding.GetEncoding("shift_jis"))
Dim text As String = read.ReadToEnd()
read.Close()
'--- 行頭 + スペースの繰り返し + // + 何らかの文字列 + 行末 用パターン
Dim reg As New Regex("^\s*//.*$")
'--- 1行づつ読むためのStringReader(ファイル読み込みストリーム自体を使ってもいいかも)
Dim stm As New StringReader(text)
'--- 結果用のStringBuilder
Dim strOut As New StringBuilder
'--- 行データ格納用
Dim strLine As String
'--- とりあえず1行読む
strLine = stm.ReadLine()
Do While strLine IsNot Nothing
'--- パターンにマッチしない行だけ出力用StringBuilderに連結
If Not reg.Match(strLine).Success Then
strOut.AppendLine(strLine)
End If
'--- 次の行をよむ
strLine = stm.ReadLine
Loop
stm.close
'--- 結果をTextBox1へ表示
TextBox1.Text = strOut.ToString
End Sub
投稿者 るきお  (社会人)
投稿日時
2011/2/14 22:53:50
ソースコードからコメントを取り除くのは実は難しいです。
95%の単純なコメントは苦も無く取り去ることができますが、やっかいなパターンがあります。
C言語でいうと、次のような行のコメントだけ取り除くロジックが書けますか?
\でクォーテーションがエスケープされているケースもあります。
さらに、今回はC言語ということですから対象外かもしれませんがC#を考えると、@"…"のケースやXMLコメントのケースなど考慮すべきことがたくさんあります。
という困難な状況はひとまず置いておいて95%の単純なコメントを除去するプログラムです。
改行の位置を自分で調べるのではなく、最初から1行ずつ読んできたほうが楽です。
なので、修正というかほとんど作り変えてしまいました。
95%の単純なコメントは苦も無く取り去ることができますが、やっかいなパターンがあります。
C言語でいうと、次のような行のコメントだけ取り除くロジックが書けますか?
string str = "//はコメントの始まりです。" //""//""
\でクォーテーションがエスケープされているケースもあります。
さらに、今回はC言語ということですから対象外かもしれませんがC#を考えると、@"…"のケースやXMLコメントのケースなど考慮すべきことがたくさんあります。
という困難な状況はひとまず置いておいて95%の単純なコメントを除去するプログラムです。
改行の位置を自分で調べるのではなく、最初から1行ずつ読んできたほうが楽です。
なので、修正というかほとんど作り変えてしまいました。
Dim sjis As System.Text.Encoding = System.Text.Encoding.GetEncoding("shift_jis")
Dim reader As New IO.StreamReader("C:\test\test.txt", sjis)
Dim writer As New IO.StreamWriter("C:\test\result.txt", False, sjis)
Do Until reader.EndOfStream
'対象から1行読む
Dim sourceLine As String = reader.ReadLine
'//の位置を取得
Dim pos As Integer = sourceLine.IndexOf("//")
Dim targetLine As String
If pos >= 0 Then
'//が存在するならば、そこまでの文字列を切り抜く。
targetLine = sourceLine.Substring(0, pos)
Else
'//が存在しないならば、読み込んだ行全体が書き込む対象となる。
targetLine = sourceLine
End If
'書き込み
writer.WriteLine(targetLine)
Loop
reader.Close()
reader.Dispose()
writer.Close()
writer.Dispose()
投稿者 るきお(管理者)  (社会人)
投稿日時
2011/2/14 23:10:15
shuさんとダブルで被っちゃいました…。
いつも投稿ありがとうございます。
いつも投稿ありがとうございます。
投稿者 shu  (社会人)
投稿日時
2011/2/15 08:16:05
被ってしまいましたね。
ちなみに私のほうはコメント行を削除、るきおさんの方はコメントを削除なので
用途に合わせて使い分けて下さい。両方なら合わせてみてください > heavenさん
ちなみに私のほうはコメント行を削除、るきおさんの方はコメントを削除なので
用途に合わせて使い分けて下さい。両方なら合わせてみてください > heavenさん
投稿者 (削除されました)  ()
投稿日時
2011/2/15 09:35:02
(削除されました)
投稿者 (削除されました)  ()
投稿日時
2011/2/15 12:21:56
(削除されました)
投稿者 heaven  (社会人)
投稿日時
2011/2/15 12:23:20
お返事ありがとうございます。
コメントの削除は「Regex.Replace(test, "/\*([^*]|\*[^/])*\*/", String.Empty)」で割と簡単に削除できましたが、行コメントの削除となると意外と難しいかったです^^;
対象ソースには、行コメントとの直前にもコードが綴られていたりする場合もあるのと、
るきおさんの、「StreamReader」では既に編集済みのソースコードを格納した変数から直接編集できなかったので、shuさんとるきおさんのコードを参考にミックスした感じで作らせていただきました。
本当に助かりました。m(_ _"m)
一応作成したコードを載せておきます。
コメントの削除は「Regex.Replace(test, "/\*([^*]|\*[^/])*\*/", String.Empty)」で割と簡単に削除できましたが、行コメントの削除となると意外と難しいかったです^^;
対象ソースには、行コメントとの直前にもコードが綴られていたりする場合もあるのと、
るきおさんの、「StreamReader」では既に編集済みのソースコードを格納した変数から直接編集できなかったので、shuさんとるきおさんのコードを参考にミックスした感じで作らせていただきました。
本当に助かりました。m(_ _"m)
一応作成したコードを載せておきます。
Dim read As New System.IO.StreamReader("C:\test.txt", System.Text.Encoding.GetEncoding("shift_jis"))
Dim text As String = read.ReadToEnd()
If System.Text.RegularExpressions.Regex.IsMatch(text, ".*//.*") Then 'コメント有無チェック
Dim lst As New List(Of String)() '読込みデータ格納用
Dim stm As New StringReader(text) '1行づつ読むためのStringReader
Dim strLine As String '行データ格納用
strLine = stm.ReadLine() 'とりあえず1行読む
Do While strLine IsNot Nothing
Dim targetLine As String '切り抜き用変数
'//の位置を取得
Dim pos As Integer = strLine.IndexOf("//")
If pos >= 0 Then
'//が存在するならば、そこまでの文字列を切り抜く。
targetLine = strLine.Substring(0, pos)
lst.Add(targetLine)
Else
'//が存在しないならば、読み込んだ行全体が書き込む対象となる。
targetLine = strLine
lst.Add(targetLine)
End If
'次の行をよむ
strLine = stm.ReadLine
Loop
'格納したデータを改行コードで結合
text = String.Join(vbCrLf, lst.ToArray)
End If
textbox1.text = text '結果をTextBox1へ表示
投稿者 よねKEN  (社会人)
投稿日時
2011/2/16 02:03:16
> コメントの削除は「Regex.Replace(test, "/\*([^*]|\*[^/])*\*/", String.Empty)」で割と簡単に削除できましたが、行コメントの削除となると意外と難しいかったです^^;
TextBox1.Text = Regex.Replace(text, "//.*$", vbCrLf, RegexOptions.Multiline)
とするだけで削除できますよ。
(厳密にはtextのデータの最後が改行ではない場合に、
余計な改行が一つ付加された状態に置換されますが)
余談ですが、//コメントはC言語のコメントではなくC++のコメントですね。
TextBox1.Text = Regex.Replace(text, "//.*$", vbCrLf, RegexOptions.Multiline)
とするだけで削除できますよ。
(厳密にはtextのデータの最後が改行ではない場合に、
余計な改行が一つ付加された状態に置換されますが)
余談ですが、//コメントはC言語のコメントではなくC++のコメントですね。
投稿者 (削除されました)  ()
投稿日時
2011/2/16 10:03:39
(削除されました)
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2011/2/16 10:07:55
> 余談ですが、//コメントはC言語のコメントではなくC++のコメントですね。
C 言語における「//」の正式サポートは、
『ISO/IEC 9899:1999 - Programming Language C』
『JIS X 3010:2003 - プログラム言語C』
からのようですね。上記の 6.4.9 を参照。
逆に言うと、それ以前の仕様(K&R, C89/ISO C90, C95) においては、
// コメントは定義されていないようです。もっとも、仕様上には無くとも
コンパイラ側では使えるように拡張実装されているケースもあるようですが。
> TextBox1.Text = Regex.Replace(text, "//.*$", vbCrLf, RegexOptions.Multiline)
「char *url = "http://www.google.co.jp";」が
「char *url = "http:」になってしまうのでは?
厳密に処理するなら、長いマクロ処理などで使われる『改行前の\』も
考慮する必要があるかも知れません。
たとえば、「char *s = "yen = 100; // money : 100";」という一行のコードを
「char *s = "\」「yen = 100; // money : 100\」「";」という三行で書かれる可能性もあるわけで。
---
るきおさん宛:
本文の行末に\記号があった場合、プレビュー画面では正しく表示されますが、
掲示板への投稿結果では「\ + 改行」が削除され、一行に繋がってしまうようです。
C 言語における「//」の正式サポートは、
『ISO/IEC 9899:1999 - Programming Language C』
『JIS X 3010:2003 - プログラム言語C』
からのようですね。上記の 6.4.9 を参照。
逆に言うと、それ以前の仕様(K&R, C89/ISO C90, C95) においては、
// コメントは定義されていないようです。もっとも、仕様上には無くとも
コンパイラ側では使えるように拡張実装されているケースもあるようですが。
> TextBox1.Text = Regex.Replace(text, "//.*$", vbCrLf, RegexOptions.Multiline)
「char *url = "http://www.google.co.jp";」が
「char *url = "http:」になってしまうのでは?
厳密に処理するなら、長いマクロ処理などで使われる『改行前の\』も
考慮する必要があるかも知れません。
たとえば、「char *s = "yen = 100; // money : 100";」という一行のコードを
「char *s = "\」「yen = 100; // money : 100\」「";」という三行で書かれる可能性もあるわけで。
---
るきおさん宛:
本文の行末に\記号があった場合、プレビュー画面では正しく表示されますが、
掲示板への投稿結果では「\ + 改行」が削除され、一行に繋がってしまうようです。
投稿者 よねKEN  (社会人)
投稿日時
2011/2/16 13:06:34
> > 余談ですが、//コメントはC言語のコメントではなくC++のコメントですね。
> C 言語における「//」の正式サポートは、
> 『ISO/IEC 9899:1999 - Programming Language C』
> 『JIS X 3010:2003 - プログラム言語C』
> からのようですね。上記の 6.4.9 を参照。
フォローありがとうございます。
最近の仕様ならひょっとしたらC言語に取り込まれているかもと、
さらっとは原典を探してみたものの、見つからなかったのですが、
まぁ大丈夫かと安易にツッコミ入れてしまいました。
(誰かがフォローしてくれることもひそかに期待していましたが:-)
> 「char *url = "http://www.google.co.jp";」が
>「char *url = "http:」になってしまうのでは?
はい、そうなります。
るきおさんの投稿(投稿日時 2011/2/14 22:53:50)で指摘済みだったのと
その後の質問者さんの提示コードではそこまでは考慮しない形でしたので、
私の提示例も文字列リテラルの中かどうかといったことを考慮していません。
> 厳密に処理するなら
C言語の言語仕様の詳細を調べないとはっきりとは言えませんが、
コンパイルが通るソースであることを前提にするとしても、
厳密にやるとなると、字句解析と構文解析まではやらないといけないと思います。
(言語仕様によっては字句解析だけでいけるかも)
> C 言語における「//」の正式サポートは、
> 『ISO/IEC 9899:1999 - Programming Language C』
> 『JIS X 3010:2003 - プログラム言語C』
> からのようですね。上記の 6.4.9 を参照。
フォローありがとうございます。
最近の仕様ならひょっとしたらC言語に取り込まれているかもと、
さらっとは原典を探してみたものの、見つからなかったのですが、
まぁ大丈夫かと安易にツッコミ入れてしまいました。
(誰かがフォローしてくれることもひそかに期待していましたが:-)
> 「char *url = "http://www.google.co.jp";」が
>「char *url = "http:」になってしまうのでは?
はい、そうなります。
るきおさんの投稿(投稿日時 2011/2/14 22:53:50)で指摘済みだったのと
その後の質問者さんの提示コードではそこまでは考慮しない形でしたので、
私の提示例も文字列リテラルの中かどうかといったことを考慮していません。
> 厳密に処理するなら
C言語の言語仕様の詳細を調べないとはっきりとは言えませんが、
コンパイルが通るソースであることを前提にするとしても、
厳密にやるとなると、字句解析と構文解析まではやらないといけないと思います。
(言語仕様によっては字句解析だけでいけるかも)
投稿者 shu  (社会人)
投稿日時
2011/2/17 00:38:14
これでだいぶましになったと思う。まだ抜けはあると思います。
TextBox1:入力
TextBox2:出力
にしてあります。
Dim reg2 As New Regex("^(?<PRE>[^""]*"")(?<AFT>.*)$", RegexOptions.Compiled)
Dim reg3 As New Regex("^(?<PRE>[^/]*)/(?<C2>[/*])(?<AFT>.*)$", RegexOptions.Compiled)
Dim reg4 As New Regex("^(?<PRE>[^*]*)\*/(?<AFT>.*)$", RegexOptions.Compiled)
Dim reg5 As New Regex("(?<PRE>(^.*?[^\\]|^)"")(?<AFT>.*)$", RegexOptions.Compiled)
Dim rd As New StringReader(TextBox1.Text)
Dim wt As New StringBuilder
Dim blnCom = False
Dim blnStr = False
Dim m As Match
Dim strLine = rd.ReadLine
Do While strLine IsNot Nothing
Do While strLine.Length > 0
'--- コメント中
If blnCom Then
m = reg4.Match(strLine)
If m.Success Then
blnCom = False
strLine = m.Groups("AFT").Value
Else
strLine = String.Empty
End If
Continue Do
End If
'--- 文字列中
If blnStr Then
m = reg5.Match(strLine)
If m.Success Then
blnStr = False
wt.Append(m.Groups("PRE").Value)
strLine = m.Groups("AFT").Value
If strLine.Length = 0 Then
wt.AppendLine()
End If
Else
wt.AppendLine(strLine)
strLine = String.Empty
End If
Continue Do
End If
'--- 文字列の開始検知
m = reg2.Match(strLine)
If m.Success Then
blnStr = True
wt.Append(m.Groups("PRE").Value)
strLine = m.Groups("AFT").Value
If strLine.Length = 0 Then
wt.AppendLine()
End If
Continue Do
End If
'--- コメントの開始検知
m = reg3.Match(strLine)
If m.Success Then
wt.Append(m.Groups("PRE").Value)
Dim strC2 = m.Groups("C2").Value
If strC2 = "/" Then
strLine = String.Empty
wt.AppendLine()
Continue Do
Else
blnCom = True
strLine = m.Groups("AFT").Value
If strLine.Length = 0 Then
wt.AppendLine()
End If
Continue Do
End If
End If
'--- 通常行
wt.AppendLine(strLine)
strLine = String.Empty
Loop
strLine = rd.ReadLine
Loop
TextBox2.Text = wt.ToString
rd.Close()
rd.Dispose()
TextBox1:入力
TextBox2:出力
にしてあります。
Dim reg2 As New Regex("^(?<PRE>[^""]*"")(?<AFT>.*)$", RegexOptions.Compiled)
Dim reg3 As New Regex("^(?<PRE>[^/]*)/(?<C2>[/*])(?<AFT>.*)$", RegexOptions.Compiled)
Dim reg4 As New Regex("^(?<PRE>[^*]*)\*/(?<AFT>.*)$", RegexOptions.Compiled)
Dim reg5 As New Regex("(?<PRE>(^.*?[^\\]|^)"")(?<AFT>.*)$", RegexOptions.Compiled)
Dim rd As New StringReader(TextBox1.Text)
Dim wt As New StringBuilder
Dim blnCom = False
Dim blnStr = False
Dim m As Match
Dim strLine = rd.ReadLine
Do While strLine IsNot Nothing
Do While strLine.Length > 0
'--- コメント中
If blnCom Then
m = reg4.Match(strLine)
If m.Success Then
blnCom = False
strLine = m.Groups("AFT").Value
Else
strLine = String.Empty
End If
Continue Do
End If
'--- 文字列中
If blnStr Then
m = reg5.Match(strLine)
If m.Success Then
blnStr = False
wt.Append(m.Groups("PRE").Value)
strLine = m.Groups("AFT").Value
If strLine.Length = 0 Then
wt.AppendLine()
End If
Else
wt.AppendLine(strLine)
strLine = String.Empty
End If
Continue Do
End If
'--- 文字列の開始検知
m = reg2.Match(strLine)
If m.Success Then
blnStr = True
wt.Append(m.Groups("PRE").Value)
strLine = m.Groups("AFT").Value
If strLine.Length = 0 Then
wt.AppendLine()
End If
Continue Do
End If
'--- コメントの開始検知
m = reg3.Match(strLine)
If m.Success Then
wt.Append(m.Groups("PRE").Value)
Dim strC2 = m.Groups("C2").Value
If strC2 = "/" Then
strLine = String.Empty
wt.AppendLine()
Continue Do
Else
blnCom = True
strLine = m.Groups("AFT").Value
If strLine.Length = 0 Then
wt.AppendLine()
End If
Continue Do
End If
End If
'--- 通常行
wt.AppendLine(strLine)
strLine = String.Empty
Loop
strLine = rd.ReadLine
Loop
TextBox2.Text = wt.ToString
rd.Close()
rd.Dispose()
テキストファイルからC言語の「//」のコメント行を削除するプログラム(//から改行コードまで削除)を組んでいますが、自分が組んだプログラムだと最初の行が改行だったりすると無限ループに陥ってしまい回避方法が分かなく困っています。
上手く修正できる方がおられましたらよろしくお願いします。