【问题标题】:c++ Reading numbers from text files, ignoring commentsc ++从文本文件中读取数字,忽略注释
【发布时间】:2012-10-29 12:49:11
【问题描述】:

所以我在这个网站上看到了很多关于从 C++ 中读取文本文件的解决方案和教程,但还没有找到解决我的问题的方法。我是 C++ 的新手,所以我认为我无法拼凑一些文档来理解这一切。

我要做的是读取文本文件编号,同时忽略文件中由“#”表示的 cmets。因此示例文件如下所示:

#here is my comment
20 30 40 50
#this is my last comment
60 70 80 90

当没有任何 cmets 时,我的代码可以很好地读取数字,但我无法很好地解析流以忽略 cmets。它现在是一种黑客解决方案。

/////////////////////// Read the file ///////////////////////
std::string line;
if (input_file.is_open())
{
    //While we can still read the file
    while (std::getline(input_file, line))
    {
        std::istringstream iss(line);
        float num; // The number in the line

        //while the iss is a number 
        while ((iss >> num))
        {
            //look at the number
        }
    }
}

else
{
    std::cout << "Unable to open file";
}
/////////////////////// done reading file /////////////////

有没有一种方法可以将评论处理与此解决方案结合起来,或者我是否需要一种不同的方法?任何建议都会很棒,谢谢。

【问题讨论】:

  • line.assign(line.substr(0,line.find('#')));(作为 while 循环中的第一条语句)将是快速进行必要更改的一种方式。
  • 这非常非常简单。你说你对上面的代码理解不够好,无法修改它。我认为你需要花一些时间来获得这种理解,然后再尝试其他任何事情。
  • 您是否尝试过文件中存在的 cmets?如所写,代码将忽略第一部分之后的任何非有效数字部分,包括 cmets。
  • 好的,所以我认为@BartvanIngenSchenau 是对的,这是我一开始的直觉,但我得到了一些奇怪的行为,现在我认为这些行为与解析无关。我在这里没有展示的是我正在使用文件输入来绘制一堆几何图形,有时我会在屏幕上绘制一条红线。所以我的想法是,可能是因为它正在做一些奇怪的事情并阅读 cmets,但现在我认为它是另外一回事。所以我将探索其他一些元素,谢谢大家。

标签: c++ parsing inputstream


【解决方案1】:

如果您的文件始终在第一列中包含#,那么只需测试该行是否以# 开头,如下所示:

while (std::getline(input_file, line))
{
    if (line[0] != "#" )
    {
        std::istringstream iss(line);
        float num; // The number in the line

        //while the iss is a number 
        while ((iss >> num))
        {
            //look at the number
        }
    }
}

尽管修剪前导和尾随空格的行是明智的,例如此处所示:Remove spaces from std::string in C++

【讨论】:

  • 如果它不是第一个字符,则使用std::find 查找它,然后使用std::string::erase 删除它以及随后的所有内容,这是一个简单的单行。
  • 我之前尝试过类似的 if 语句,但出现错误:comparison between pointer and integer ('int' and 'const char*')
  • 如果您将getline(input_file, line) 替换为getline(input_file &gt;&gt; std::ws, line),您的注释行可以包含前导空格。
【解决方案2】:

“读取一行并将其解析为字符串”的替代方法,可以使用流本身作为传入缓冲区:

while(input_file)
{
    int n = 0;

    char c; 
    input_file >> c; // will skip spaces ad read the first non-blank

    if(c == '#')
    {
        while(c!='\n' && input_file) input_file.get(c);
        continue; //may be not soooo beautiful, but does not introduce useless dynamic memory
    }

    //c is part of something else but comment, so give it back to parse it as number
    input_file.unget(); //< this is what all the fuss is about!
    if(input_file >> n)
    { 
        // look at the nunber
        continue;
    }

    // something else, but not an integer is there ....
    // if you cannot recover the lopop will exit 
}

【讨论】:

  • 现在有一个很好的例子来说明如何编写不可读的代码。更不用说最后一个if 是不正确的。 (如果你走得那么远,那将永远是真的,除非出现硬件错误。)
  • @JamesKanze:根据标准,当提取操作失败(例如,因为您希望读取一个数字但 e 是输入端的非数字数字)。这里的重点不是“关闭代码”并让它打开以分析更多其他案例。我重新设计了代码,但这里的重点不是让它优雅,而是避免引入一些不必要的动态内存分配/释放(典型的字符串-s和sringstream-s)。
  • 在原始代码中,您直到输入失败后才测试failbitinput_file &gt;&gt; n 评估为假)。如果输入失败,则必须设置failbitbadbitbadbit 当且仅当 streambuf 存在异常时才设置(在大多数情况下从不)。所以当你测试failbit 时,几乎可以肯定它已经设置好了。一旦出现故障,您可以测试eof(),以确定是因为没有更多可阅读的内容,还是因为输入格式有错误(两者都导致@987654330 @ 待设置)。
  • 使用std::string 通常不会导致开销,这与从文件读取的开销相比是可衡量的。
  • 最后,一个二十行长的循环,到处都是continue,完全不可读。 (我实际上想不出continue 会产生可读代码的任何上下文。)
【解决方案3】:

如果这只是一种用途,对于像您这样的面向行的输入, 最简单的解决方案就是从你刚才的行中删除评论 阅读:

line.erase( std::find( line.begin(), line.end(), '#' ), line.end() );

更通用的解决方案是使用过滤流缓冲区, 喜欢:

class FilterCommentsStreambuf : public std::streambuf
{
    std::istream& myOwner;
    std::streambuf* mySource;
    char myCommentChar;
    char myBuffer;

protected:
    int underflow()
    {
        int const eof = std::traits_type::eof();
        int results = mySource->sbumpc();
        if ( results == myCommentChar ) {
            while ( results != eof && results != '\n') {
                results = mySource->sbumpc(0;
            }
        }
        if ( results != eof ) {
            myBuffer = results;
            setg( &myBuffer, &myBuffer, &myBuffer + 1 );
        }
        return results;
    }

public:
    FilterCommentsStreambuf( std::istream& source,
                             char comment = '#' )
        : myOwner( source )
        , mySource( source.rdbuf() )
        , myCommentChar( comment )
    {
        myOwner.rdbuf( this );
    }
    ~FilterCommentsStreambuf()
    {
        myOwner.rdbuf( mySource );
    }
};

在这种情况下,你甚至可以放弃getline

FilterCommentsStreambuf filter( input_file );
double num;
while ( input_file >> num || !input_file.eof() ) {
    if ( ! input_file ) {
        //  Formatting error, output error message, clear the
        //  error, and resynchronize the input---probably by
        //  ignore'ing until end of line.
    } else {
        //  Do something with the number...
    }
}

(在这种情况下,我发现跟踪行号也很有用 FilterCommentsStreambuf。这样你就有了错误 消息。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-22
    • 2022-06-29
    • 2015-09-15
    • 2017-07-26
    • 2017-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多