c++ อ่านตัวเลขจากไฟล์ข้อความ โดยไม่สนใจความคิดเห็น

ดังนั้นฉันจึงเห็นวิธีแก้ไขปัญหามากมายบนไซต์นี้และบทช่วยสอนเกี่ยวกับการอ่านจากไฟล์ข้อความในภาษา C ++ แต่ยังไม่ทราบวิธีแก้ไขปัญหาของฉัน ฉันยังใหม่กับภาษา C++ ดังนั้นฉันคิดว่าฉันมีปัญหาในการรวบรวมเอกสารบางส่วนเพื่อให้เข้าใจได้ทั้งหมด

สิ่งที่ฉันพยายามทำคืออ่านหมายเลขไฟล์ข้อความโดยไม่สนใจความคิดเห็นในไฟล์ที่แสดงด้วย "#" ดังนั้นไฟล์ตัวอย่างจะมีลักษณะดังนี้:

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

รหัสของฉันสามารถอ่านตัวเลขได้ดีเมื่อไม่มีความคิดเห็นใดๆ แต่ฉันไม่เข้าใจการแยกวิเคราะห์สตรีมดีพอที่จะเพิกเฉยต่อความคิดเห็น มันเป็นวิธีแก้ปัญหาการแฮ็กในตอนนี้

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

มีวิธีที่ฉันสามารถรวมการจัดการความคิดเห็นเข้ากับโซลูชันนี้ได้หรือไม่ หรือฉันต้องการแนวทางอื่น คำแนะนำใด ๆ จะดีมากขอบคุณ


person Ninja_Panda    schedule 09.11.2012    source แหล่งที่มา
comment
line.assign(line.substr(0,line.find('#'))); (เป็นคำสั่งแรกใน while-loop) จะเป็นวิธีหนึ่งในการเปลี่ยนแปลงที่จำเป็นอย่างรวดเร็ว   -  person jogojapan    schedule 09.11.2012
comment
นี่เป็นเรื่องง่ายมาก คุณบอกว่าคุณไม่เข้าใจโค้ดด้านบนดีพอที่จะแก้ไขมัน ฉันคิดว่าคุณต้องใช้เวลาทำความเข้าใจก่อนที่จะลองทำอย่างอื่น   -  person john    schedule 09.11.2012
comment
คุณได้ลองแล้วโดยมีความคิดเห็นอยู่ในไฟล์หรือไม่? ตามที่เขียนไว้ โค้ดจะละเว้นส่วนใดๆ ของบรรทัดหลังส่วนแรกซึ่งไม่ใช่ตัวเลขที่ถูกต้อง ซึ่งรวมถึงความคิดเห็นด้วย   -  person Bart van Ingen Schenau    schedule 09.11.2012
comment
โอเค ฉันคิดว่า @BartvanIngenSchenau ถูกต้อง ซึ่งเป็นสัญชาตญาณของฉันในตอนแรก แต่ฉันได้รับพฤติกรรมแปลกๆ บางอย่าง ซึ่งตอนนี้ฉันคิดว่าไม่เกี่ยวข้องกับการแยกวิเคราะห์ สิ่งที่ฉันไม่ได้แสดงที่นี่คือ ฉันใช้ไฟล์อินพุตเพื่อวาดรูปทรงเรขาคณิตจำนวนมาก และบางครั้งฉันก็มีเส้นสีแดงลากผ่านหน้าจอ ดังนั้นความคิดของฉันอาจเป็นเพราะมันกำลังทำอะไรแปลก ๆ และอ่านความคิดเห็น แต่ตอนนี้ฉันคิดว่ามันเป็นอย่างอื่น ผมจะสำรวจองค์ประกอบอื่นๆ บ้าง ขอบคุณทุกคน   -  person Ninja_Panda    schedule 10.11.2012


คำตอบ (3)


หากไฟล์ของคุณมี # อยู่ในคอลัมน์แรกเสมอ ให้ทดสอบว่าบรรทัดขึ้นต้นด้วย # ดังนี้:

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

ควรตัดเส้นช่องว่างนำหน้าและต่อท้าย ดังตัวอย่างที่แสดงไว้ที่นี่: ลบช่องว่างออกจาก std::string ใน C++

person Chris    schedule 09.11.2012
comment
หากไม่ใช่อักขระตัวแรก คุณสามารถใช้ std::find เพื่อค้นหาอักขระนั้น และใช้ std::string::erase เพื่อลบอักขระนั้นและทุกสิ่งที่ตามมา - person James Kanze; 09.11.2012
comment
ดูว่าฉันลองใช้คำสั่ง if แบบนั้นมาก่อนแล้ว และฉันได้รับข้อผิดพลาด: comparison between pointer and integer ('int' and 'const char*') - person Ninja_Panda; 10.11.2012
comment
หากคุณแทนที่ getline(input_file, line) ด้วย getline(input_file >> std::ws, line) บรรทัดความคิดเห็นของคุณก็อาจมีช่องว่างนำหน้าได้ - person Micha Wiedenmann; 12.11.2012

