istream ostream?

I'm trying to read names and ID numbers off a file. They are formatted like this:
sclaus 12
jblack 779
jbanno 334

My program should repeatedly read a username from the istream parameter, look up the corresponding ID number in the file, and print that ID number to the ostream on a line by itself (with no other whitespace). If there is no such username, it should print "error" on a line by itself. Then keep reading usernames until there are none left (that is, until the istream has reached eof), and return 0. If the file can't open, return -1.

Any tips on what's wrong with my code?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int getIdFromFile (string filename, istream &is, ostream &os) {
	ifstream inf(filename);											
	if (!inf) {
		cout << "ERROR: Could not open file" << endl;
		return -1;
		}
	while (!inf.eof()) {
		string name;
		int ID;
		inf >> name >> ID;
				
		if (name == "") {
			os << "error" << endl;
		}
		else {
			is >> name;
			os << ID << endl;
		}
	}	
return 0;
}
And yes I have all of my proper headers
Just in case:

you need iostream and fstream.

First, stop using istream and ostream. Use ifstream and ofstream, because iheritance is 1-way: if you're using member functions from ofstream, and you pass an ostream, the compiler will catch it as an error, because members of ofstream are not members of ostream like members of ostream are members of ofstream.

line 3, use the bool good() member function of ifstream. it returns true if no error flags are set.

on Line 7: instead of using bool eof(), use bool good(). Somthing might happen to the file that corrupts in in some way, and it could create an infinite loop (because although you're not at the enf od the file, you can't read from it, and if the stream becomes corrupted it will never reach the "end").

One last correction: you can use ifstream member function peek() to check the next character without removing it from the stream buffer. I suggest writing a function to check that there's a number before retrievint it from ifstream.

example:

1
2
3
4
5
/* You will need to write 'is_number()' for this*/
if(is_number(in.peek()) && in.good())
{
    in>> a_number;
}


Lastly, I would break it down a little more. Write a function to look through the file, and a function to retrieve the data from a line.
Last edited on
Unfortunately, due to the constraints of my teacher's awful testing program, I have to use istream and ostream. I changed the program quite a bit, to where now it stores the names and IDs into separate vectors.

However, the names and IDs are not getting passed correctly to the istream and ostream

int getIdFromFile (string filename, istream &is, ostream &os) {
	
	ifstream inf(filename);								//Opens file to read input	
																	
	if (!inf) {											//If the file can't open, it prompts an error and
		cout << "ERROR: Could not open file" << endl;	//returns -1
		return -1;
		}
		
	vector<string> names;								//Vector of Names
	vector<string> IDs;									//Vector of ID numbers associated with name
	
	while (!inf.eof()) {								//While the file is still reading lines
		string temp;									
		inf >> temp;									//Ifstream takes each group of characters as a string{
		int lettertest = 0;
		for (int i=0; i<temp.size(); i++) {			//If the string contains any letters
			if (isalpha(temp[i])) {					//int lettertest will be increased by the amount of letters
				lettertest++;
			}
		}
													
		if (lettertest > 0) {						//If letter test is greater than 0, meaning that there is letters
			names.push_back(temp);					//in the temp string, it is stored to the names vector
		}
		else {
			IDs.push_back(temp);					//If there is 0, then it is put into the IDs vector
		}
	}

	for (int i=0; i<names.size(); i++) {				//For the amount of names inside the names vector
			string temp2 = names[i];
			string temp3 = IDs[i];
			is >> temp2;								//The istream reads the name of the particular spot in the vector	
			os << temp3 << endl;						//Otherwise, it outstreams the ID for that particular name on its own line
	}
	return 0;											//Returns 0 if the file was read
}
ok, you're taking the wrong approach.

First, I would cast istream and ostream as an fstream if you have to use them so badly. Here is some code I wrote to help you out:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <sstream>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>

typedef unsigned long int id_type;

//prototypes:
struct identification_data;
bool is_number(const char&);
bool retrieve_ident(identification_data&, std::istream&);
bool retrieve_idList(std::vector<identification_data>&, std::ostream&, std::istream&);


//definitions:

/* Identification information.  More can be added if needed.*/
struct identification_data
{
    std::string name = "";
    id_type id = 0;
}

/* Converts one type to another.  Only types that have the member function operators '<<' and '>>'
    are compatible. */
template<class type1, class type2>
inline type2 conv(const type1& t1)
{
    using namespace std;
    stringstream ss;
    type2 t2;
    ss<< t1;
    ss>> t2;
    return t2;
}

/* Returns true/false based on whether the given character is a number. */
bool is_number(const char& ch)
{
    bool isnum(false);
    short character(conv<char, short>(ch));
    for(short x = 0; x < 9; x++)
    {
        if(!isnum && (character == x))
        {
            isnum = true;
        }
    }
    return isnum;
}

/* Retrieves a line of identification from a stream.  True/false is returned
for success/failure, and failure is returned if no valid information is retrieved. */
bool retrieve_ident(identification_data& id, std::istream& in)
{
    //we assume the stream is good no matter what
    //this functon just handles the retrieval of the data
    id.name.erase();
    id.id = 0;
    bool success(false);
    
    if(isalpha(char(in.peek())))
    {
        in>> id.name;
    }
    if(is_number(char(in.peek())))
    {
        in>> id.id;
    }

    /* did we get a name and an id?*/
    success = ((id.name.size() > 0) && (id.id > 0));
    return success;
}

/* True/false based on success/failure.  Failure occures if the stream's error flags are
    set upon initialization, or no information is retrieved. */
bool retrieve_idList(std::vector<identification_data>& the_list, std::ostream& out, std::istream& in)
{
    using namespace std;
    bool success(false);
    the_list.erase(the_list.begin(), the_list.end());
    
    if(in.good())
    {
        //jump to the beginning of the stream, there's no way to tell where we are at first.
        in.seekg(0, ifstream::beg);
        
        //now we can assume that we are at the beginning of the stream.  Let's start reading!
        while(in.good())
        {
            the_list.push_back(identification_data()); //append data structure
            if(!retrieve_ident(the_list.back())) //retrieve information from the stream
            {
                the_list.pop_back(); //fail, so remove the 'new addition'
            }
        }
    }

    //check to see if we retrieved anything:
    if(the_list.size() > 0)
    {
        success = true;
    }
    return success;
}


If you copy/paste, then you're only hurting yourself. I'm only doing this because it's obvious you lack understanding for what you're trying to accomplish. I want to to read this, and ask questions about it so that you can eliminate areas where you're having trouble understanding this.
Last edited on
@IWishIKnew
I think you are really over-complicating things.

@Frank Magecaster
Your original code looks OK (I haven't tested it) but the loop condition could be a problem, it's usually not recomended to loop on eof. If the usernames are guaranteed to not have spaces you can use the stream extraction operator to fill the name and id (as you were originally doing), example: while(ifs >> name && ifs >> id && n != name)

http://ideone.com/gcjUR4
eh, mabey so.
Topic archived. No new replies allowed.