C++ Inserting String into another String at specific position - c++

I have a custom String class that I've built. I'm trying to build a custom insert function that inserts a string into a specified position.
For Example:
String str("test");
str.insert(2, "animal");
would return:
"tesanimalt"
What i have so far:
String& String::insert(int pos, const String& str) {
char newString[100];
strncpy(newString, chars, pos);
newString[pos] = '\0';
strcat(newString, str.chars);
strcat(newString, chars + pos);
return *this;
}

There are a lot of problems with the code, one of which is that your String doesn’t seem to contain any actual storage. You instead allocate a local array as a temporary variable, then let it be discarded.
It’s not possible to fix this without a MCVE, as we cannot see how String is supposed to work. There seems to be some kind of member called chars, and you probably want to copy to that. However, if it contains a buffer allocated with malloc() or new[], you might, if m is the length of the enclosing string, and n the length of the intercalated string, and you insert at position i:
Reallocate the buffer of the destination string to its new size (The sum of the sizes of the two strings without their terminating nulls, plus one byte for a terminating null). Alternatively, allocate a new buffer and copy the first i elements of the enclosing string.
Shift elements i through n of the enclosing string n elements to the right of the resized buffer.
Copy the string to insert to positions i through i+n-1.
Write a terminating null to position m+n.
Since you appear to want to modify the enclosing string, if you created a new buffer, deallocate the old one. If you reallocated the buffer, set chars to the possibly-changed value.

Related

How to code a strcat function that works with two dynamic arrays

