如何从文件中的特定行获取getline()? C ++

时间:2011-05-31 01:34:10

标签: c++

我看了一下,并没有找到关于如何从C ++文件中读取特定文本行的明确答案。我有一个包含超过100,000个英文单词的文本文件,每个单词都有自己的行。我不能使用数组,因为它们显然不会保存那么多数据,并且向量需要很长时间来存储每个单词。我怎样才能做到这一点?

P.S。我发现这个关于C ++的问题没有重复

while (getline(words_file, word))
{
    my_vect.push_back(word);
}

编辑:

下面的评论者帮助我意识到将文件加载到向量的唯一原因是因为我正在调试。简单地运行.exe几乎立即加载文件。感谢大家的帮助。

6 个答案:

答案 0 :(得分:3)

您有几个选项,但没有一个会自动让您转到特定的行。文件系统不跟踪文件中的行号。

一种方法是在文件中使用固定宽度的线条。然后根据您想要的行号和每行的字节数读取适当数量的数据。

另一种方法是循环,一次读取一行,直到你到达你想要的那一行。

第三种方法是在文件开头创建一种索引,以引用每行的位置。当然,这需要您控制文件格式。

答案 1 :(得分:3)

如果您的单词没有空白区域(我假设它们没有),您可以使用deque使用更棘手的非getline解决方案!

using namespace std; 

int main() {
    deque<string> dictionary;

    cout << "Loading file..." << endl;
    ifstream myfile ("dict.txt");
    if ( myfile.is_open() ) {
        copy(istream_iterator<string>(myFile),
             istream_iterator<string>(),
             back_inserter<deque<string>>(dictionary));
        myfile.close();
    } else {
        cout << "Unable to open file." << endl;
    }

    return 0;
}

以上内容将整个文件读入一个字符串,然后根据std::stream默认值(任何空格 - 这是我的假设)对字符串进行标记,这使得它略有不同快点。这可以在大约2-3秒内完成,100,000字。我也使用deque,这是这个特定场景的最佳数据结构(imo)。当我使用向量时,它需要大约20秒(甚至不接近你的分钟标记 - 你必须做一些其他会增加复杂性的事情)。

要访问第1行的字词:

cout << dictionary[0] << endl;

希望这很有用。

答案 2 :(得分:1)

我在评论中已经提到过这个问题,但我想让其他人遇到这个问题时更多地了解......

我认为以下代码需要很长时间才能从文件中读取,因为std::vector可能需要多次重新分配其内部内存以考虑您要添加的所有这些元素。这是一个实现细节,但如果我理解正确std::vector通常从小开始,并根据需要增加其内存以容纳新元素。当你一次添加一些元素时,这种方法很好,但是当你一次添加一千个元素时效率非常低。

while (getline(words_file, word)) {
    my_vect.append(word); }

因此,在运行上面的循环之前,尝试使用my_vect(100000)(具有指定元素数量的构造函数)初始化向量。这会强制std::vector提前分配足够的内存,以便以后不需要随意改变内容。

答案 3 :(得分:1)

这个问题非常不清楚。你如何确定具体的 线?如果它是第n行,最简单的解决方案就是调用 getline n次,抛出除最后结果之外的所有结果;调用 ignore n-1次可能稍快一点,但我怀疑是否 你总是在读同一个字符串(而不是构建一个字符串 每次新的),时间的差异不会很大。如果你 有一些其他标准,文件真的很大(从你的 描述它不是)并排序,您可以尝试使用二进制搜索, 寻找到文件的中间,提前阅读,找到了 开始下一行,然后根据它确定下一步 值。 (我用它来查找日志文件中的相关条目。但是 我们谈论的是几千兆字节的文件。)

如果您愿意使用系统相关代码,则可能更有利 记忆映射文件,然后搜索'\ n'的第n次出现 (std::find n次)。

增加:只是一些快速的基准测试。在我的Linux机器上,获取 来自/usr/share/dict/words的第100000个单词(479623个单词,每行一个, 在我的机器上),拿走

  • 272毫秒,阅读所有单词 进入std::vector,然后编制索引,
  • 256毫秒做同样的事,但是 与std::deque
  • 使用getline 30毫秒,但是 只是忽略了结果,直到 一个我感兴趣的,
  • 使用20毫秒 istream::ignore
  • 使用mmap和6毫秒 循环std::find

FWIW,每种情况下的代码是:

对于std :: containers:

template<typename Container>
void Using<Container>::operator()()
{
    std::ifstream input( m_filename.c_str() );
    if ( !input )
        Gabi::ProgramManagement::fatal() << "Could not open " << m_filename;
    Container().swap( m_words );
    std::copy( std::istream_iterator<Line>( input ),
               std::istream_iterator<Line>(),
               std::back_inserter( m_words ) );
    if ( static_cast<int>( m_words.size() ) < m_target )
        Gabi::ProgramManagement::fatal() 
            << "Not enough words, had " << m_words.size()
            << ", wanted at least " << m_target;
    m_result = m_words[ m_target ];
}

getline未保存:

void UsingReadAndIgnore::operator()()
{
    std::ifstream input( m_filename.c_str() );
    if ( !input )
        Gabi::ProgramManagement::fatal() << "Could not open " << m_filename;
    std::string dummy;
    for ( int count = m_target; count > 0; -- count )
        std::getline( input, dummy );
    std::getline( input, m_result );
}

ignore

void UsingIgnore::operator()()
{
    std::ifstream input( m_filename.c_str() );
    if ( !input )
        Gabi::ProgramManagement::fatal() << "Could not open " << m_filename;
    for ( int count = m_target; count > 0; -- count )
        input.ignore( INT_MAX, '\n' );
    std::getline( input, m_result );
}

对于mmap

void UsingMMap::operator()()
{
    int input = ::open( m_filename.c_str(), O_RDONLY );
    if ( input < 0 )
        Gabi::ProgramManagement::fatal() << "Could not open " << m_filename;
    struct ::stat infos;
    if ( ::fstat( input, &infos ) != 0 )
        Gabi::ProgramManagement::fatal() << "Could not stat " << m_filename;
    char* base = (char*)::mmap( NULL, infos.st_size, PROT_READ, MAP_PRIVATE, input, 0 );
    if ( base == MAP_FAILED )
        Gabi::ProgramManagement::fatal() << "Could not mmap " << m_filename;
    char const* end = base + infos.st_size;
    char const* curr = base;
    char const* next = std::find( curr, end, '\n' );
    for ( int count = m_target; count > 0 && curr != end; -- count ) {
        curr = next + 1;
        next = std::find( curr, end, '\n' );
    }
    m_result = std::string( curr, next );
    ::munmap( base, infos.st_size );
}

在每种情况下,代码都会运行

答案 4 :(得分:0)

您可以寻找特定的位置,但这需要您知道线的起始位置。对于100,000字而言,“不到一分钟”对我来说听起来很慢。

答案 5 :(得分:0)

读取一些数据,计算新行,丢弃该数据并阅读更多内容,再次计算新行...并重复,直到您阅读了足够的新行来达到目标​​。

此外,正如其他人所建议的那样,这不是一种特别有效的访问数据的方式。你可以通过制作索引来获得良好的服务。