Filestream input kickin my arse

Discussion in 'C++' started by drmario, Mar 25, 2008.

  1. drmario

    drmario Guest

    I normally hate asking for help when I feel like I should be able to get
    something on my own. But after about 10 hours of trying to figure out where
    I'm going wrong, I have to surrender.

    I need to open a file and read the data in. It will be in this format:

    Gali leo 100.0
    Michael Angelo 100.00
    Mario Hollibaugh 99.4
    Christopher Polites 92.7
    Rodney Monson 100
    Michael Jordan 74.28
    Bill Gates 95
    Joanof Arc 89.6
    How Now 94
    Dane Cook 98.8

    It's supposed to be first name, last name, test score. The only thing
    guaranteed about the format is that it will be:

    (x)firstname(x)lastname(x)grade(x)

    where x=any possible number of spaces or tabs. I'm not allowed to use
    getline, and I can't use the datatype string. I have to read the first and
    last name as cstrings, and then read the grade in as double. If the person
    who builds the text file puts in an entry like:

    95 Angelo 100.00

    well then that's their fault and the output will list 95 as Angelo's first
    name. However, if they make such an entry:

    Michael Angelo b

    he simply ends up without a grade recorded.

    The general algorithm I'm using is: (I will omit EOF checks for simplicity's
    purpose)
    1) If leading whitespace exists: (if ifstream.peek()==' ' ||
    ifstream.peek()=="\t")
    use ifstream.get to read it into a junk char var
    2) Once first char of first name is found
    use ifstream.get to read the chars of the first name, one by one,
    into a string var
    3) Once last char of first name is found
    insert ' ' after first name using simple assignment
    repeat all that for last name
    4) a simlple ifstream >> to read the grade into double variable (which
    should ignore all whitespace)
    5) Check if ifstream is good with if(!ifstream), in other words check if
    the user was a moron and can't follow formatting instructions. If not,
    clear the stream.
    6) Ignore all characters (using numeric limits) until I reach the '\n'
    character.
    7) Repeat on next line

    It's working perfectly for the first line, but for some reason it messes up
    after that. Furthermore, when I put a cout << test inside my if(!ifstream),
    it gets printed up on the screen a dozen times. I can't understand why.
    Nothing in my sample file should be causing input failure.

    If I haven't bored you to death, any suggestions? :) Thanks guys!

    cheers,
    Mario
    drmario, Mar 25, 2008
    #1
    1. Advertising

  2. drmario

    Guest

    On 25 Mrz., 11:09, "drmario" <> wrote:
    > I normally hate asking for help when I feel like I should be able to get
    > something on my own. But after about 10 hours of trying to figure out where
    > I'm going wrong, I have to surrender.


    Try this one, some STL magic but no std::strings (Note, the main()
    function does not need any variables other than the container for the
    score records and the program contains no explicit loops, checking of
    input format, or checking of stream state :)

    #include <algorithm>
    #include <cstdlib>
    #include <fstream>
    #include <iostream>
    #include <iterator>
    #include <list>

    class ScoreRecord {

    friend std::istream& operator >> ( std::istream&, ScoreRecord& );
    friend std::eek:stream& operator << ( std::eek:stream&, const
    ScoreRecord& );

    public:

    const char* FirstName() const { return firstname; }
    const char* LastName() const { return lastname; }
    double Score() const { return score; }

    private:

    char firstname[1000];
    char lastname[1000];
    double score;

    }; // class ScoreRecord

    int main() {
    std::list<ScoreRecord> scores;

    // Read the datafile
    std::copy(
    std::istream_iterator<ScoreRecord>( std::ifstream( "data.txt" ) ),
    std::istream_iterator<ScoreRecord>(),
    std::back_inserter( scores )
    );

    // Dump the records to the console.
    std::copy(
    scores.begin(),
    scores.end(),
    std::eek:stream_iterator<ScoreRecord>( std::cout, "\n" )
    );

    return 0;

    } // main()

    std::istream& operator >> ( std::istream& is, ScoreRecord& srecord ) {
    char score[1000];

    is >> srecord.firstname
    >> srecord.lastname
    >> score;

    srecord.score = atof( score );
    // returns 0 in case of garbage score

    return is;
    }

    std::eek:stream& operator << ( std::eek:stream& os, const ScoreRecord&
    srecord ) {
    return os
    << srecord.FirstName() << ' '
    << srecord.LastName() << ' '
    << srecord.Score();
    }

    best,

    Michael
    , Mar 25, 2008
    #2
    1. Advertising

  3. drmario

    drmario Guest

    Jeeze Michael thanks for that advice!! I think I'm gonna go ahead and write
    it that way just to learn about all those standard temp lib classes.
    Unfortunately this is for a fairly basic class in computer programming and I
    think the professor assumed we wouldn't know about the STL so didn't bother
    to say we can't use it. I'm gonna ask him if I can use the STLs anyway, but
    I think what he wants is for us to have to come up with a way to do this
    using only the basic functions on cstrings (strcpy, >>, get, getline, etc.
    etc.). But thank you for your input. Regardless of what my prof says I'm
    gonna explore your solution. Thanks!!

    Mario


    <> wrote in message
    news:...
    > On 25 Mrz., 11:09, "drmario" <> wrote:
    >> I normally hate asking for help when I feel like I should be able to get
    >> something on my own. But after about 10 hours of trying to figure out
    >> where
    >> I'm going wrong, I have to surrender.

    >
    > Try this one, some STL magic but no std::strings (Note, the main()
    > function does not need any variables other than the container for the
    > score records and the program contains no explicit loops, checking of
    > input format, or checking of stream state :)
    >
    > #include <algorithm>
    > #include <cstdlib>
    > #include <fstream>
    > #include <iostream>
    > #include <iterator>
    > #include <list>
    >
    > class ScoreRecord {
    >
    > friend std::istream& operator >> ( std::istream&, ScoreRecord& );
    > friend std::eek:stream& operator << ( std::eek:stream&, const
    > ScoreRecord& );
    >
    > public:
    >
    > const char* FirstName() const { return firstname; }
    > const char* LastName() const { return lastname; }
    > double Score() const { return score; }
    >
    > private:
    >
    > char firstname[1000];
    > char lastname[1000];
    > double score;
    >
    > }; // class ScoreRecord
    >
    > int main() {
    > std::list<ScoreRecord> scores;
    >
    > // Read the datafile
    > std::copy(
    > std::istream_iterator<ScoreRecord>( std::ifstream( "data.txt" ) ),
    > std::istream_iterator<ScoreRecord>(),
    > std::back_inserter( scores )
    > );
    >
    > // Dump the records to the console.
    > std::copy(
    > scores.begin(),
    > scores.end(),
    > std::eek:stream_iterator<ScoreRecord>( std::cout, "\n" )
    > );
    >
    > return 0;
    >
    > } // main()
    >
    > std::istream& operator >> ( std::istream& is, ScoreRecord& srecord ) {
    > char score[1000];
    >
    > is >> srecord.firstname
    > >> srecord.lastname
    > >> score;

    >
    > srecord.score = atof( score );
    > // returns 0 in case of garbage score
    >
    > return is;
    > }
    >
    > std::eek:stream& operator << ( std::eek:stream& os, const ScoreRecord&
    > srecord ) {
    > return os
    > << srecord.FirstName() << ' '
    > << srecord.LastName() << ' '
    > << srecord.Score();
    > }
    >
    > best,
    >
    > Michael
    drmario, Mar 25, 2008
    #3
  4. drmario

    James Kanze Guest

    On Mar 25, 11:09 am, "drmario" <> wrote:
    > I normally hate asking for help when I feel like I should be
    > able to get something on my own. But after about 10 hours of
    > trying to figure out where I'm going wrong, I have to
    > surrender.


    > I need to open a file and read the data in. It will be in this format:


    > Gali leo 100.0
    > Michael Angelo 100.00
    > Mario Hollibaugh 99.4
    > Christopher Polites 92.7
    > Rodney Monson 100
    > Michael Jordan 74.28
    > Bill Gates 95
    > Joanof Arc 89.6
    > How Now 94
    > Dane Cook 98.8


    > It's supposed to be first name, last name, test score. The only thing
    > guaranteed about the format is that it will be:


    > (x)firstname(x)lastname(x)grade(x)


    > where x=any possible number of spaces or tabs. I'm not
    > allowed to use getline, and I can't use the datatype string.


    In which case, I'd strongly suggest finding a different course,
    because this one won't teach you anything good about C++. (If I
    had to implement it in a context where std::getline and
    std::string weren't available, the first thing I'd do is
    implement something similar to them.)

    > I have to read the first and last name as cstrings, and then
    > read the grade in as double. If the person who builds the
    > text file puts in an entry like:


    > 95 Angelo 100.00


    > well then that's their fault and the output will list 95 as
    > Angelo's first name. However, if they make such an entry:


    > Michael Angelo b


    > he simply ends up without a grade recorded.


    And what do they require if the line is just:
    xxx
    If I understand correctly, the requirement is that any error in
    input format results in undefined behavior, and you don't have
    to program for it. Which is just wrong, but in that case:
    std::cin >> firstName >> lastName >> grade ;
    should do the trick. (Provided you've defined an appropriate
    type for firstName and lastName. You should never input like
    this to a char[].)

    > The general algorithm I'm using is: (I will omit EOF checks
    > for simplicity's purpose)
    > 1) If leading whitespace exists: (if ifstream.peek()==' ' ||
    > ifstream.peek()=="\t")
    > use ifstream.get to read it into a junk char var


    You don't have to read into anything. Just source.get(). Or
    just use:
    source >> std::ws ;
    However:

    You really should be inputting into a type which follows the
    standard input conventions. The most basic standard convention
    is that the >> operator look like:

    std::istream&
    operator>>( std::istream& source, SomeType& dest )
    {
    std::istream::sentry s( source ) ;
    if ( s ) {
    // actual input here...
    }
    return source ;
    }

    The constructor of the sentry object will take care of skipping
    white space.

    Note that the system's definition of white space includes new
    lines. The standard way (IMHO the only "correct" way) of
    handling this is to read line by line (using getline---but you
    should easily be able to write an equivalent function yourself),
    then parse using the standard extraction operators on an
    istringstream (or an istrstream) initialized with the line.

    > 2) Once first char of first name is found
    > use ifstream.get to read the chars of the first name, one by one,
    > into a string var


    I though you weren't allowed to use string.

    > 3) Once last char of first name is found
    > insert ' ' after first name using simple assignment
    > repeat all that for last name
    > 4) a simlple ifstream >> to read the grade into double variable (which
    > should ignore all whitespace)


    Reading into an std::string variable, or a char[], also ignores
    all whitespace.

    Including newline. If newline is significant as a record
    separator, read complete lines, then use istringstream to read
    the fields in the line.

    > 5) Check if ifstream is good with if(!ifstream), in other words check if
    > the user was a moron and can't follow formatting instructions. If not,
    > clear the stream.
    > 6) Ignore all characters (using numeric limits) until I reach the '\n'
    > character.
    > 7) Repeat on next line


    > It's working perfectly for the first line, but for some reason
    > it messes up after that. Furthermore, when I put a cout <<
    > test inside my if(!ifstream), it gets printed up on the screen
    > a dozen times. I can't understand why. Nothing in my sample
    > file should be causing input failure.


    Without seeing exactly what is happening, it's hard to say. But
    in general, the restrictions you specify seem awfully
    artificial. Unless the goal is to teach you how to parse a
    source on which you cannot back up (in which case, either a
    simple state machine or a recursive descent parser can be used).
    Note, however, that all >> operators use whitespace as
    delimiter, and skip leading whitespace. And that they consider
    a new line as whitespace, and will skip it as well.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Mar 26, 2008
    #4
  5. drmario

    drmario Guest

    I don't really have the restrictions that I implied I did. The
    professor said we can do it however we want, but gave no guidelines as to
    how much error/format checking is involved. So of course most students will
    use proven libraries (which is smart of course, cuz it's faster and more
    efficient), and assume that everything will be formatted 100% correctly, in
    order to do the least ammount of work.

    Not me. I don't like using libraries when I'm first learning, and I
    hate wizards in that capacity too. I like to know what's going on behind
    the scenes before I start using libraries that encapsulate that
    functionality. If it were up to me I'd write every program in assembly
    first, to understand what was going on, then rewrite in a higher language.
    So the restrictions you saw were self-imposed to force an understanding of
    I/O. It works too cuz a few days ago I knew only basics about C++ I/O.
    Today I'm digging through the debugger values for my ifstream variable
    looking for the value that represents ios_base::iostate. I wanna find out
    exactly when the input failure is occuring.

    Thanks for responding though, that was a lotta helpful stuff you wrote!!



    regards,

    Mario





    "James Kanze" <> wrote in message
    news:...
    On Mar 25, 11:09 am, "drmario" <> wrote:
    > I normally hate asking for help when I feel like I should be
    > able to get something on my own. But after about 10 hours of
    > trying to figure out where I'm going wrong, I have to
    > surrender.


    > I need to open a file and read the data in. It will be in this format:


    > Gali leo 100.0
    > Michael Angelo 100.00
    > Mario Hollibaugh 99.4
    > Christopher Polites 92.7
    > Rodney Monson 100
    > Michael Jordan 74.28
    > Bill Gates 95
    > Joanof Arc 89.6
    > How Now 94
    > Dane Cook 98.8


    > It's supposed to be first name, last name, test score. The only thing
    > guaranteed about the format is that it will be:


    > (x)firstname(x)lastname(x)grade(x)


    > where x=any possible number of spaces or tabs. I'm not
    > allowed to use getline, and I can't use the datatype string.


    In which case, I'd strongly suggest finding a different course,
    because this one won't teach you anything good about C++. (If I
    had to implement it in a context where std::getline and
    std::string weren't available, the first thing I'd do is
    implement something similar to them.)

    > I have to read the first and last name as cstrings, and then
    > read the grade in as double. If the person who builds the
    > text file puts in an entry like:


    > 95 Angelo 100.00


    > well then that's their fault and the output will list 95 as
    > Angelo's first name. However, if they make such an entry:


    > Michael Angelo b


    > he simply ends up without a grade recorded.


    And what do they require if the line is just:
    xxx
    If I understand correctly, the requirement is that any error in
    input format results in undefined behavior, and you don't have
    to program for it. Which is just wrong, but in that case:
    std::cin >> firstName >> lastName >> grade ;
    should do the trick. (Provided you've defined an appropriate
    type for firstName and lastName. You should never input like
    this to a char[].)

    > The general algorithm I'm using is: (I will omit EOF checks
    > for simplicity's purpose)
    > 1) If leading whitespace exists: (if ifstream.peek()==' ' ||
    > ifstream.peek()=="\t")
    > use ifstream.get to read it into a junk char var


    You don't have to read into anything. Just source.get(). Or
    just use:
    source >> std::ws ;
    However:

    You really should be inputting into a type which follows the
    standard input conventions. The most basic standard convention
    is that the >> operator look like:

    std::istream&
    operator>>( std::istream& source, SomeType& dest )
    {
    std::istream::sentry s( source ) ;
    if ( s ) {
    // actual input here...
    }
    return source ;
    }

    The constructor of the sentry object will take care of skipping
    white space.

    Note that the system's definition of white space includes new
    lines. The standard way (IMHO the only "correct" way) of
    handling this is to read line by line (using getline---but you
    should easily be able to write an equivalent function yourself),
    then parse using the standard extraction operators on an
    istringstream (or an istrstream) initialized with the line.

    > 2) Once first char of first name is found
    > use ifstream.get to read the chars of the first name, one by one,
    > into a string var


    I though you weren't allowed to use string.

    > 3) Once last char of first name is found
    > insert ' ' after first name using simple assignment
    > repeat all that for last name
    > 4) a simlple ifstream >> to read the grade into double variable (which
    > should ignore all whitespace)


    Reading into an std::string variable, or a char[], also ignores
    all whitespace.

    Including newline. If newline is significant as a record
    separator, read complete lines, then use istringstream to read
    the fields in the line.

    > 5) Check if ifstream is good with if(!ifstream), in other words check if
    > the user was a moron and can't follow formatting instructions. If not,
    > clear the stream.
    > 6) Ignore all characters (using numeric limits) until I reach the '\n'
    > character.
    > 7) Repeat on next line


    > It's working perfectly for the first line, but for some reason
    > it messes up after that. Furthermore, when I put a cout <<
    > test inside my if(!ifstream), it gets printed up on the screen
    > a dozen times. I can't understand why. Nothing in my sample
    > file should be causing input failure.


    Without seeing exactly what is happening, it's hard to say. But
    in general, the restrictions you specify seem awfully
    artificial. Unless the goal is to teach you how to parse a
    source on which you cannot back up (in which case, either a
    simple state machine or a recursive descent parser can be used).
    Note, however, that all >> operators use whitespace as
    delimiter, and skip leading whitespace. And that they consider
    a new line as whitespace, and will skip it as well.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    drmario, Mar 26, 2008
    #5
  6. drmario

    James Kanze Guest

    On 26 mar, 20:19, "drmario" <> wrote:
    > I don't really have the restrictions that I implied I did.
    > The professor said we can do it however we want, but gave no
    > guidelines as to how much error/format checking is involved.


    In which case, I would guess that not handling errors graciously
    would result in a definitely lower grade.

    > So of course most students will use proven libraries (which is
    > smart of course, cuz it's faster and more efficient),


    You'd be surprised. Most students aren't that intelligent, and
    will reinvent the wheel, rather than looking things up to do
    them correctly.

    > and assume that everything will be formatted 100% correctly,
    > in order to do the least ammount of work.


    Assuming 100% correct formatting is an error, IMHO. Unless
    you've been explicitly told that it's OK (since the goal of the
    project is to teach something else).

    > Not me. I don't like using libraries when I'm first learning,


    The line between standard libraries and the compiler is a fine
    one. In this case, as I said, if I didn't have the standard
    library, the first thing I'd do is write some sort of string
    class and a getline function. (In fact, I learned C++ long
    before there was a standard library, a my string class was
    probably the first thing I wrote. Like most other people at the
    time.)

    In the case of I/O, of course, you almost have to use the
    standard library if you want your code to be portable. The
    system level calls are not the same for Unix and for Windows.

    > and I hate wizards in that capacity too. I like to know
    > what's going on behind the scenes before I start using
    > libraries that encapsulate that functionality. If it were up
    > to me I'd write every program in assembly first, to understand
    > what was going on, then rewrite in a higher language.


    That's the way I learned. But I'm not sure it's the most
    effective way. I actually started with transistors, learned how
    to implement gates with them, then how do put the gates together
    to create registers and an adder. Before I wrote my first line
    or assembler. So where to you stop?

    The important reason for using a high level language is the
    abstraction. The fact that you don't have to understand all
    this to use it. And that even if you do understand it, you more
    or less forget it when using the language.

    > So the restrictions you saw were self-imposed to force an
    > understanding of I/O. It works too cuz a few days ago I knew
    > only basics about C++ I/O. Today I'm digging through the
    > debugger values for my ifstream variable looking for the value
    > that represents ios_base::iostate. I wanna find out exactly
    > when the input failure is occuring.


    Have fun:). One really major weakness of I/O, both in C and in
    C++, is that their error handling tends to mask the real causes.

    Personally, I think you'd be better off doing thing the opposite
    way. Write something that works first, then try to undestand
    why it works: what services the library is providing, for
    example. The try replacing some parts of the library with your
    own code.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Mar 26, 2008
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Amit
    Replies:
    8
    Views:
    3,768
    Alan Pretre
    Aug 4, 2003
  2. majiofpersia

    webservices filestream

    majiofpersia, Jul 16, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    317
    majiofpersia
    Jul 16, 2003
  3. Dorsa
    Replies:
    11
    Views:
    778
    vMike
    Jan 2, 2004
  4. =?Utf-8?B?TWFyaw==?=
    Replies:
    3
    Views:
    4,061
    Joerg Jooss
    Dec 30, 2003
  5. terrorix
    Replies:
    3
    Views:
    32,840
    Joerg Jooss
    May 8, 2004
Loading...

Share This Page