Change string by index - c++

I am a beginner in C++ and I am currently working with strings.
My question is why when compiling the code I'm providing below, I can get the string's characters when I use index notation, but cannot get the string itself using cout?
This is the code:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string original; // original message
string altered; // message with letter-shift
original = "abc";
cout << "Original : " << original << endl; // display the original message
for(int i = 0; i<original.size(); i++)
altered[i] = original[i] + 5;
// display altered message
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
cout << "altered : " << altered << endl;
return 0;
}
When I run this, the characters in the string altered are displayed correctly with this line:
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
But the string itself is not displayed with this line:
cout << "altered : " << altered << endl;
I would like to know why this happens.

You have not resized your altered string to fit the length of the original string before the loop, thus your code exhibits undefined behavior:
altered[i] = original[i] + 5; // UB - altered is empty
To fix this, resize altered before the loop:
altered.resize(original.size());
Or use std::string::operator+= or similar to append to altered:
altered += original[i] + 5;
This way, it can be empty before the loop, it will automatically resize itself to contain appended characters.
Explanation
The way UB is happening here, is that you're succeeding in writing the data in the static array, which std::string uses for short string optimization (std::string::operator[] does no checks if you're accessing this array past the std::string::size()), but std::string::size() remains 0, as well as std::string::begin() == std::string::end().
That's why you can access the data individually (again, with UB):
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
but cout << aligned does not print anything, considering simplified operator<< definition for std::string looks functionally like this:
std::ostream &operator<<(std::ostream &os, std::string const& str)
{
for(auto it = str.begin(); it != str.end(); ++it) // this loop does not run
os << *it;
return os;
}
In one sentence, std::string is not aware of what you did to its underlying array and that you meant the string to grow in length.
To conclude, <algoritm> way of doing this transformation:
std::transform(original.begin(), original.end(),
std::back_inserter(altered), // or altered.begin() if altered was resized to original's length
[](char c)
{
return c + 5;
}
(required headers: <algorithm>, <iterator>)

In your program string altered is empty. It has no elements.
Thus you may not use the subscript operator to access non-existent elements of the string as you are doing
altered[i] = original[i] + 5;
So you can append the string with new characters. There are several ways to do this. For example
altered.push_back( original[i] + 5 );
or
altered.append( 1, original[i] + 5 );
or
altered += original[i] + 5;
As you may not apply the subscript operator for an empty string to assign a value then it is better to use the range-based for loop because the index itself in fact is not used. For example
for ( char c : original ) altered += c + 5;

The size of altered is always zero - by using indexes you are trying to copy values from original to altered at indexes altered does not have. As LogicStuff has said, this is undefined behaviour - it doesn't generate an error because when we use indexes with std::string we are in fact calling an operator on a std::string to access the data field of a string. Using [] operator is defined in the C++ Standard as having no range check - that's why no error was thrown. The safe way to access indexes is to use the at(i) method: altered.at(i) will instead throw a range error if altered.size() <= i
However, I'm going to give this as my solution because it's a "Modern C++" approach (plus shorter and complete).
This is the alternative I would do to what has been given above:
string original = "abc";
string altered = original;
for (auto& c : altered) c += 5; // ranged for-loop - for each element in original, increase its value by 5
cout << altered << endl;
Note the significant reduction in code :-)
Even if I were doing it LogicStuff's way, I would still do it like this:
string original = "abc"
string altered = ""; // this is actually what an empty string should be initialised to.
for (auto c : original) altered += (c+5);
However, I actually don't recommend this approach, because of the way push_back() and string appending / string concatenation work. It's fine in this small example, but what if original was a string holding the first 10 pages of a book to be parsed? Or what if it's a raw input of a million characters? Then every time the data field for altered reaches its limit it needs to be re-allocated via a system call and the contents of altered are copied and the prior allocation for the data field is freed. This is a significant performance impediment which grows relative to the size of original -- it's just bad practice. It would always be more efficient to do a complete copy and then iterate, making the necessary adjustments on the copied string. The same applies to std::vector.