หากนี่เป็นเพียงการใช้งานอย่างหนึ่ง สำหรับการป้อนข้อมูลแบบบรรทัดเช่นของคุณ วิธีแก้ไขที่ง่ายที่สุดคือเพียงตัดความคิดเห็นออกจากบรรทัดที่คุณเพิ่งอ่าน:

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 ด้วย วิธีนี้จะทำให้คุณได้รับข้อความแสดงข้อผิดพลาด)

person James Kanze    schedule 09.11.2012

อีกทางเลือกหนึ่งสำหรับ "อ่านอะไลน์และแยกวิเคราะห์เป็นสตริง" สามารถใช้สตรีมเป็นบัฟเฟอร์ขาเข้าได้:

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
ตอนนี้มีตัวอย่างที่ดีของวิธีเขียนโค้ดที่อ่านไม่ได้แล้ว ไม่ต้องพูดถึงว่า if สุดท้ายไม่ถูกต้อง (ถ้าคุณไปได้ไกลถึงขนาดนั้น มันก็จะเป็นจริงเสมอ เว้นแต่จะมีข้อผิดพลาดด้านฮาร์ดแวร์) - person James Kanze; 09.11.2012
comment
@JamesKanze: ตามมาตรฐานแล้ว บิตที่ล้มเหลว (ไม่ใช่ badbit ซึ่งแตกต่างกัน) จะถูกตั้งค่าเมื่อการดำเนินการแยกล้มเหลว (เช่นเนื่องจากคุณคาดว่าจะอ่านตัวเลข แต่ e เป็นตัวเลขที่ไม่ใช่ตัวเลขที่อินพุต) ประเด็นนี้ไม่ใช่การปิดโค้ดและปล่อยให้เปิดเพื่อวิเคราะห์กรณีเพิ่มเติมเพิ่มเติม ฉันจัดรูปแบบโค้ดใหม่ แต่ประเด็นในที่นี้ไม่ใช่เพื่อให้ดูสวยงาม แต่เพื่อหลีกเลี่ยงการแนะนำหน่วยความจำแบบไดนามิกที่ไม่จำเป็นบางส่วน alloc/dealloc (โดยทั่วไปคือ string-s และ sringstream-s) - person Emilio Garavaglia; 09.11.2012
comment
ในโค้ดต้นฉบับ คุณไม่ได้ทดสอบ failbit จนกระทั่งหลังจากการป้อนข้อมูลล้มเหลว (input_file >> n ประเมินเป็นเท็จ) และหากอินพุตล้มเหลว จะต้องตั้งค่า failbit หรือ badbit badbit ถูกตั้งค่าก็ต่อเมื่อมีข้อยกเว้นจาก streambuf (ซึ่งโดยส่วนใหญ่แล้วจะไม่เป็นเช่นนั้น) ดังนั้นเมื่อคุณทดสอบ failbit มันเกือบจะถูกตั้งค่าอย่างแน่นอน เมื่อเกิดความล้มเหลว คุณ สามารถ ทดสอบ eof() เพื่อตัดสินใจว่าเป็นเพราะไม่มีอะไรให้อ่านอีก หรือเนื่องจากมีข้อผิดพลาดในรูปแบบอินพุต (ทั้งสองอย่างนี้ทำให้ failbit ถูกตั้งค่า ). - person James Kanze; 09.11.2012
comment
โดยทั่วไปการใช้ std::string จะไม่ทำให้เกิดโอเวอร์เฮดซึ่งสามารถวัดได้เมื่อเปรียบเทียบกับโอเวอร์เฮดในการอ่านไฟล์ - person James Kanze; 09.11.2012
comment
และสุดท้าย การวนซ้ำที่มีความยาวยี่สิบบรรทัดโดยมี continue ทั่วทุกตำแหน่งนั้นไม่สามารถอ่านได้โดยสิ้นเชิง (ฉันไม่สามารถนึกถึงบริบทใด ๆ ที่ continue จะส่งผลให้มีโค้ดที่อ่านได้) - person James Kanze; 09.11.2012
comment
@JamesKanze: ใช่นั่นคือสาเหตุที่ฉันลบ if ออกโดยไม่มีค่าอื่นเนื่องจากฉันไม่สามารถนึกภาพการจัดการที่เป็นไปได้อื่น ๆ แนะนำในกรณีที่อินพุตที่ไม่ใช่ int (อาจข้ามบรรทัดหรือเพียงว่างเปล่าแรก ... ขึ้นอยู่กับความหมายที่ซ่อนอยู่หลังตัวเลขเหล่านั้น) ในนั้นฉันเห็นด้วย ฉันแค่ชี้ให้เห็นว่ามันไม่ใช่แค่ความล้มเหลวของฮาร์ดแวร์ตามความคิดเห็นแรกของคุณ ตามที่คุณอธิบายไว้ในคำตอบของคุณดีกว่า ขอบคุณที่ชี้ให้เห็น! - person Emilio Garavaglia; 09.11.2012
comment
@JamesKanze: การดำเนินการต่อก็เหมือนกับการหยุดที่เหมือนกับการกลับมาที่เหมือนกับการข้ามไป ตามประสบการณ์ของฉัน ทุกคนที่ไม่ต้องการอ่านมักจะไม่สามารถอ่านได้เสมอ และทุกคนที่รู้จักก็สามารถอ่านได้อย่างสมบูรณ์แบบ แนวทางที่ไม่เคร่งศาสนาเล็กน้อยอาจช่วยให้คุณเข้าใจโค้ดที่ใช้สไตล์ที่ไม่ใช่สไตล์ที่คุณชอบมากที่สุดได้ การดำเนินการต่อเป็นกลไกที่ถูกต้องตามกฎหมายอย่างสมบูรณ์เพื่อหลีกเลี่ยงการซ้อนแบบลึก และเพื่อหลีกเลี่ยงการแนะนำสถานะปลอม มีหลายกรณีที่สิ่งนี้เพิ่มมูลค่า - person Emilio Garavaglia; 09.11.2012
comment
@JamesKanze: ไฟล์ไม่จำเป็นต้องผูกกับไฟล์ดิสก์เสมอไป ให้ OP เป็นผู้ตัดสินการแลกเปลี่ยน - person Emilio Garavaglia; 09.11.2012
comment
หากคุณรู้สึกว่าจำเป็นต้องใช้ continue แสดงว่าลูปและฟังก์ชันของคุณซับซ้อนเกินไป ฉันใช้เวลาพอสมควรในการทำความเข้าใจว่าโค้ดของคุณทำงานอย่างไร และใช้เวลานานกว่านั้นกว่าจะพบว่าโค้ดนั้นไม่ถูกต้อง continue ดีสำหรับการสร้างความสับสน แต่ไม่มีอะไรอื่น - person James Kanze; 09.11.2012
comment
และหากคุณกังวลเกี่ยวกับการจัดสรรแบบไดนามิกใน string ฯลฯ โปรดดูวิธีแก้ไขปัญหาที่สองของฉัน ไม่มี std::string ในไซต์ และอ่านได้ง่ายมาก หาก คุณคุ้นเคยกับวิธีการทำงานของ streambuf (ไม่ใช่อย่างอื่น และฉันคิดว่าความรู้ดังกล่าวมีความก้าวหน้าพอสมควรใน C++ ฉันไม่คิดว่านี่เป็นหนึ่งในสิ่งแรกที่โปรแกรมเมอร์ควรเรียนรู้) - person James Kanze; 09.11.2012
comment
@เจมส์แคนเซ่: +1 การตั้งคำถามเกี่ยวกับ continue ก็เหมือนกับการตั้งคำถามเกี่ยวกับตำแหน่งที่คุณใส่เหล็กจัดฟัน แค่ศาสนา. ใครๆก็มีเป็นของตัวเอง การใช้ streambuf เฉพาะเจาะจงนั้นน่าสนใจ แต่ฉันพบว่ามันไม่อ่านง่ายนัก ไม่ใช่เพราะสไตล์การเขียนโค้ด แต่เพราะมันไปอยู่ในด้านที่ปกติซ่อนไว้ของ stream i/o (มีกี่คนที่รู้ว่าทำไม underflow จึงใช้ที่นี่ และ sbumpc?) แต่สำหรับโค้ดวัตถุประสงค์ทั่วไปซึ่งเป็นวิธีที่จะไปได้อย่างแน่นอน (คุณสามารถโยง ตัวประมวลผลล่วงหน้า ที่แตกต่างกันได้ ดังนั้นจึงมีความยืดหยุ่นมาก) - person Emilio Garavaglia; 09.11.2012
comment
การใช้ streambuf ที่กำหนดเอง สามารถ อ่านได้ หาก คุณรู้จักโปรโตคอล streambuf อย่างไรก็ตาม ความรู้เกี่ยวกับโปรโตคอล streambuf ไม่ใช่ภาษา C++ ขั้นพื้นฐาน และมีโปรแกรมเมอร์ C++ ที่เก่งๆ จำนวนมากที่ไม่คุ้นเคย (ในทางกลับกัน ก็คุ้มค่าที่จะเรียนรู้ เนื่องจากเป็นการเปิดประตูสู่รูปแบบที่เป็นประโยชน์หลายประการ) - person James Kanze; 12.11.2012