删除注释的C ++程序

时间:2009-06-21 10:24:07

标签: c++ parsing

我正在尝试创建一个程序,该程序接受c ++代码的文本文件并输出另一个 带有该代码的文件,减去它包含的任何注释。

假设rFile和wFile定义如下:

ifstream rFile; // File stream object for read only
ofstream wFile; // File stream object for write only

rFile.open("input.txt", ios::in);
wFile.open("output.txt", ios::out);

我的第一个想法是简单地浏览文本并做相当于笔画(徽标 参考)当a(稍微改进)peek()识别/ *并且当它看到* /时笔下来。当然之后 看到//它会“竖起”直到达到\ n。

这种方法的问题在于output.txt不包含任何内容 原始空格或换行符。

这是代码(我在这个阶段甚至没有尝试删除评论):

while (!rFile.eof())
{
rFile>>first;  //first is a char
wFile<<first;
}

然后我尝试使用getline()分别获取每行代码然后添加 wFile的结尾。它到目前为止工作,但使事情变得更复杂,更少 优雅,代码不易阅读。

所以,我想知道是否有人对我有任何指示。 (没有双关语!)

N.B。这是我给予的大型家庭作业的一部分而且我是 仅限于使用C ++函数而不是C函数。

11 个答案:

答案 0 :(得分:5)

<强>更新

有人提到了这一点,但我认为get可能是比“&gt;&gt;”更好的使用功能。

原帖:

解决方案是逐个字符地读取输入,而不是使用getline()

您可以使用“&gt;&gt;”读取字符,然后使用“&lt;&lt;”输出它们。这样你就不必使用“endl”了。行终止符和空格字符将作为单个字符读入。

当您看到评论的开头时,您可以停止输出字符,直到您吃掉相应的评论终止符。

在处理“//”标记的结尾时,您还需要确保将“\ r \ n”视为单个终止符。

答案 1 :(得分:2)

您是否考虑过使用regular expressions to find the comment strings的C ++库?找到它们后,显然可以用空字符串替换它们。

答案 2 :(得分:2)

我会使用istreambuf_iterator:
这允许您一次遍历文件一个字符。

这也允许您从循环逻辑中删除处理逻辑,使您完成文件。

#include <iterator>
#include <iostream>
#include <algorithm>


class CommentFilter
{
    public:
        CommentFilter(std::ostream& output)
            :m_commentOn(false)
            ,m_output(output)
        {}

        // For each character we find call this method 
        void operator()(char c) const
        {
            // Check for a change in the comment state. (ie PenDown)
            // Leaving this for you to do.


            // Now print the stuff you want.
            if (!m_commentOn)
            {
                // If the commentOn is true then we don't print.
                // Otherwise we do.
                m_output << c;
            }
        }
    private:
        bool            m_commentOn;
        std::ostream&    m_output;
};

int main()
{
    CommentFilter   filter(std::cout);

    // The istreambuf_iterator allows you to iterate through a stream one obejct at a time.
    // In this case we define the object to be a char.
    //
    // So for each obejct (char) we find we call the functor filter with that object.
    // This means filer must have a method so that it can be called like this  filter('a')
    // To-Do this we define the operator() see-above.
    std::for_each(  std::istreambuf_iterator<char>(std::cin),
                    std::istreambuf_iterator<char>(),
                    filter
                );
}

答案 3 :(得分:1)

您的问题与using fstream to read every character including spaces and newline类似。如果要逐个字符地读取文件,包括新行和空格try istream::get

答案 4 :(得分:1)

读取每个char,并保留几个bool变量。一个bool用于字符串,另一个用于字符,其他用于转义,其他用于单行语句,其他用于块注释。

当单行语句和块注释都为“false”时,仅输出您的char。

如果找到//或/ *序列并且它不在字符串中(以便不会裁剪"/*Abc*/"),则触发充分的布尔值。

哦,我差点忘了。换行符和* /序列应将相应的注释bool设置为false。

答案 5 :(得分:1)

>>运算符不是完整的解决方案。正如你所知,它喜欢跳过空格。使用get()成员函数获取字符getline()

一旦你做完了,就开始了。

笔式,笔式方法对我来说很好看。然后出现了什么是评论的问题。

您需要跟踪引用的字符串和字符常量,以确保不会从中删除注释标记。 ('//'是合法的,虽然是实现定义的,但不会发表评论。)您可能需要注意,引用字符串中的\"??/"不会关闭字符串,类似于字符常量。您可能需要注意行尾细微差别:紧接在\??/之前的行尾不是实际行尾。 (或者你可以忽略三字母;几乎所有人都这样做。)