Related

Passing values ​to an array within a loop

I'm trying to make a program in C++ in which the number of mathematical signs are counted. I am using isdigit to figure this out, but when I pass the value of my string, it gives me a warning.
This is the code that I have. The line digito[i] = entrada[i] is where I think the problem lies, but I do not understand why.
cout << "Input the operation" << endl;
cin >> input;
string digit[] = { "" };
string sign[]={""};
int cn = 0, cs = 0;
for (int i = 0; i < input.size(); i++) {
if (isdigit(input[i])) {
cout << "There is a digit in position " << i << endl;
cn += 1;
digit[i] = input[i];
}
else {
cout << "There is a sign in position " << i << endl;
// sign[i] = input[i];
cs += 1;
sign[i] = input[i];
}
}
It takes me to this code as the problem:
static _CONSTEXPR17 void assign(char& _Left, const char& _Right) noexcept
{ // assign an element
_Left = _Right;
}
Those two strings are problematic. You've unnecessarily declared them as arrays with one element each, and initialized each string to empty.
string digito[] = { "" };
string signo[]={""};
Yet afterwards, you're indexing them with non-zero indices:
digito[i] = entrada[i];
This line is problematic because of two reasons; going beyond the array bounds, and incompatible types.
digito[i] is the type of std::string (because digito is std::string[]), while entrada[i] is char (assuming entrada is std::string). std::string has an overload of its operator= that allows assigning to a single character, but that's not what you want here, I assume.
As for the second problem, std::string requires you to enlarge it before you random-access it at a given index. The best way to do this in this case would be during construction, dropping the erroneous array use:
std::cin >> entrada;
std::string digito(entrada.size(), ' ');
std::string signo(entrada.size(), ' ');
That being said, I'm not sure if this code does what you want it to. Given an input string of:
2+2/3
You'll get two such strings:
digito = "2 2 3"
signo = " + / "
If your actual goal was to tokenize the input (divide into numbers and operators), a much better way would be to use either two std::vector<char>s, or, even better:
using Number = int;
enum class Operator { Plus, Minus, Div, Mul };
using Token = std::variant<Number, Operator>
using Sequence = std::vector<Token>;
A consistent, strongly-typed data model will make it much easier to write correct code that produces it and operates on the results afterwards.

Output of an expanded array returns unexpected answer

I have finished writing a program that included reversing, expanding and shifting arrays using the pointer requirement asked by the professor. Everything compiles but the answer from the expand function does not return what I wish: adding 0s after the old user input array which asks for the size of the array and the numbers you wish to put into the array. I think my problem may lie from the fact that I include a pointer on something that might not have a reference in the program. Below is my code:
// *numPtr refers to my old user input array and int tamaño is the size of the array
void expandArray(int *numPtr, int tamaño) {
int *nuevoArray = new int[tamaño *2];
for (int i = 0; i<tamaño; i++) {
nuevoArray[i] = numPtr[i];
}
for (int i = tamaño; i < (tamaño*2); i++) {
nuevoArray[i] = 0;
}
std::cout << nuevoArray << " ";
}
As I said, my theory of the code not compiling the way I wish is because I use the *nuevoArray and it has no reference in my main code, but then again, I am just a beginner with C++. I was thinking of just doing a vector, but I think I would not follow the pointer requirements placed by the professor.
If you want to print the contents of nuevoarray, just use a for loop like this:
for (int i = 0; i < (tamaño*2); i++) {
std::cout << nuevoArray[i] << " ";
}
std::cout << "\n";
Also, since you are using new[] to create the array, you should not forget to delete[] it!
you can print your array by using
for (int i = 0 ; i < tamano * 2 ; ++i) {
std::cout << nuevoArray[i] << " ";
}
std::cout << std::endl;
or in c++11
for (auto i : nuevoArray) {
std::cout << i << " ";
}
std::cout << std::endl;
PS: The std::endl will return to the start of the new line and flush the cout buffer.
Your code does appear to be allocating a larger array and correctly copying data from numPtr into the new array and also correctly filling the remainder of the new array with zeros.
You don't explicitly say what you expect this function to output, but I'm guessing you expect it to print out the contents of the new array, and that you believe there's a problem because instead of that, you're seeing it print something like "0x7fb46be05d10".
You're not correctly printing the array out. Instead you're printing the memory address of the first element out. If you want to see the contents, then you need to loop over the elements of the array and print each one out individually.
Here's a function showing one way of doing that:
#include <algorithm>
#include <iterator>
void printArray(int *arr, int n) {
std::copy(arr, arr + n, std::ostream_iterator<int>(std::cout, " "));
}
Now you can replace the line std::cout << nuevoArray << " "; in your existing code with printArray(nuevoArray, tamaño*2);
(Also it sounds like whoever is teaching you C++ should take a look at this presentation from the recent C++ conference, CppCon 2015: Stop Teaching C)

