Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

How to append chars to a c-style string

Posted on 2009-02-22
13
Medium Priority
?
2,131 Views
Last Modified: 2012-05-06
Hi experts,

I am building a string class for a Cpp assignment and I am struggling with overloading the >> operator.

 I have an implementation of this method working, but it includes the creation of a temp array of a defined size, and I don't want a defined size, I want it to work for any size of string. In order to achieve this I need to know the length of the string, allocate this amount of memory on the heap, and then copy in the string. I have a loop that is reading the characters in the input one by one, a counter keeping track of the length, and during this process I would also like to be building my c-string, but I cant figure out how to make that happen. I have attached some sample code - the strcat() function does not work here, but I think it gives an illustration of what I want to do... I would like to append each char to an existing c-string. Seems like it should be straight-forwardish, but its killing me...

Thanks!
istream & operator>>( istream & in, String & string ) {
	char * temp; = "";
	char character;
	while((character = in.get()) != '\n') {
		// I want to add the character to a c-string so that I have a completed one by the
                // time the loop is done
                temp = strcat(temp, character);
		string.length++
	}	
	// this sets the null terminator and replaces \n in the last array element 
	temp[i] = 0;
	// we need to deallocate the memory that sPtr is currently pointing to
	delete[] string.sPtr;
	// and reallocate to the correct length
	string.sPtr = new char[string.length + 1];
	// now we can copy the temp string into sPtr
	strcpy(string.sPtr,temp);
	// return a reference to allow cascading
	return in;
}

Open in new window

0
Comment
Question by:AndreeaN
  • 5
  • 4
  • 4
13 Comments
 
LVL 5

Expert Comment

by:sumitkchawla
ID: 23704013
Declare a fixed size buffer initially, then use realloc to allocate memory based on new size requirements.
0
 
LVL 1

Author Comment

by:AndreeaN
ID: 23704120
The problem I am trying to nabigate is having to declare a fixed size to the initial buffer, and I'm not certain that your suggestion will alleviate this problem. The way I current have it (not shown, I can post if it would help...) is to have an array of 100 chars, read from istream into that, then allocate the memeory and copy over the char array. But I dont want to have to specify the arbitrary 100 chars (what if the string is 101 chars or 10001 and so on), I would like to know the length and assign it appropriately.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 23704146
>> I would like to append each char to an existing c-string.

Is there any reason you can't use the C++ std::string instead ?

        http://www.cplusplus.com/reference/string/

It's easier and safer to use, and allows you to append data as much as you want.
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 5

Expert Comment

by:sumitkchawla
ID: 23704181
You can start with a buff of 1 byte and  dynamically allocate memory based on your need. Does it help??
0
 
LVL 1

Author Comment

by:AndreeaN
ID: 23704201
Infinity08: Yes, no std::string unfortunately, it's a school assignment to create our own string class...

Sumitkchawla: I am not sure if what you are suggesting would help. Most likely a lock of knowledge on my part - could you give me some more detail on exactly how that would work, or maybe a few lines of sample code?
0
 
LVL 1

Author Comment

by:AndreeaN
ID: 23704355
Thanks for your prompt responses, but including that library would be akin to just using the regular std::string class, which would kind of defeat the purpose of my exercise. Also, as a student and in the spirit of learning, I really just want to understand if/how I can read from cin a character a time, and put those characters one by one into c-style string array without declaring it's size first.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 23704373
>> Infinity08: Yes, no std::string unfortunately, it's a school assignment to create our own string class...

In that case, you'll need to do something similar to what the std::string class does. Namely, start the string by allocating a (small) amount of memory for it, and as the string grows, you can increase the size of the allocated memory (for example by doubling the memory size). That of course requires that you keep track of the allocated size, as well as the "used" size.

You can make use of realloc for changing the size of an allocated (using malloc or calloc) block of memory :

        http://www.cplusplus.com/reference/clibrary/cstdlib/realloc.html
0
 
LVL 1

Author Comment

by:AndreeaN
ID: 23704494
OK, I think I see what you are getting at, but maybe I need to frame my question a little better. Forget the dynamic memory for now, as it's not really that relevant to my question. And also, perhaps I am missing the point and this just is not possible. Here are the steps I am looking for:

The user has input something at the keyboard, I want to take that input and put in into a character array, but I do not want to declare the size of the array until I know how big the input is. One theoretical way I can see of achieving this is reading the input character by character using cin.get() to find out the length of the string,  then create the array with my now known length. Then I need to magically put the characters back into the istream object and use cin.get again to loop through them, this time putting them into my char array. I am looking for a way to do this, or some buffer that I can put the characters in which I could pull them out in the same way the I can with cin and get(). Or in indeed getline(), that would be fine too, as long as I could count the number of characters coming in first... Does this make sense?
0
 
LVL 5

Expert Comment

by:sumitkchawla
ID: 23704521
Did you had a look at the DString implementation and the append method?

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 23704530
>> but I do not want to declare the size of the array until I know how big the input is.

You can't know the size until you've read it all (unless you're reading from a file, or unless you can find out the size of the sent data in some other way).

What you do, is read data until the buffer is full, then increase the size of the buffer, and read some more, etc.


>> One theoretical way I can see of achieving this is reading the input character by character using cin.get() to find out the length of the string

Rather use getline :

        http://www.cplusplus.com/reference/iostream/istream/getline.html


>> then create the array with my now known length. Then I need to magically put the characters back into the istream object and use cin.get again to loop through them

You can see that there are a few issues here, don't you ;) (the use of the word "magic" should give you a good clue heh) At the very least, the performance will suffer because you need to process the same data twice.

Dynamically growing a buffer really is the way to go here.

So :

>> as long as I could count the number of characters coming in first...

Abandon this idea. There's no need for something like that.

Just allocate a reasonably sized buffer (not too big to waste space, not too small so it can still hold the most likely input), read data from the stream until the buffer is full (or no more data has to be read), and increase the buffer size if needed.
0
 
LVL 1

Author Comment

by:AndreeaN
ID: 23717987
OK, so with the weight of expert opinion telling me to ditch my original plan and go with some sort of dynamically increasing buffer, I tried to do that. I haven't implemented anything like this before, so now I have some code that appears to work, I'll post my effort and invites comments or suggestions on how I should have done it. I've included my thought process in the notes to help make sense of it:




// overloading the >> operator
// this method will read the input 4 characters at a time and dynamically grow the required 
// memory to store it. If thought more efficient, the number of charaters read in each iteration can be increased 
// by adjusting the BUFFER_SIZE constant. It works for all values greater than 1.
istream & operator>>( istream & in, String & string ) {
	// set a buffer to the number of characters to be read in at a time
	// if changing, must be > 1...
	int const BUFFER_SIZE = 4;
	// the buffer will grow as long as we have more to read
	int dynamicBuffer = BUFFER_SIZE;
	// create buffer1 and buffer2 - eventually buffer1 will be used to store buffer1 + buffer2
	// so it is two times the BUFFER_SIZE in length
	char * buffer1 = new char[(BUFFER_SIZE*2)];
	char * buffer2 = new char[BUFFER_SIZE];
	// and create two dynamic buffers to copy between as the string grows
	char * tempString1 = new char[dynamicBuffer + 1];
	char * tempString2 = new char[dynamicBuffer + 1];
	in.get(buffer1, BUFFER_SIZE); // read in first 10 chars
 
	// if the length of buffer one is the BUFFER_SIZE - 1, then we may have more input to read
	// if not, then buffer1 already holds our string
	if((strlen(buffer1) == (BUFFER_SIZE-1))) {
		in.get(buffer2, BUFFER_SIZE); // read in 2nd 10 chars
		delete[] tempString1;
		// increase dynamic buffer to 20 chars
		dynamicBuffer += BUFFER_SIZE; 
		tempString1 = new char[dynamicBuffer]; 
		// concatenate buffer2 into buffer1
		strncat(buffer1, buffer2, BUFFER_SIZE-1); 
		// copy the contents into tempString1
		strcpy(tempString1,buffer1);
		// at this point tempString1 holds our string and this will be assigned to string.sPtr if
		// there is no more data to be read from the input stream 
		// this loop while execute until there is no more data to read
		while(in.get(buffer1, BUFFER_SIZE)) { 
			// delete tempString2 as we need to reallocate with more memory
			delete[] tempString2;
			// increase the BUFFER_SIZE and point tempString2 to it
			dynamicBuffer += BUFFER_SIZE;
			tempString2 = new char[dynamicBuffer];
			// copy exising tempString1 into the larger tempString2 and append the
			// input to it 
			strcpy(tempString2, tempString1);
			strncat(tempString2, buffer1, BUFFER_SIZE);
			// repeat the process in the other direction - i.e. delete and increase size of 
			// tempString1, copy over ts2 and append with the input
			in.get(buffer1, BUFFER_SIZE);
			delete[] tempString1;
			dynamicBuffer += BUFFER_SIZE;
			tempString1 = new char[dynamicBuffer];
			strcpy(tempString1, tempString2);
			strncat(tempString1, buffer1, BUFFER_SIZE);
			// once this loop has completed, tempString1 will point at the completed string
		}
		// free the memory currently being pointed to by string.sPtr, and other dynamic memory location
		delete[] string.sPtr;
		delete[] buffer1;
		// point string.sPtr at tempString1 which is the location of our new string
		string.sPtr = tempString1;
	} else {
		// free the memory currently being pointed to by string.sPtr, and other dynamic memory pointers
		delete[] string.sPtr;
		delete[] tempString1;
		// point string.sPtr at tempString1 which is the location of our new string
		string.sPtr = buffer1;
	}
	// delete buffer2 and tempString2
	delete[] buffer2;
	delete[] tempString2;
	return in;
}

Open in new window

0
 
LVL 53

Accepted Solution

by:
Infinity08 earned 2000 total points
ID: 23719690
You made things a lot more complicated than they need to be. Here's some simple example code :
const size_t strBaseAlloc = 64;            // the base allocated size
 
class String {
  private :
    char *data;                            // will hold a pointer to the string data
    size_t allocLen;                       // will hold the allocated size
    size_t usedLen;                        // will hold the used size
 
  public :
    String() : data(0), allocLen(0), usedLen(0) { }
 
    friend std::istream &operator>>(std::istream &is, String &str);
};
 
std::istream &operator>>(std::istream &is, String &str) {
  // 1) allocate a block of size strBaseAlloc for 'data', and set 'allocLen' accordingly
  // 2) try to fill the allocated 'data' buffer by reading from the 'is' stream (use getline). Don't forget to modify 'usedLen'
  // 3) increase the size of the allocated memory block for 'data' if needed
  // 4) repeat 2) and 3) until all data is read.
  return is;
}

Open in new window

0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

572 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question