c++ Membaca nomor dari file teks, mengabaikan komentar

Jadi saya telah melihat banyak solusi di situs ini dan tutorial tentang membaca dari file teks di C++, namun belum menemukan solusi untuk masalah saya. Saya baru di C++ jadi saya rasa saya mengalami kesulitan dalam mengumpulkan beberapa dokumentasi untuk memahami semuanya.

Apa yang saya coba lakukan adalah membaca nomor file teks sambil mengabaikan komentar di file yang dilambangkan dengan "#". Jadi contoh filenya akan terlihat seperti:

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

Kode saya dapat membaca angka dengan baik ketika tidak ada komentar apa pun, tetapi saya tidak cukup memahami penguraian aliran untuk mengabaikan komentar. Ini semacam solusi peretasan saat ini.

/////////////////////// 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 /////////////////

Apakah ada cara agar saya dapat menggabungkan penanganan komentar dengan solusi ini atau apakah saya memerlukan pendekatan yang berbeda? Saran apa pun akan sangat bagus, terima kasih.


person Ninja_Panda    schedule 09.11.2012    source sumber
comment
line.assign(line.substr(0,line.find('#'))); (sebagai pernyataan pertama dalam perulangan while) akan menjadi salah satu cara untuk membuat perubahan yang diperlukan dengan cepat.   -  person jogojapan    schedule 09.11.2012
comment
Ini sangat, sangat sederhana. Anda mengatakan Anda tidak cukup memahami kode di atas untuk memodifikasinya. Saya pikir Anda perlu meluangkan waktu untuk memahami hal itu sebelum mencoba hal lain.   -  person john    schedule 09.11.2012
comment
Sudahkah Anda mencobanya dengan komentar yang ada di file? Seperti yang tertulis, kode akan mengabaikan bagian mana pun dari baris setelah bagian pertama yang bukan nomor valid, termasuk komentar.   -  person Bart van Ingen Schenau    schedule 09.11.2012
comment
Oke jadi menurut saya @BartvanIngenSchenau benar, itulah intuisi saya pada awalnya, tetapi saya mendapatkan beberapa perilaku aneh yang sekarang menurut saya tidak ada hubungannya dengan penguraian. Apa yang tidak saya tampilkan di sini adalah saya menggunakan input file untuk menggambar sekumpulan geometri, dan terkadang saya mendapatkan garis merah di layar. Jadi menurutku itu mungkin karena dia melakukan sesuatu yang aneh dan membaca komentar, tapi sekarang menurutku itu hal lain. Jadi saya akan mengeksplorasi beberapa elemen lainnya, terima kasih semuanya.   -  person Ninja_Panda    schedule 10.11.2012


Jawaban (3)


Jika file Anda selalu berisi # di kolom pertama, maka uji saja, apakah barisnya dimulai dengan # seperti ini:

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

Namun adalah bijaksana untuk memangkas garis spasi putih di depan dan di belakang, seperti yang ditunjukkan di sini misalnya: Hapus spasi dari std::string di C++

person Chris    schedule 09.11.2012
comment
Jika itu bukan karakter pertama, gunakan satu kalimat sederhana std::find untuk menemukannya, dan std::string::erase untuk menghapusnya dan semua yang berikutnya. - person James Kanze; 09.11.2012
comment
Lihat, saya mencoba pernyataan if seperti itu sebelumnya, dan saya mendapatkan kesalahan: comparison between pointer and integer ('int' and 'const char*'). - person Ninja_Panda; 10.11.2012
comment
Jika Anda mengganti getline(input_file, line) dengan getline(input_file >> std::ws, line), baris komentar Anda dapat berisi spasi di depan. - person Micha Wiedenmann; 12.11.2012

Jika ini hanya salah satu kegunaannya, untuk input berorientasi baris seperti milik Anda, solusi paling sederhana adalah dengan menghapus komentar dari baris yang baru saja Anda baca:

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

Solusi yang lebih umum adalah dengan menggunakan streambuf pemfilteran, seperti:

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

Dalam hal ini, Anda bahkan dapat melupakan 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...
    }
}

(Dalam kasus seperti itu, menurut saya berguna juga untuk melacak nomor baris di FilterCommentsStreambuf. Dengan begitu, Anda dapat mengetahui pesan kesalahannya.)

person James Kanze    schedule 09.11.2012