Read into std::string using scanf

As the title said, I'm curious if there is a way to read a C++ string with scanf.
I know that I can read each char and insert it in the deserved string, but I'd want something like:
string a;
scanf("%SOMETHING", &a);
gets() also doesn't work.
Thanks in advance!
this can work
char tmp[101];
scanf("%100s", tmp);
string a = tmp;
There is no situation under which gets() is to be used! It is always wrong to use gets() and it is removed from C11 and being removed from C++14.
scanf() doens't support any C++ classes. However, you can store the result from scanf() into a std::string:
Editor's note: The following code is wrong, as explained in the comments. See the answers by Patato, tom, and Daniel Trugman for correct approaches.
std::string str(100, ' ');
if (1 == scanf("%*s", &str[0], str.size())) {
// ...
}
I'm not entirely sure about the way to specify that buffer length in scanf() and in which order the parameters go (there is a chance that the parameters &str[0] and str.size() need to be reversed and I may be missing a . in the format string). Note that the resulting std::string will contain a terminating null character and it won't have changed its size.
Of course, I would just use if (std::cin >> str) { ... } but that's a different question.
Problem explained:
You CAN populate the underlying buffer of an std::string using scanf, but(!) the managed std::string object will NOT be aware of the change.
const char *line="Daniel 1337"; // The line we're gonna parse
std::string token;
token.reserve(64); // You should always make sure the buffer is big enough
sscanf(line, "%s %*u", token.data());
std::cout << "Managed string: '" << token
<< " (size = " << token.size() << ")" << std::endl;
std::cout << "Underlying buffer: " << token.data()
<< " (size = " << strlen(token.data()) << ")" << std::endl;
Outputs:
Managed string: (size = 0)
Underlying buffer: Daniel (size = 6)
So, what happened here?
The object std::string is not aware of changes not performed through the exported, official, API.
When we write to the object through the underlying buffer, the data changes, but the string object is not aware of that.
If we were to replace the original call: token.reseve(64) with token.resize(64), a call that changes the size of the managed string, the results would've been different:
const char *line="Daniel 1337"; // The line we're gonna parse
std::string token;
token.resize(64); // You should always make sure the buffer is big enough
sscanf(line, "%s %*u", token.data());
std::cout << "Managed string: " << token
<< " (size = " << token.size() << ")" << std::endl;
std::cout << "Underlying buffer: " << token.data()
<< " (size = " << strlen(token.data()) << ")" << std::endl;
Outputs:
Managed string: Daniel (size = 64)
Underlying buffer: Daniel (size = 6)
Once again, the result is sub-optimal. The output is correct, but the size isn't.
Solution:
If you really want to make do this, follow these steps:
Call resize to make sure your buffer is big enough. Use a #define for the maximal length (see step 2 to understand why):
std::string buffer;
buffer.resize(MAX_TOKEN_LENGTH);
Use scanf while limiting the size of the scanned string using "width modifiers" and check the return value (return value is the number of tokens scanned):
#define XSTR(__x) STR(__x)
#define STR(__x) #x
...
int rv = scanf("%" XSTR(MAX_TOKEN_LENGTH) "s", &buffer[0]);
Reset the managed string size to the actual size in a safe manner:
buffer.resize(strnlen(buffer.data(), MAX_TOKEN_LENGTH));
The below snippet works
string s(100, '\0');
scanf("%s", s.c_str());
Here a version without limit of length (in case of the length of the input is unknown).
std::string read_string() {
std::string s; unsigned int uc; int c;
// ASCII code of space is 32, and all code less or equal than 32 are invisible.
// For EOF, a negative, will be large than 32 after unsigned conversion
while ((uc = (unsigned int)getchar()) <= 32u);
if (uc < 256u) s.push_back((char)uc);
while ((c = getchar()) > 32) s.push_back((char)c);
return s;
}
For performance consideration, getchar is definitely faster than scanf, and std::string::reserve could pre-allocate buffers to prevent frequent reallocation.
You can construct an std::string of an appropriate size and read into its underlying character storage:
std::string str(100, ' ');
scanf("%100s", &str[0]);
str.resize(strlen(str.c_str()));
The call to str.resize() is critical, otherwise the length of the std::string object will not be updated. Thanks to Daniel Trugman for pointing this out.
(There is no off-by-one error with the size reserved for the string versus the width passed to scanf, because since C++11 it is guaranteed that the character data of std::string is followed by a null terminator so there is room for size+1 characters.)
int n=15; // you are going to scan no more than n symbols
std::string str(n+1); //you can't scan more than string contains minus 1
scanf("%s",str.begin()); // scanf only changes content of string like it's array
str=str.c_str() //make string normal, you'll have lots of problems without this string

