簡體   English   中英

如何從文件中的特定行獲取行()? C++

[英]How to getline() from specific line in a file? C++

我環顧四周,並沒有找到關於如何從 C++ 文件中讀取特定文本行的明確答案。 我有一個包含超過 100,000 個英文單詞的文本文件,每個單詞都在自己的行中。 我不能使用 arrays 因為它們顯然不會保存那么多數據,並且向量需要太長時間來存儲每個單詞。 我怎樣才能做到這一點?

PS我沒有發現關於C++這個問題的重復

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

編輯:

下面的評論者幫助我意識到將文件加載到向量的唯一原因是因為我正在調試。 簡單地運行 .exe 幾乎可以立即加載文件。 感謝大家的幫助。

你有幾個選項,但沒有一個會自動讓你 go 到特定的行。 文件系統不跟蹤文件中的行號。

一種方法是在文件中使用固定寬度的行。 然后根據您想要的行號和每行的字節數讀取適當數量的數據。

另一種方法是循環,一次讀取一行,直到你到達你想要的那一行。

第三種方法是在文件開頭創建一種索引來引用每行的位置。 當然,這需要您控制文件格式。

如果您的單詞沒有空格(我假設他們沒有),您可以使用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默認值(任何空格 - 這是我的一個很大的假設)對字符串進行標記,這使得它稍微快一點。 100,000 個單詞在大約 2-3 秒內完成。 我還使用了deque ,這是這個特定場景的最佳數據結構 (imo)。 當我使用向量時,大約需要 20 秒(甚至不接近你的分鍾標記——你必須在做其他增加復雜性的事情)。

要訪問第 1 行的單詞:

cout << dictionary[0] << endl;

希望這很有用。

我已經在評論中提到了這一點,但我想為遇到此問題的其他人提供更多的可見性......

我認為從文件中讀取以下代碼需要很長時間,因為std::vector可能必須多次重新分配其內部 memory 以考慮您添加的所有這些元素。 這是一個實現細節,但如果我理解正確std::vector通常從小開始,並根據需要增加其 memory 以適應新元素。 當您一次添加少量元素時,這很好用,但是當您一次添加一千個元素時效率非常低。

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

因此,在運行上面的循環之前,嘗試使用my_vect(100000) (具有指定元素數量的構造函數)初始化向量。 這會強制std::vector提前分配足夠的 memory ,這樣它就不需要在以后洗牌了。

這個問題非常不清楚。 如何確定具體線路? 如果是第 n 行,最簡單的解決方案就是調用getline n 次,將除最后一個結果之外的所有結果都扔掉; 調用ignore n-1 次可能會稍微快一些,但我懷疑如果你總是讀入同一個字符串(而不是每次都構造一個新字符串),時間上的差異不會很大。 如果您有一些其他條件,並且文件真的很大(根據您的描述不是)並且已排序,您可以嘗試使用二進制搜索,尋找文件的中間,提前閱讀足夠的內容以找到下一行,然后根據它的值決定下一步。 (我用它來查找日志文件中的相關條目。但我們談論的是幾 GB 大小的文件。)

如果您願意使用系統相關的代碼,它可能對 memory map 文件有利,然后搜索第 n 次出現 '\n' ( std::find n 次)。

添加:只是一些快速的基准測試。 在我的 Linux 框中,從/usr/share/dict/words獲取第 100000 個單詞(在我的機器上,每行 479623 個單詞)大約需要

  • 272 毫秒,將所有單詞讀入std::vector ,然后進行索引,
  • 256 毫秒做同樣的事情,但使用std::deque
  • 使用getline 30 毫秒,但只是忽略結果,直到我感興趣的結果,
  • 使用istream::ignore 20 毫秒,並且
  • 使用mmap並在std::find上循環需要 6 毫秒。

FWIW,每種情況下的代碼是:

對於 std:: 容器:

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 );
}

在每種情況下,代碼都會運行

您可以尋找特定的 position,但這需要您知道線路的起點。 100,000 個單詞的“不到一分鍾”對我來說聽起來確實很慢。

讀取一些數據,計算換行符,丟棄該數據並讀取更多,再次計算換行符......並重復,直到您閱讀了足夠的換行符以達到目標。

此外,正如其他人所建議的那樣,這不是一種特別有效的數據訪問方式。 制作索引會為您提供良好的服務。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM