C++ string array slice - c++

How would I split an inputted string such as "one two three four five" into an array.
currentyly I have this:
const int SIZE = 5;
string digit[SIZE];
cout << "Enter the five here:";
for(int i = 0; i < SIZE; i++)
{
cout << i+1 << ")";
getline(cin, digit[i]);
}
but as it stands, the user has to hit enter every time. How do I get it so when I call digit[1] for the example input above, I get two. Hopefully that makes sense, I would imagine there is some function to do this for you, but if there is really elementary way of doing it, that would probably benefit me best, I'm still learning. thx

If you want to read words separated by whitespace, you can take advantage of the fact that extracting a string from an input stream will stop at whitespace:
for(int i = 0; i < SIZE; i++)
{
cout << i+1 << ")";
cin >> digit[i];
}

well if you want to take all the 'five' in a single line you can do that also.
and then you can use strtok() to split the string into five strings.
see: http://www.cplusplus.com/reference/clibrary/cstring/strtok/

You also may use getline function with 3 arguments. 3rd is delimiter.
getline(cin, digit[i], ' ');
Of course, it isn't best way to read input from cin. But you can use such approach for splitting full string, which you've get from user.

Related

Why are my char functions not doing their job?

So I am still new to C++, and I'm trying to make a program that has the user input a string, and then my functions return the string in reverse case, all lower case, and then all uppercase. Instead I just keep receiving the first letter of the string back, always uppercase. Not sure what I am doing wrong. Any suggestions?
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
char answer[255] = "";
int max = strlen(answer);
void reverse() {
for (int i = 0; i < max; i++) {
if (islower(answer[i])) {
isupper(answer[i]);
}
else if (isupper(answer[i])) {
islower(answer[i]);
}
else if (isspace(answer[i])) {
isspace(answer[i]);
}
}
cout << answer[max];
}
void lower() {
for (int i = 0; i < max; i++) {
if (isupper(answer[i])) {
islower(answer[i]);
}
else {
answer[i] = answer[i];
}
}
cout << answer[max];
}
void upper() {
for (int i = 0; i < max; i++) {
if (islower(answer[i])) {
isupper(answer[i]);
}
else {
answer[i] = answer[i];
}
}
cout << answer[max];
}
int main() {
cout << "Please enter a word, or a series of words: " << endl;
cin >> answer[max];
reverse();
lower();
upper();
system("pause");
return 0;
}
islower(char) is just a built in function to check if the char is in lowercase or not. Same goes with isupper. It does not change the case of the character.
In order to convert to lowercase/uppercase, use tolower/toupper. This would return the character in the converted case. But, it is important that you need to assign the returned value to the character itself.
Refer to this answer for some more clarity related to islower, isupper, tolower and toupper.
And now coming to the point why it's printing just the 1st character: As #user4581301 has mentioned in his comment,
"cin >> answer[max]; will read exactly one character because answer[max] is exactly one character, the first character. In C++ you have to do things in order. For example, int max = strlen(answer); will provide an answer based on what is in that string at that time. Since the string was initialized one line earlier and contains an empty string, max will be 0."
Hence your cin should be cin >> answer. BUT, this will accept the 1st word of your sentence. In order to accept all the words including the spaces, use getline() instead. And for using this, answer should be declared as string answer instead of a char array.
This is how you accept a full sentence: getline(cin,answer);
And your variable max will give an error in a few compilers as being ambiguous. This is because of the using namespace std;. to avoid this, rename max to something else, like maxlen.
And finding the length of answer: It would be better if you call answer.length() after accepting the string from user rather than doing it globally.
Your working code should look something like this:
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
string answer;
int maxlen;
void reverse() {
for (int i = 0; i < maxlen; i++) {
if (islower(answer[i])) {
answer[i] = toupper(answer[i]);
}
else if (isupper(answer[i])) {
answer[i] = tolower(answer[i]);
}
else if (isspace(answer[i])) {
answer[i]=' ';
}
}
cout << "Reversed string: " + answer << endl;
}
void lower() {
for (int i = 0; i < maxlen; i++) {
if (isupper(answer[i])) {
answer[i] = tolower(answer[i]);
}
else {
answer[i] = answer[i];
}
}
cout << "Lower case string: " + answer << endl;
}
void upper() {
for (int i = 0; i < maxlen; i++) {
if (islower(answer[i])) {
answer[i] = toupper(answer[i]);
}
else {
answer[i] = answer[i];
}
}
cout << "Upper case string: " + answer << endl;
}
int main() {
cout << "Please enter a word, or a series of words: " << endl;
getline(cin,answer);
cout << "Original string: " + answer << endl;
maxlen = answer.length();
reverse();
lower();
upper();
return 0;
}
With the output:
Please enter a word, or a series of words:
ReVeRsAl UPPER aNd lower
Original string: ReVeRsAl UPPER aNd lower
Reversed string: rEvErSaL upper AnD LOWER
Lower case string: reversal upper and lower
Upper case string: REVERSAL UPPER AND LOWER
cin >> answer[max];
will read exactly one character because answer[max] is exactly one character, the character in the array at position max.
max is 0 because you have to do things in order. For example,
int max = strlen(answer);
will provide the length of answer at that time this line is reached. Since the string was initialized one line earlier
char answer[255] = "";
and contains an empty string, max will be 0. This means answer[max] is answer[0] Nothing in the code ever changes max, so it will remain 0.
OK, say we change things a little and rather than reading into a single character, we read into answer as a string. You will need to
cin.getline(answer, sizeof(answer));
because
cin >> answer;
will read one whitespace-delimited token. One word. Your stated goal is to read more than one word. istream::getline will read everything it finds into the first parameter up to the end of the line or it finds the number of characters specified in the second parameter minus 1 (in order to reserve space for the string's null terminator). sizeof(answer) is literally the size of the answer array in bytes. We're operating in byte-sized characters so the count of characters and number of bytes are the same. Extra care must be taken if multibyte characters are being used.
This seems like a good place to recommend using std::string and std::getline instead. They make a large number of problems, such as the maximum number of characters that can be read, vanish for the vast majority of cases.
I'm not going to use them here, though because the assignment likely has a "No strings" policy.
So now that we have cin.getline(answer, sizeof(answer)); reading the user's input we can work on getting the size for max. We could strlen, but we could also use istream::gcount to get the number of characters read by getline.
main now looks something like
int main() {
cout << "Please enter a word, or a series of words: " << endl;
cin.getline(answer, sizeof(answer));
max = cin.gcount();
reverse();
lower();
upper();
system("pause");
return 0;
}
Whole bunch of stuff can go wrong at this point.
using namespace std; can wreak havoc on the max because of possible collisions with std::max. In general, avoid using namespace std; The few letters it saves you from typing often are recovered by the time wasted debugging the weird errors it can introduce.
isupper(answer[i]); doesn't do anything useful as others have noted in the comments. You want
answer[i] = toupper(static_cast<unsigned char>(answer[i]));
See Do I need to cast to unsigned char before calling toupper(), tolower(), et al.? for why that insane-and-pointless-looking cast may be necessary. Thank you HolyBlackCat for bringing that to my attention.
Self assignments like
answer[i] = answer[i];
are pointless for reasons that should be obvious once you stop and think about it.
Likewise
else if (isspace(answer[i])) {
isspace(answer[i]);
}
May not be particularly useful. If answer[i] is a space, set it to a space? It's already a space. What it would do is replace other forms of whitespace, tabs and carriage returns, with a space. Newline has already been picked off by getline. Also probably needs a cast similar to the one used in the toupper example above. I'm still reading up on that.
As hinted at above,
cout << answer[max];
is not effective. It prints out one character, and if max has been fixed, answer[max] will be the terminating null. Instead print out the whole array.
cout << answer;
General suggestions:
Don't write much code at a time. Write a few lines, a function at the most, before compiling and testing. If you had tested
int main() {
cout << "Please enter a word, or a series of words: " << endl;
cin >> answer[max];
cout << answer;
}
You would have immediately seen data was not being read correctly. and fixed it before proceeding. By allowing errors to build up, you make it harder to find any one bug. You may correctly fix a bug only to find the fix undone or concealed by another bug.
Avoid using global variables. Try to place variables in the smallest possible scope. In this case, move answer and max into main and pass them to the other functions as parameters. This makes it a lot easier to keep track of who set what variable and when. It also helps prevent accidental Variable Shadowing.

C++ Check String Array for Spaces/Returns

I would like to make a space/return check for a string array. And a bit more I guess...
The user enters two lines, both being strings. To accommodate for these two lines, rather than making two separate string variables, I made a string array called string x[2]. Then the input gets combined into one variable called string combined and then outputs everything on one line.
Anyways, I would like the program to allow each line to have multiple strings, i.e. line one is I like tuna and line two is blah blah. I tried to make this a multidimensional array like x[2][3] but then if they enter only one word in a line then this forces them to enter in more lines until the array is completely filled up.
I want the user to enter up to 3 words in each line and a maximum of two lines. So doesn't that mean I have to check for spaces and returns? If so, how?
I don't want to make this post codeless, so for some point of reference, here is my original code which only lets them enter one word per line and two lines:
int main()
{
string x[2], combined;
for (int i = 0; i < 2; ++i)
{
cin >> x[i];
if (i == 0) combined = x[0];
else
combined += " " + x[i];
}
cout << combined << endl;
}
/*
Program:
Hello
World
Hello World
Press any key to continue...
I apologize if this post/question is very ambiguous and seems like I haven't put much thought in, but I'm a beginner and haven't been able to figure this one out. Thanks for your time!
Use getline
for (int i = 0; i < 2; ++i)
{
getline(cin, x[i]);
}
Use getline to read entire line of input
string x[2];
cout<<"input first string"<<endl;
getline(cin, x[0]);
cout<<"input second string"<<endl;
getline(cin, x[1]);
cout<<"Printing first string "<<x[0]<<endl;
cout<<"Printing second string "<<x[1]<<endl;
there is one similar problem in C. there you can't use scanf() if user inputs the string with spaces (cause there will be an error), you have to use gets(), which is unsafe.

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.

how to split a string on comma in C++ [duplicate]

This question already has answers here:
How do I tokenize a string in C++?
(37 answers)
Closed 10 years ago.
I have this code currently in which the user would enter a number for how many numbers they want in the array. followed by '12345' however about a second after writing it i realized this would only work if they entered number 0-9 anything in double figures or more wouldnt work.
int numberOfValues;
cout << "Please enter the amount of integers you want in the array" << endl;
cin >> numberOfValues;
int valuesArray[numberOfValues];
string valuesString;
cout << "Please Enter " << numberOfValues << " numbers" << endl;
cin>>valuesString;
for(int i = 0; i < numberOfValues; i++)
{
valuesArray[i] = valuesString[i];
}
return valuesArray;
im thinking that the best way to do this would be for the user to enter numbers separated by a comma and to split them afterwards (iv done this same little porgram in java and trying to change it to C++ for my own personal learning) like in java i used string.split(",") i was wondering if there is anything similar in c++??
The simplest way I can think of would be to avoid reading to an intermediate string and let cin do the work for you:
std::vector<int> valuesArray;
int i = 0;
do {
cin >> i;
valuesArray.push_back(i);
} while (valuesArray.size() < numberOfValues && cin.get() == ',');
/* edit: You may need to consume a '\n', if you expect one, too: */
do {
i = cin.get();
} while (i != '\n');
Use strtok. Documentation and example can be found Here
use combination of string::substr() and string::find().
Find the next comma charater and then find the substring from current location to next command character
It is not standard C++ string, but still, Qt's QString class provides a ready-to-use method QString::split(...) with support for stuff like regular expressions, options for split behavior, case sensitivity and whatnot...
I wrote a tokenizer time ago, hope it works for you:
std::vector<std::string> tokenize(const std::string &_line,const char *_delimeter)
{
std::vector<std::string> Tokens;
if(_line.empty()) return Tokens;
std::string str;
BOOST_FOREACH(char c,_line){
if(c==*_delimeter)
{
Tokens.push_back(str);
str.clear();
continue;
}
str += c;
}
if(!str.empty())
Tokens.push_back(str);
return Tokens;
}
it is not efficient, but works for testing purpose.

Can I do a multidimensional char array in c++?

First off, this is a "homework" question so vector libraries and string libraries are off limits. I'm trying to get to the basics of c++.
My intention with this code is to make and use an array of string arrays. A list of words in other words.
When I run this code I get a bunch of nonsense.
If there is a better way to make a list of words in c++, I would love to hear about it.
const int cart_length = 50;
const int word_length = 50;
int main()
{
char cart_of_names[cart_length][word_length];
float cart_of_costs[cart_length];
char name[word_length];
cout << "enter the name of the first item: ";
cin >> name;
for(int i=0; i<word_length; i++)
{
cart_of_names[0][i] = name[i];
}
cout << endl;
cout << "that is: ";
for(int x=0; x<word_length; x++)
{
cout << cart_of_names[0][x];
}
cout << endl;
return 0;
}
If the string entered is not 50 characters long (cart_length), then less than 50 characters will be valid in the name. You should have an if(cart_of_names[0][x]==0) break; in your second loop.
I don't exactly understand what you are looking for. Following code will help you to read and print a list of 50 words. Hope this would help you.
const int cart_length = 50;
const int word_length = 50;
int main()
{
char cart_of_names[cart_length][word_length];
float cart_of_costs[cart_length];
for(int i=0; i<cart_length; i++)
{
cout << "enter the name of the " << i + 1 << "th item: ";
cin >> cart_of_names[i];
}
cout << "that is: ";
for(int x=0; x < cart_length; x++)
{
cout << cart_of_names[x] << endl;
}
return 0;
}
Check out STLSoft's fixed_array_2d (and it's higher order siblings). There's a detailed discussion of how they're implemented for maximum performance in Matthew Wilson's Imperfect C++.
If you can't use std::string, at least look at the functions like strncpy() from C for your name copying. Also, you're forgetting that c-style strings are null terminated.
Unless you're forbidden to use STL (which would be just mean), just use std::list<std::string>. www.cplusplus.com has detailed descriptions and examples for those classes.
Otherwise, you're stuck with an array of char arrays: in that case, be prepared for a lot of buffer overflow errors. Look around on the above site for the char[] management functions (strncpy() and the like), they'll make your life a bit easier (but not a lot).
In C, the best way I found to conceptualize what you are trying to do is using an array of char*. Same effect, but if you start to work with it I believe you may find it is easier on the brain.
It looks pretty close to me. Strings in C are null-terminated, which means that the end of the string is indicated by a null character. In a sense, a string in C is really just an array of bytes.
When you do:
cout << "enter the name of the first item: ";
cin >> name;
If I enter the string "Book", in memory it'll look like something like:
|0|1|2|3|4|5..49|
|B|o|o|k|0|*HERE BE DRAGONS*
Well, really it will contain the ASCII values corresponding to those letters, but for our purposes, it contains those letters. There here be dragons is memory that that you didn't initialize, so it contains whatever garbage your platform sets it to.
So when you copy your string, you need to instead look for that 0 byte at the end of the string.
for(int i=0; name[i]!=0; i++)
{
cart_of_names[0][i] = name[i];
}
Then when you output it, you don't actually need to do it a character at a time. You can just do cout<<cart_of_names[0]. cout knows where the string ends because of that terminating null character.
If you use strcpy() instead of
cart_of_names[0][i] = name[i];
it may work better but I cringe just looking at all that code.
"If there is a better way to make a list of words in c++, I would love to hear about it."
Include #include <string> and use std::string. The std::string type is part of the C++ specification, I think.
#include <iostream>
#include <string>
int main(void) {
std::string list[7];
list[0] = "In C++";
list[1] = "you can use";
list[2] = "the `std::string` type.";
list[3] = "It removes";
list[4] = "many of the problems";
list[5] = "introduced by";
list[6] = "C-style strings.";
for (int k=0; k<7; k++) std::cout << list[k] << ' ';
std::cout << '\n';
return 0;
}