How to convert vector to string and convert back to vector

----------------- EDIT -----------------------
Based on juanchopanza's comment : I edit the title
Based on jrok's comment : I'm using ofstream to write, and ifstream to read.
I'm writing 2 programs, first program do the following tasks :
Has a vector of integers
convert it into array of string
write it in a file
The code of the first program :
vector<int> v = {10, 200, 3000, 40000};
int i;
stringstream sw;
string stringword;
cout << "Original vector = ";
for (i=0;i<v.size();i++)
{
cout << v.at(i) << " " ;
}
cout << endl;
for (i=0;i<v.size();i++)
{
sw << v[i];
}
stringword = sw.str();
cout << "Vector in array of string : "<< stringword << endl;
ofstream myfile;
myfile.open ("writtentext");
myfile << stringword;
myfile.close();
The output of the first program :
Original vector : 10 200 3000 40000
Vector in string : 10200300040000
Writing to File .....
second program will do the following tasks :
read the file
convert the array of string back into original vector
----------------- EDIT -----------------------
Now the writing and reading is fine, thanks to Shark and Jrok,I am using a comma as a separator. The output of first program :
Vector in string : 10,200,3000,40000,
Then I wrote the rest of 2nd program :
string stringword;
ifstream myfile;
myfile.open ("writtentext");
getline (myfile,stringword);
cout << "Read From File = " << stringword << endl;
cout << "Convert back to vector = " ;
for (int i=0;i<stringword.length();i++)
{
if (stringword.find(','))
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(','));
}
}
for (int j=0;j<v.size();i++)
{
cout << v.at(i) << " " ;
}
But it can only convert and push back the first element, the rest is erased. Here is the output :
Read From File = 10,200,3000,40000,
Convert back to vector = 10
What did I do wrong? Thanks
The easiest thing would be to insert a space character as a separator when you're writing, as that's the default separator for operator>>
sw << v[i] << ' ';
Now you can read back into an int variable directly, formatted stream input will do the conversion for you automatically. Use vector's push_back method to add values to it as you go.
Yes, this question is over a year old, and probably completely irrelevant to the original asker, but Google led me here so it might lead others here too.
When posting, please post a complete minimal working example, having to add #include and main and stuff is time better spent helping. It's also important because of your very problem.
Why your second code isn't working is all in this block
for (int i=0;i<stringword.length();i++)
{
if (stringword.find(','))
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(','));
}
}
istringstream (stringword) >> value interprets the data up to the comma as an integer, the first value, which is then stored.
stringword.find(',') gets you the 0-indexed position of the comma. A return value of 0 means that the character is the first character in the string, it does not tell you whether there is a comma in the string. In that case, the return value would be string::npos.
stringword.erase deletes that many characters from the start of the string. In this case, it deletes 10, making stringword ,200,3000,40000. This means that in the next iteration stringword.find(',') returns 0.
if (stringword.find(',')) does not behave as wished. if(0) casts the integer to a bool, where 0 is false and everything else is true. Therefore, it never enters the if-block again, as the next iterations will keep checking against this unchanged string.
And besides all that there's this:
for (int j=0;j<v.size();i++)
{
cout << v.at(i) << " " ;
}
it uses i. That was declared in a for loop, in a different scope.
The code you gave simply doesn't compile, even with the added main and includes. Heck, v isn't even defined in the second program.
It is however not enough, as the for condition stringword.length() is recalculated every loop. In this specific instance it works, because your integers get an extra digit each time, but let's say your input file is 1,2,3,4,:
The loop executes normally three times
The fourth time, stringword is 4, stringword.length() returns 2, but i is already valued 3, so i<stringword.length() is invalid, and the loop exits.
If you want to use the string's length as a condition, but edit the string during processing, store the value before editing. Even if you don't edit the string, this means less calls to length().
If you save length beforehand, in this new scenario that would be 8. However, after 4 loops string is already empty, and it executes the for loop some more times with no effect.
Instead, as we are editing the string to become empty, check for that.
All this together makes for radically different code altogether to make this work:
while (!stringword.empty())
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(',')+1);
}
for (int i = 0; i < v.size(); i++)
{
cout << v.at(i) << " " ;
}
A different way to solve this would have been to not try to find from the start, but from index i onwards, leaving a string of commas. But why stick to messy stuff if you can just do this.
And that's about it.

C++ program with map is not giving same resutls as it stored last time

I have the following program, which is storing four strings in map and printing first time. Now its running the another time to retrieve the stored values. But the second resutls are not same as first time results.
#include <map>
using namespace std;
void fun_call(void **,char * );
main(){
void *data=NULL;
char value[100];
int i=0,j=0;
char key[][10]={"disk1","disk2","disk3","disk4"};
cout << "printing all mapped values " << endl ;
data = (void *) malloc( 100);
for(j=0;j<2;j++){
for(i=0;i<4;i++){
fun_call(&data,key[i]);
memcpy(value,data,100);
cout << "key ="<<key[i]<<" value is " << value << endl;
}
cout <<"====================="<< endl;
}
}
void fun_call(void **tmp,char name[10])
{
void *tmp_data;
char str[100]="ravindra";
int len =0;
static std::map<std::string,void *> name_data_map;
std::map<std::string,void *>::iterator iter ;
iter=name_data_map.find(name) ;
if ( iter == name_data_map.end())
{
len=strlen(str)+strlen(name)+1;
tmp_data = (void *) malloc ( len );
strcat(str,name);
memcpy(tmp_data,str,len);
name_data_map[name]=tmp_data;
cout << "Inside the if" << endl ;
}
else
cout << "disk pos "<< iter->first << endl;
cout << "Outside the if" << endl ;
iter=name_data_map.find(name) ;
memcpy(*tmp,iter->second,len);
}
Output:
$ ./a.out
printing all mapped values
Inside the if
Outside the if
key =disk1 value is ravindradisk1
Inside the if
Outside the if
key =disk2 value is ravindradisk2
Inside the if
Outside the if
key =disk3 value is ravindradisk3
Inside the if
Outside the if
key =disk4 value is ravindradisk4
=====================
disk pos disk1
Outside the if
key =disk1 value is ravindradisk4
disk pos disk2
Outside the if
key =disk2 value is ravindradisk4
disk pos disk3
Outside the if
key =disk3 value is ravindradisk4
disk pos disk4
Outside the if
key =disk4 value is ravindradisk4
any idea why the second iteration is giving all data as : "ravindradisk4"
len is set to 0 in the beginning of fun_call, so if in the second run it doesn't go into your if, memcpy copies 0 bytes in the end. So the last value in main() from the first iteration remains the same regardless of key.
There are a lot of things wrong with your code, if it is intended to be a valid (or remotely idiomatic) C++ program.
As #starbugs points out, you're not using the right length the second time through to copy your result out. The one-line "fix" would be to change:
memcpy(*tmp,iter->second,len);
...to:
memcpy(*tmp,iter->second,strlen((char*)iter->second)+1);
For some basics on why brittle C string techniques are best replaced with C++ methodology, I like to show people this:
Learning Standard C++ As A New Language (PDF) by Bjarne
Once you've grasped that you might be more able to embrace the spirit in which C++ and the standard library should be used.
Your program is so trivial that it's easy to show how it can be simplified to produce idiomatic code which is far more robust and easy to read:
#include <map>
#include <iostream>
#include <string>
using namespace std;
string fun_call(string name)
{
static map<string,string> name_data_map;
map<string,string>::iterator iter;
iter = name_data_map.find(name);
if (iter == name_data_map.end()) {
string mapvalue = "ravindra";
mapvalue += name;
name_data_map[name] = mapvalue;
cout << "Inside the if" << endl ;
}
else
cout << "disk pos "<< iter->first << endl;
cout << "Outside the if" << endl;
iter = name_data_map.find(name) ;
return iter->second;
}
int main() {
string keys[] = {"disk1","disk2","disk3","disk4"};
cout << "printing all mapped values " << endl ;
for(int j = 0; j < 2; j++) {
for(int i = 0; i < 4; i++){
string value = fun_call(keys[i]);
cout << "key =" << keys[i] <<" value is " << value << endl;
}
cout << "=====================" << endl;
}
}
I'll stop there at providing a basically equivalent program with the same output and control flow.
Notes:
In standard C++, main must have an int as the return type (though it doesn't need arguments or a return statement, oddly enough)
The using namespace std; line frees you from having to type std:: in front of things in front of standard library classes like string, map, and their iterators. But don't put that in header files because it can cause problems with other source files that include them and have their own definitions which might conflict with the standard names when not disambiguated.
If you use the standard library then value types do their memory management under the hood, and the memory they use is allocated inside the class and freed in the destructor. Should you ever need to do explicit memory management then use new and delete.
First off, in general, in C++ consider using new/delete instead of malloc()/free().
I am not sure what you are exactly trying to accomplish (i.e. why you continuously copy values) but you have no length set so memcpy() doesn't copy anything.
Another simple fix to this issue is to use the pointer stored in iter->second (note that you would be able to modify data then and update that map entry - so perhaps this is not what you want).
For instance, do not allocate memory for your data variable in main and simply change this line
memcpy(*tmp, iter->second, len);
to
*tmp = iter->second;
Now the pointer address of data in main is set to the pointer address stored in the map.
First of all, I'm not even sure how your code compiles. Your main function lacks a return type and void/no-return is just bad practice. Restructure it to accomodate a simple return of 0 and make its return type int.
Furthermore, several includes are lacking before it even compiles (namely, iostream and string). Instead of using using namespace std, try to "pull" only the things you need from the std namespace. Bringing it all in is a potential hazard and bad practice in general, because you might encounter naming convention collisions in the future (and that will bring forth lots of headaches).
Back to the issue at hand. You're, if you're not experimenting/punishing your mind, applying some very bad practices here. This much memory-copying and pointer shifting around I don't get to do even while I'm working with moving vertex buffers around. And match your allocations with deallocations, that's some very bad memory management. And in C++, we use new/delete.
Since you're passing in the address of the pointer to the data variable, you can simply modify data's pointer by using *tmp.
Since your name_data_map is static, it survives the loop. Therefore, the second data member of the iter is the actual pointer to the data object at hand. Simply change the last line of code of your second function:
*tmp = iter->second;
Anyways, that's my two cents... I don't even get what you're trying to do. Good luck!