Alternatif untuk "baca aline dan parse sebagai string", dapat menggunakan aliran itu sendiri sebagai buffer masuk:

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 
}
person Emilio Garavaglia    schedule 09.11.2012
comment
Sekarang ada contoh bagus tentang cara menulis kode yang tidak dapat dibaca. Belum lagi if terakhir salah. (Jika Anda sampai sejauh itu, hasilnya akan selalu benar, kecuali ada kesalahan perangkat keras.) - person James Kanze; 09.11.2012
comment
@JamesKanze: Menurut standar, failbit (bukan badbit, berbeda) diatur ketika operasi ekstraksi gagal (misalnya karena Anda berharap membaca angka tetapi e adalah digit non-numerik pada input). Intinya di sini, bukan untuk menutup kode dan membiarkannya terbuka untuk menganalisis kasus tambahan lebih lanjut. Saya menata ulang kodenya, tetapi intinya, di sini, bukan untuk membuatnya elegan, tetapi untuk menghindari pengenalan beberapa alokasi/dealloc memori dinamis yang tidak diperlukan (khas dengan string-s dan sringstream-s). - person Emilio Garavaglia; 09.11.2012
comment
Dalam kode asli, Anda tidak menguji failbit sampai input gagal (input_file >> n dinilai salah). Dan jika input gagal, failbit atau badbit harus disetel; badbit disetel jika dan hanya jika ada pengecualian dari streambuf (yang dalam banyak kasus tidak pernah terjadi). Jadi saat Anda menguji failbit, hampir pasti sudah disetel. Setelah terjadi kegagalan, Anda dapat menguji eof(), untuk memutuskan apakah hal tersebut disebabkan karena tidak ada lagi yang perlu dibaca, atau karena terdapat kesalahan dalam format input (keduanya menyebabkan failbit disetel ). - person James Kanze; 09.11.2012
comment
Menggunakan std::string biasanya tidak akan menyebabkan overhead yang dapat diukur dibandingkan dengan overhead pembacaan file. - person James Kanze; 09.11.2012
comment
Dan akhirnya, sebuah loop yang panjangnya dua puluh baris, dengan continue di mana-mana, sama sekali tidak dapat dibaca. (Saya tidak dapat memikirkan konteks apa pun di mana continue akan menghasilkan kode yang dapat dibaca.) - person James Kanze; 09.11.2012
comment
@JamesKanze: ya, itu sebabnya saya menghapus if, tidak memiliki nilai lain, karena saya tidak dapat membayangkan apa yang disarankan penanganan lain jika ada input non-int (mungkin melewati baris atau hanya sampai kosong pertama... Tergantung semantik apa yang tersembunyi di balik angka-angka itu). Dalam hal itu saya setuju. Saya hanya menunjukkan bahwa ini bukan hanya kegagalan perangkat keras seperti komentar pertama Anda. Seperti yang Anda jelaskan dengan lebih baik dalam balasan Anda. Terima kasih telah menunjukkannya! - person Emilio Garavaglia; 09.11.2012
comment
@JamesKanze: lanjutkan itu seperti istirahat itu seperti kembali itu seperti goto. Sesuai pengalaman saya, mereka selalu tidak dapat dibaca oleh siapa pun yang tidak ingin membacanya, dan dapat dibaca dengan sempurna oleh siapa pun yang mengetahuinya. Pendekatan yang kurang religius dapat membantu Anda mengecilkan kode yang menggunakan gaya yang paling tidak Anda sukai. Melanjutkan adalah mekanisme yang sah untuk menghindari penumpukan yang mendalam dan untuk menghindari masuknya negara palsu. Ada kalanya hal ini menambah nilai. - person Emilio Garavaglia; 09.11.2012
comment
@JamesKanze: suatu file tidak selalu terikat ke file disk. biarkan OP memutuskan trade-off. - person Emilio Garavaglia; 09.11.2012
comment
Jika Anda merasa membutuhkan continue, loop dan fungsi Anda terlalu rumit. butuh beberapa saat bagi saya untuk memahami cara kerja kode Anda, dan bahkan lebih lama lagi untuk mengetahui bahwa kode itu salah. continue bagus untuk kebingungan, tapi tidak untuk yang lain. - person James Kanze; 09.11.2012
comment
Dan jika Anda khawatir tentang alokasi dinamis di string, dll., lihat solusi kedua saya. Tidak ada std::string di situs. Dan sangat mudah dibaca jika Anda memahami cara kerja streambuf. (Bukan sebaliknya, dan saya akan menganggap pengetahuan seperti itu sebagai C++ tingkat lanjut; menurut saya itu bukan hal pertama yang harus dipelajari seorang programmer.) - person James Kanze; 09.11.2012
comment
@JamesKanze: +1. mempertanyakan tentang continue seperti mempertanyakan di mana Anda memasang kawat gigi. Hanya agama. Siapapun punya miliknya sendiri. Penggunaan streambuf tertentu memang menarik, tetapi menurut saya tidak begitu mudah dibaca. Bukan karena gaya pengkodeannya, tetapi karena ia masuk ke sisi aliran i/o yang biasanya tersembunyi. (Berapa banyak yang tahu mengapa underflow digunakan di sini? dan sbumpc?) Namun untuk kode tujuan umum, itulah cara yang tepat (Anda bahkan dapat merangkai praprosesor yang berbeda, sehingga bisa sangat fleksibel) - person Emilio Garavaglia; 09.11.2012
comment
Penggunaan streambuf khusus adalah dapat dibaca jika Anda mengetahui protokol streambuf. Namun, pengetahuan tentang protokol streambuf bukanlah dasar C++, dan ada banyak pemrogram C++ yang sangat baik yang belum mengenalnya. (Di sisi lain, ini sangat berharga untuk dipelajari, karena ini membuka pintu ke beberapa pola yang berguna.) - person James Kanze; 12.11.2012