As we know, the strcat function concatinates one c-string onto another to make one big c-string containing two others.
My question is how to make a strcat function that works with two dynamically allocated arrays.
The desired strcat function should be able to work for any sized myStr1 and myStr2
//dynamic c-string array 1
char* myStr1 = new char [26];
strcpy(myStr1, "The dog on the farm goes ");
//dynamic c-string array 2
char* myStr2 = new char [6];
strcpy(myStr2, "bark.");
//desired function
strcat(myStr1,myStr2);
cout<<myStr1; //would output 'The dog on the farm goes bark.'
This is as far as I was able to get on my own:
//*& indicates that the dynamic c-string str1 is passed by reference
void strcat(char*& str1, char* str2)
{
int size1 = strlen(str1);
int size2 = strlen(str2);
//unknown code
//str1 = new char [size1+size2]; //Would wipe out str1's original contents
}
Thanks!
You need first to understand better how pointers work. Your code for example:
char* myStr1 = new char [25];
myStr1 = "The dog on the farm goes ";
first allocates 25 characters, then ignores the pointer to that allocated area (the technical term is "leaks it") and sets myStr1 to point to a string literal.
That code should have used strcpy instead to copy from the string literal into the allocated area. Except that the string is 25 characters so you will need to allocate space for at least 26 as one is needed for the ASCII NUL terminator (0x00).
Correct code for that part should have been:
char* myStr1 = new char [26]; // One more than the actual string length
strcpy(myStr1, "The dog on the farm goes ");
To do the concatenation of C strings the algorithm could be:
measure the lengths n1 and n2 of the two strings (with strlen)
allocate n1+n2+1 charaters for the destination buffer (+1 is needed for the C string terminator)
strcpy the first string at the start of the buffer
strcat the second string to the buffer (*)
delete[] the memory for the original string buffers if they are not needed (if this is the right thing to do or not depends on who is the "owner" of the strings... this part is tricky as the C string interface doesn't specify that).
(*) This is not the most efficient way. strcat will go through all the characters of the string to find where it ends, but you already know that the first string length is n1 and the concatenation could be done instead with strcpy too by choosing the correct start as buffer+n1. Even better instead of strcpy you could use memcpy everywhere if you know the count as strcpy will have to check each character for being the NUL terminator. Before getting into this kind of optimization however you should understand clearly how things work... only once the string concatenation code is correct and for you totally obvious you are authorized to even start thinking about optimization.
PS: Once you get all this correct and working and efficient you will appreciate how much of a simplification is to use std::string objects instead, where all this convoluted code becomes just s1+s2.
You allocate memory and make your pointers point to that memory. Then you overwrite the pointers, making them point somewhere else. The assignment of e.g. myStr1 causes the variable to point to the string literal instead of the memory you allocated. You need to copy the strings into the memory you have allocated.
Of course, that copying will lead to another problem, as you seem to forget that C-strings need an extra character for the terminator. So a C-string with 5 characters needs space for six characters.
As for your concatenation function, you need to do copying here too. Allocate enough space for both strings plus a single terminator character. Then copy the first string into the beginning of the new memory, and copy the second string into the end.
Also you need a temporary pointer variable for the memory you allocate, as you otherwise "would wipe out str1's original contents" (not strictly true, you just make str1 point somewhere else, losing the original pointer).

Not sure why I am getting different lengths when using a string or a char

When I call gethostname using a char my length 25 but when I use a string my length is 64. Not really sure why. Both of them I am declaring the same size on HOST_NAME_MAX.
char hostname[HOST_NAME_MAX];
BOOL host = gethostname(hostname, sizeof hostname);
expectedComputerName = hostname;
int size2 = expectedComputerName.length();
std::string test(HOST_NAME_MAX, 0);
host = gethostname(&test[0], test.length());
int testSize = test.length();
An std::string object can contain NULs (i.e. '\0' characters). You are storing the name in the first bytes of a string object that was created with a size of HOST_NAME_MAX length.
Storing something in the beginning of the string data won't change the length of the string that remains therefore HOST_NAME_MAX.
When creating a string from a char pointer instead the std::string object created will contain up to, but excluding, the first NUL character (0x00). The reason is that a C string cannot contain NULs because the first NUL is used to mark the end of the string.
Consider what you're doing in each case. In the former code snippet, you're declaring a character array capable of holding HOST_NAME_MAX-1 characters (1 for the null terminator). You then load some string data into that buffer via the call to gethostname and then print out the length of buffer by assigning it to a std::string object using std::string::operator= that takes a const char *. One of the effects of this is that it will change an internal size variable of std::string to be strlen of the buffer, which is not necessarily the same as HOST_NAME_MAX. A call to std::string::length simply returns that variable.
In the latter case, you're using the std::string constructor that takes a size and initial character to construct test. This constructor sets the internal size variable to whatever size you passed in, which is HOST_NAME_MAX. The fact that you then copy in some data to std::strings internal buffer has no bearing on its size variable. As with the other case, a call to the length() member function simply returns the size - which is HOST_NAME_MAX - regardless of whether or not the actual length of the underlying buffer is smaller than HOST_NAME_MAX.
As #MattMcNabb mentioned in the comments, you could fix this by:
test.resize( strlen(test.c_str()) );
Why might you want to do this? Consistency with the char buffer approach might be a reason, but another reason may be performance oriented. In the latter case you're not only outright setting the length of the string to HOST_NAME_MAX, but also its capacity (omitting the SSO for brevity), which you can find starting on line 242 of libstdc++'s std::string implementation. What this means in terms of performance is that even though only, say, 25 characters are actually in your test string, the next time you append to that string (via +=,std::string::append,etc), it's more than likely to have to reallocate and grow the string, as shown here, because the internal size and internal capacity are equal. Following #MattMcNabb's suggestion, however, the string's internal size is reduced down to the length of the actual payload, while keeping the capacity the same as before, and you avoid the almost immediate re-growth and re-copy of the string, as shown here.

Does the std::string::resize() method manages the terminating character?

I am copying some data from a stream into a string, so I thought about resizing the string with the actual number of characters plus one for the terminating one, like this:
std::istringstream stream { "data" };
const std::size_t count = 4;
std::string copy;
copy.resize(count + 1);
stream.read(&copy[0], count);
copy[count] = 0;
However, in this case, copy indicates it has a size of 5 (which is consistent since I called resize(5)). Does that mean that resize() will add the extra terminating character itself? Which would mean that I do not have to worry about appending \0 after invoking read(&data[0], count)?
No you don't have to. The string class abstracts the concept of "null terminated char sequence", so that you don't have to worry about that anymore.
Also, the size of the string returned doesn't count the terminating character, which is consistent with the behavior I mentioned, because if you don't have to deal with the terminating character, you don't have to know about it. Your string is just the characters you want to manipulate, without any concern for that "utility" character that has nothing to do with your actual data.
The quote §21.4.7.1 basic_string accessors [string.accessors] from the standard indicates that std::string has a guaranteed null terminated buffer.
Also according to the standard §21.4.4/6-8 basic_string capacity [string.capacity]:
void resize(size_type n, charT c);
6 Requires: n <= max_size()
7 Throws: length_error if n > max_size().
8 Effects: Alters the length of the string designated by *this as follows:
- If n <= size(), the function replaces the string designated by *this with a string of length n whose elements are a copy of the initial elements of the original string designated by *this.
- If n > size(), the function replaces the string designated by *this with a string of length n whose first size() elements are a copy of the original string designated by *this, and whose remaining elements are all initialized to c.
void resize(size_type n);
9 Effects: resize(n,charT()).
Interpreting the above std::string::resize will not affect the terminating null character of the string's buffer.
Now to your code:
statement std::string copy; defines an empty string (i.e., copy.size() == 0).
statement copy.resize(count + 1); since (n == 5) > 0 will replace copy with a string of length 5 filled with \0 (i.e., null characters).
Now in statement stream.read(&copy[0], count); std::stream::read will simply copy a block of data, without checking its contents nor appending a null character at the end.
In other words it will just replace the first 4 null characters of copy with "data". The size of copy won't change it will be still a 5 sized string. That is, the contents of copy's buffer will be "data\0\0".
So calling copy[count] = 0; is redundant since copy[4] is already \0. However, your string is not "data" but rather "data\0".

Parsing a string to a pointer array of chars: char[0] contains the full string and [1] onward contains nothing

I'm trying to parse a simple string to an array of *char and for some reason when I use string.c_str() it puts the entire string into *char[0] and the rest of the array is left blank (I originally thought that chars could only hold one ASCII character but I guess that they act differently as pointers), could anyone have a scan through my function and tell me if there are any obvious mistakes?
static void SetGame()
{
// Variable Initiation
int myRandom = rand() % (numOfWords - 1);
lengthOfString = wordArray[myRandom].length();
// Reinitiate Pointer Arrays
stringArray = new string[lengthOfString];
isDiscoveredArray = new bool[lengthOfString];
// Parse string to the array of characters
*stringArray = wordArray[myRandom].c_str();
// Set each boolean array value to false
for (int i = 0; i < sizeof(isDiscoveredArray); i++)
{
isDiscoveredArray[i] = false;
}
}
Here are my decelerations of the pointers
// Global Variable and pointer Declerations
string *wordArray;
int numOfWords;
string *stringArray;
int lengthOfString;
bool *isDiscoveredArray;
Any ideas? Thanks.
You are mixing types here. First you build an array of strings and store it in a pointer, then you assign to the first element a const char* coming from c_str. The code you currently have would be if your were creating a string for every character in your selected word.
Make your "stringArray" a const char* to fit with the code you already have, but remove the memory allocation.
You've got an array of std::string and when you deference it (i.e. *stringArray) its the same as stringArray[0], so that is why it always going into the first element of your array.
Since you are setting your array have the same number of elements as the string your are copying has characters, you may just want to use a string rather than a string array to copy it into.
If it supposed to be char* (character array) then you will need to explicitly copy the source, which is the result of wordArray[myRandom].c_str(), into your character array rather than using simple assignment.

C++ On Demand Creation

I'm reading a book where the following code appears.
TTextInBuffer::TTextInBuffer(const char *pInputFileName, TAbortCode ac)
: pFileName(new char[strlen(pInputFileName) + 1])
pFileName is declared as a const char, so I'm assuming that the second line creates a new char in pFileName. I would just like to know the specifics of what is happening. Thanks.
When this constructor is called, the initializer list here is executed:
: pFileName(new char[strlen(pInputFileName) + 1])
The strlen() call finds the length of the pInputFileName string based on its contents. It basically walks it as a char array until it finds a NULL and then returns the result. This is being done in order to compute the space needed for the new string within pFileName.
The + 1 is there to make sure there's room for the extra NULL termination character at the end.
Finally, whatever number pops out of that expression is fed into a memory allocation call using the keyword new. This gets memory dynamically allocated on the heap where the string data will end up. The new call returns the address of where that memory has been allocated, and that is passed to the pFileName pointer variable so that it will point to it.
So, to summarize:
The length of pInputFileName is computed
The computed length is increased by 1 to cater for NULL in the copy
new is called to request space for the copy
The address returned by new is assigned to pFileName
The one thing that's missing from your code is the actual copy over of the contents of the input string to the destination, but perhaps that happens within the constructor body (between the { and } characters).
Second line allocates memory area (array of chars) by calling operator new[].
Argument of new is size of array to allocate. So, in this snippet the length is set to length of string pInputFileName + 1. This + 1 serves to fit null character that is used in C and C++ to determine where strings ends.