如果您对有限状态机(又称确定性有限自动机)有所了解,您可能希望使用该方法。从本质上讲,你在任何时候都处于某种状态,在阅读角色时你会执行一个取决于状态和角色的动作,并可能改变到另一个状态。

例如,假设您处于州READING_ALONG,并且遇到/。你什么都不写,改为SAW_A_SLASH州。如果下一个字符为*,则输入C_STYLE_COMMENT状态;如果是/,则进入CPP_STYLE_COMMENT状态,如果不是,则打印“/”和当前字符,然后返回READING_ALONG

答案 6 :(得分:1)

您需要考虑许多州:

  • 您在单引号字符串中的状态
  • 您使用双引号字符串的状态
  • 您找到//
  • 的州
  • 您找到/ *
  • 的州
  • 最后,在一行结尾处有一个\

这可能导致编译器和文本突出显示器不同意的一些非常混乱的格式化:

include <stdio>;
INT someVariable = 0;
/* where does this comment end? *\
///  I don't know
someVariable = 6;  
// most text editors don't think it ends until here --> */\
   but someVariable = 6;  shouldnt actually be commented out, and this line should be! \
this is also part of the comment ,   a "3 line " one line comment? WTF!
std::cout << someVariable << std::endl;
// even though "someVariable=6" appears to be commented out, it shouldn't be.
// so this will print "6"

// /* \
*/this text should be commented out aswell 
通过评论剥离器运行该代码应该返回:

include <stdio>;
INT someVariable = 0;
someVariable = 6;  
std::cout << someVariable << std::endl;

而有趣的部分是,当你必须有编译器错误时,请参考根据orignal mess而不是剥离版本的代码行。

答案 7 :(得分:0)

如果您不想或不能使用正则表达式,则应将STL用于以下函数:

  

find_last_off

     

find_first_of

标识您要删除的字符串的intervall。 “\ n”是该行的结尾,但有点more complex

但是你应该遵循anderstornvig建议,正则表达式现在是TR1的一部分,因此它是C ++的工具(如果你使用Visual C ++ 2008,包括快速版或最新版本的G ++,如果不使用Boost)。

寻找从哪里开始的第三个链接。

以你的例子为例:

你应该在“;”后面找“//” 匹配“//”之后的所有文本,直到行尾($ in regex term)

Aslo,你也应该考虑花括号之后的评论。 / *评论等。很多特殊情况。

Getting started with C++ TR1 regular expressions

Regular Expression Tutorial

Finding Comments in (C) Source Code Using Regular Expressions

答案 8 :(得分:0)

我认为这是一个偏离主题,因为你特意说了C ++,但我认为Perl或Python会更容易使用。对于字符串来说,C和C ++是痛苦的。

你可以:

  1. ' *\/\/.*'替换为空字符串以删除//注释和
  2. 读完文件,保留一个标志,表明你是否在/ *内 评论,如果你是的话,不写任何东西。请记住,* *注释不会 巢。
  3. 编辑:小心1号。我忘了你必须确保你不在引号内。不要使用那个正则表达式。

答案 9 :(得分:0)

我试着保持简单和简短: - )..

#include <stdio.h>


FILE *rfd,*wfd;
char ch;

void end()
{
    int c=0;
    switch((ch=fgetc(rfd)))
    {
    case '/':
            do
            {
                ch=fgetc(rfd);
                if(ch=='\n')
                    break;
            }while(ch!=EOF);
            ch=fgetc(rfd);
            return;     

    case '*':do
            {
                c++;
                ch=fgetc(rfd);
                if(ch=='*' && (fgetc(rfd))=='/')
                    break;
            }while(ch!=EOF);            
            if(ch==EOF)
                fseek(rfd,-c-1,2);
            ch=fgetc(rfd);
            return;

    default:
        fputc('/',wfd);
        return;
    }
}

int main (int argc,char **argv)
{

    rfd=fopen("read.txt","r");
    wfd=fopen("write.txt","w");

    while((ch=fgetc(rfd))!=EOF)
    {
        if(ch=='/')
                end();

        fputc(ch,wfd);
    }

    printf("\ndone ");
    fflush(stdin);
    getchar();
}

答案 10 :(得分:0)

如果您只想从字符串中删除//样式的注释:

line.erase(line.find('//'));

诀窍。