簡體   English   中英

在C ++中從文件中獲取輸入時如何跳過讀取“:”

[英]How skip reading “ : ” while taking input from a file in C++

假設我要輸入文件第一行中的小時,分​​鍾和秒,並將它們分別存儲到3個不同的變量中,分別是小時,分鍾和秒。 我想不出一種簡單的方法來跳過閱讀冒號(“:”)。

輸入文件示例:

12:49:00

商店:

hrs = 12
mins = 59
sec = 00

您可以使用std::regex進行匹配,范圍檢查和驗證輸入。

#include <iostream>
#include <regex>
#include <string>

int main()
{
    const std::regex time_regex("(\\d|[0,1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)");
    std::smatch time_match;
    std::string line;

    while (std::getline(std::cin, line))
    {
        if (std::regex_match(line, time_match, time_regex))
        {
            int hours = std::stoi(time_match[1]);
            int minutes = std::stoi(time_match[2]);
            int seconds = std::stoi(time_match[3]);
            std::cout << "h=" << hours << " m=" << minutes << " s=" << seconds << std::endl;
        }
        else
        {
            std::cout << "Invalid time: " << line << std::endl;
        }
    }

    return 0;
}

看到這個例子住在這里

分解正則表達式(\\\\d|[0,1]\\\\d|2[0-3]):([0-5]\\\\d):([0-5]\\\\d)

  1. \\d|[0,1]\\d|2[0-3]匹配小時(24小時制),該時間是以下之一:

    • \\d :0-9
    • [0,1]\\d :01-19
    • 2[0-3] :20-23
  2. [0-5]\\d匹配分鍾:兩位數字00-59

  3. [0-5]\\d匹配秒:兩位數字00-59,如上所述。

不使用臨時字符跳過冒號的替代方法:

#include <iostream>
int main()
{
        int h,m,s;
        std::cin >> h;
        std::cin.ignore(1) >> m;
        std::cin.ignore(1) >> s;
        std::cout << h << ':' << m << ':' << s << std::endl;
        return 0;
}

這似乎可行:

int h, m, s;                                                                                                                                                                                                                               
char c;                                                                                                                                                                                                                                    
cin >> h >> c >> m >> c >> s;

您只需以這種方式跳過:符號。 我不知道這是否是一個好的解決方案。

使用cin.ignore

cin >> h;                                                                                                                                                                                                                                  
cin.ignore(1);                                                                                                                                                                                                                             
cin >> m;                                                                                                                                                                                                                                  
cin.ignore(1);                                                                                                                                                                                                                             
cin >> s;

已經有好幾個答案,一個已經被接受。 但是,我想提出我的解決方案,不僅是對您的問題的有效答案,而且還是關於良好設計實踐的建議。 恕我直言,當它涉及從文件中讀取信息並將其內容存儲到變量或數據結構中時,我更喜歡以特定方式進行操作。 我喜歡將特定操作的功能和職責分為各自的功能:


  • 1:我首先喜歡具有打開文件,讀取內容並將信息存儲到字符串,流或大緩沖區中的功能。 一旦從文件中讀取了適當數量的信息,該函數將在處理完文件后關閉文件句柄,然后返回結果。 有幾種方法可以做到這一點,但它們都是相似的。
    • 答:從文件中讀取一行,然后返回字符串或流。
    • b:逐行讀取文件中的所有信息,並將每行存儲到其自己的字符串或流中,然后返回這些字符串或流的向量。
    • c:將文件的所有內容讀入單個字符串,流或大緩沖區,然后將其返回。
  • 2:擁有該文件的內容之后,通常將調用一個函數,該函數將解析該數據,並且這些函數將根據要使用的數據結構而根據需要解析的內容類型而有所不同。 同樣,這些解析函數將調用一個函數,該函數會將字符串拆分為稱為標記的字符串向量。 調用分割字符串函數后,數據解析將使用字符串操縱器-轉換器將字符串轉換為正在使用的當前數據結構所需的內置類型,並將其存儲到通過引用傳遞。
  • 3:我的splitString函數有兩種變體。
    • 答:一個字符作為分隔符。
    • b:另一個將字符串作為分隔符。
    • c:這兩個函數都將根據使用的分隔符返回一個字符串向量。

這是我使用此文本文件進行輸入的代碼示例。

time.txt

4:32:52

main.cpp

#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <exception>

struct Time {
    int hours;
    int minutes;
    int seconds;
};

std::vector<std::string> splitString( const std::string& s, char delimiter ) {
    std::vector<std::string> tokens;
    std::string token;
    std::istringstream tokenStream( s );
    while( std::getline( tokenStream, token, delimiter ) ) {
        tokens.push_back( token );
    }

    return tokens;
}

std::string getLineFromFile( const char* filename ) {
    std::ifstream file( filename );
    if( !file ) {
        std::stringstream stream;
        stream << "failed to open file " << filename << '\n';
        throw std::runtime_error( stream.str() );
    }

    std::string line;
    std::getline( file, line );

    file.close();

    return line;
}

void parseLine( const std::string& fileContents, Time& time ) {
    std::vector<std::string> output = splitString( fileContents, ':' );

    // This is where you would want to do your sanity check to make sure
    // that the contents from the file are valid inputs before converting
    // them to the appropriate types and storing them into your data structure.

    time.hours = std::stoi( output.at( 0 ) );
    time.minutes = std::stoi( output.at( 1 ) );
    time.seconds = std::stoi( output.at( 2 ) );
}

int main() {
    try {
        Time t;
        std::string line = getLineFromFile( "time.txt" );
        parseLine( line, t );

        std::cout << "Hours: " << t.hours << '\n'
            << "Minutes: " << t.minutes << '\n'
            << "Seconds: " << t.seconds << "\n\n";

    } catch( std::runtime_error& e ) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

輸出:

Hours: 4
Minutes: 32
Seconds: 52

現在,您可以看到在這種特殊情況下,此處使用的功能僅設計為從文件中讀取一行,當然也從文件中讀取第一行。 我的庫中還有其他未在此處顯示的功能,這些功能將讀取文件的每一行,直到沒有更多的行可供讀取,或將所有文件讀取到單個緩沖區中為止。 我有另一個版本的拆分字符串,它將字符串作為分隔符,而不是單個字符。 最后,對於解析函數,由於每個解析函數都依賴於您要使用的數據結構,因此最終將是唯一的。

這使代碼易於閱讀,因為每個函數都執行了應做的事情,僅此而已。 我更喜歡這種設計,而不是試圖從文件中獲取信息並嘗試在文件打開時解析它。 打開文件時,如果發生錯誤或損壞的數據,但是編譯器沒有抱怨的地方,太多的事情可能出錯,那么您的變量或數據結構可能包含無效信息,而您卻沒有意識到。 至少以這種方式,您可以打開文件,從文件中獲取所需內容,並將其存儲到字符串或字符串向量中,完成讀取后關閉文件,然后返回內容。 然后,標記數據后,解析功能將負責測試數據。 現在,在上面顯示的當前解析函數中,我沒有做任何健全性檢查來使事情保持簡單,但這是您在返回填充的數據結構之前測試數據以查看信息是否有效的地方。


如果您對從文件中讀取多行的此版本的另一個版本感興趣,只需注釋一個請求,然后我會將其附加到此答案中。


暫無
暫無

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

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