15 character limit in string attributes in Classes

Pages: 12
Hi
I have created the following class

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
include <iostream>
include <string>

class Staff{
private:
	int StaffNumber;
	string Name;
public:
	 void Person::createNew(){
		int i;
		string s,t;

		cout << "Enter No:  ";
		getline( cin, t);
		if (!(istringstream( t ) >> i))
			i = 0;
     
		cout << "Enter Name:";
		getline( cin, s );
		StaffNumber = i;
		Name = s;
 	
	}
	void Person::viewDetails() {
		cout << "\nStaffNumber:	" << StaffNumber;
		cout << "\t| Name: " << Name;
	}
};


then on the main() method i save this object to a file and then i can read the object from the file as well.. This works fine as long as i enter less than or equal to 15 characters for the string Name.
Suppose i call the createNew() method and enter a number first and then i enter a string of more than 15 characters for the name and save the object to the file. Then i read the object from the file and put it into a variable and when i use viewDetails() method to display the details i see some gibberish instead of the Name. And then i get this error

Unhandled exception at 0x65907a8b (msvcp90d.dll) in Staff Personnel System.exe: 0xC0000005: Access violation reading location 0x006f68b0.

It seems there is some kind of 15 character limit for strings.
I want to store the object with Name at any length.

Can anyone help me on this?
Last edited on
Your problem is where you are saving/loading from disk. There is no limit on the length of a std::string, it sounds like you are just hastily overwriting a strings internal representation/buffer.
Could you also post the code that writes and reads the class to/from file
I have created a class called FManipulator to do this.

This method will add a new record to the file.
1
2
3
4
5
6
7
void FManipulator::insert(Person person){
    ofstream file;
    file.open(filename, ios_base::out | ios_base::app | ios_base::binary);
    file.seekp(ios_base::end);
    file.write ( reinterpret_cast<const char *>(&person), sizeof(person) );
    file.close();
}


This method will display the list of all the staff.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void FManipulator::displayList(){
    
    ifstream file;
    Person person;
    file.open(filename, ios_base::in | ios_base::binary );
    file.seekg(0);

    while( file.read( reinterpret_cast<char *>(&person), sizeof(person) ) ){
        person.viewDetails();
        cout << " SIZE: " << sizeof(person);
    }
	cout << "\n";
    file.close();
}


It doesn't give me error when i insert a record, but when i display the records it gives an error at the exact position in the while loop where the string Name exceeds 15 characters. The whole thing works fine as long as i dont enter strings longer than 15 chars.
I'm pretty sure that would only work for POD types. You can't expect to save a pointer (memory address) and then read it and the data still exist. Similar to a deep copy, you need to do a "deep save" if there is such a thing. std::string uses dynamically allocated memory, so you can't just forcefully save it in this manor. If it works in any case, I think you're just getting lucky.
Last edited on
I agree with you moorecm.
the sizeof(person) is always the same even though I'm using string. So I gave it a try to save the objects into the file and it worked, because all objects are of the same size. When I input more than 15 chars into the string I can still write it to the file. But I think the size of that specific object will not be the same as the others and therefore when I retrieve the data using the same size as others, it gives error. That's what I think.

I think the "string" in C++ works something like a vector which has an initial size of 15 and when more characters are assigned it's size is increased dynamically. Therefore when I read the record I must use the size of that specific record, but we don't know which record it is right.

I started C++ only 2 months ago but I'm familiar with other languages including basics of C, so I don't really know how C++ works
sizeof(std::string) will return 15 because that is the size of the class, not the size of the string. Use the .size() method to get the actually size.
i believe i'm lacking some knowledge regarding reading and writing objects into files. The .size() method might give me the size of the string but the string is located inside the class 'Person' and i'm saving objects of this class.
Ah, I see.

Ok, your method won't work because:
moorecm wrote:
that would only work for POD types


std::string is not a POD type, thus your class is not a POD type.
:D i guess I should change the string to a character array of size 30. i think this should solve the problem for now.
You could write your own write/read methods and still use std::string. The methods would just have to operate member by member and in the case of the string, write it's contents or read a temporary and construct the string.
You can't do a binary save on an object containing a std::string. You need to write your own input/output operators:

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
#include <iostream>
#include <fstream>
#include <string>

class Staff{
	// give input/output operator functions access to this class
	friend std::istream& operator>>(std::istream& is, Staff& s);
	friend std::ostream& operator<<(std::ostream& os, const Staff& s);
private:
	int StaffNumber;
	std::string Name;
public:
	 void createNew(){
		// ... etc...
		 StaffNumber = 7;
		 Name = "wibble";
	}

	 void print()
	 {
		 std::cout << "StaffNumber: " << StaffNumber << std::endl;
		 std::cout << "Name       : " << Name << std::endl;
	 }
};

// Read Staff from stream
std::istream& operator>>(std::istream& is, Staff& s)
{
	is >> s.StaffNumber >> std::ws;
	std::getline(is, s.Name);
	return is;
}

// Write Staff to stream
std::ostream& operator<<(std::ostream& os, const Staff& s)
{
	os << s.StaffNumber << ' ' << s.Name << '\n';
	return os;
}

int main()
{
	Staff s;
	s.createNew();
	std::ofstream ofs("test.txt");
	ofs << s; // write s to file
	ofs.close();

	s.print();

	Staff t;
	std::ifstream ifs("test.txt");
	ifs >> t; // read t from file

	t.print();
}
StaffNumber: 7
Name       : wibble
StaffNumber: 7
Name       : wibble
Last edited on
Thanks Galik,
So basically you're writing the member variables alone. I got one question, would this allow me to save more than one object into the file and read from specific positions?
You can read and write multiple objects. But each object is of variable length because you don't know how long each string is. So you can't access the file randomly (specific positions). You would have to read through from the beginning.
i see.

How would you then modify one of the objects and replace it with a longer string? Wouldn't it overwrite the next adjacent object?
To do that you would have to modify the output functions to always use fixed lengths. So when you output the string you could truncate it if it is too long, or ensure the user can not enter more than a specified amount.

Then you can position were you read and write exactly to align with each record using seekg() and seekp().
Umm, yea that's the problem. I want the user to enter a string of any length not a fixed length and also be able to access the file from any random location.
Then perhaps you need an index. That is a file containing fixed length records that point to the position of the actual data. To be honest when things get this complex I generally turn to a database. They are designed for this.
the thought of creating another file for this has been in my mind for a while now, but i thought its a stupid idea so i dint do it :D
so i guess i'm gonna do that now and that will solve the entire problem...
actually i cud do this without dynamic lengths, OR sequential reading of file, but i just wanna know how to random access with dynamic lengths.

Also i am required to do this only with files not database.

You have been a great help for me. I'm now actually using the code that you posted above instead of my original code.
Thanks a lot
Okay, but the index route is not as simple as it looks. When you come to deleting files you end up leaving differently sized holes in your data file. So then you have the problem of finding a free space to store new records in. Also if you edit a record and the data grows too large for the space already allocated. You then need to re-allocate it leaving a hole where it once was. So the data becomes 'fragmented' over time. This is actually why you have a "defrag" option for your hard disk. If this is a college assignment I would be tempted to use fixed length strings and save myself from all the grief.
Last edited on
Pages: 12