I am practising user input handling. My goal is to have the user enter a line of integers separated by space (" "), read them as integers, store them and work on them later. I stumbled upon an interesting problem (Atleast in my oppinion) the way I am doing it, it seems that it is always not reading the last digit which was entered by the user. I will post the entire program here (since there are some extra libreries that are included).
I have left some comments in the program
#include <iostream>
#include <string>
#include <vector>
#include <stdlib.h>
using namespace std;
int main()
{
//this vector will store the integers
vector<int> a;
// this will store the user input
string inp;
getline(cin, inp);
// this string will temporarily store the digits
string tmp;
//be sure that the reading part is okay
cout << inp << endl;
//until you meet something different than a digit, read char by char and add to string
for(int i = 0; i < inp.length(); i++)
{
if(isdigit(inp[i]))
{
tmp +=inp[i];
}
else
{
// when it is not a character, turn to integer, empty string
int value = atoi(tmp.c_str());
a.push_back(value);
tmp = "";
}
}
// paste the entire vector of integers
for(int i = 0; i < a.size(); i++)
{
cout << a[i] << endl;
}
return 0;
}
Replace this line
for(int i = 0; i <inp.length(); i++)
by
for(int i = 0; i <= inp.length(); i++)
DEMO IDEONE
The problem with your code is: In example 25 30 46 whenever i=7, tmp=46. You are not pushing 46 in vector as inp[8] is a newline character, so your for loop terminates after i become 7.
Please Note: i <= inp.length() runs perfectly in most of the compilers as \0 is used/treated as sentinel.However, there are few compilers(like Microsoft Visual C++) that may show Assertion error: string subscript out of range.
If the very end of the line is a digit, you don't hit the else on the last iteration, and that last number never gets pushed into the vector.
The simplest solution would be to replicate the non-digit logic after the loop:
if (!tmp.empty()) // If tmp has content, we need to put it in the vector.
{
int value = atoi(tmp.c_str());
a.push_back(value);
tmp = "";
}
Although I'm sure you can think of a nicer way of structuring it.
Here's a version I came up with using std::stringstream, that also avoids atoi:
int main()
{
std::vector<int> ints;
std::string line;
std::getline (std::cin, line);
std::cout << "Read \"" << line << "\"\n";
std::stringstream ss(line);
int remaining = line.size();
while (remaining)
{
if(std::isdigit(ss.peek())) // Read straight into an int
{
int tmp;
ss >> tmp;
ints.push_back(tmp);
}
else
{
ss.get(); // Eat useless characters
}
remaining = line.size()-ss.tellg();
}
for (auto i : ints)
std::cout << i << '\n';
return 0;
}
Running:
$ ./a.out <<< "12 34 56"
Read "12 34 56"
12
34
56
Note, this is specifically made to work with any old gibberish between the numbers:
$ ./a.out <<< "12-abc34-56"
Read "12-abc34-56"
12
34
56
If there will only be whitespace, this is even easier, as reading ints from a stringstream will ignore that automatically. In which case you just need:
int tmp;
while (ss >> tmp)
{
ints.push_back(tmp);
}
Your program need a string which is ended with a non-digit character to work correctly. Try this string "1 12 14587 15 " because in your algorithm when your forgot the last space, your program store the number into the tmp string but don't save it into the vector. To correct that you need to add a last push_back just after your first loop.
You update a with new value only when when non digit is found. Thus if you have string ending with digits, tmp will contain digital string but you will never get to else that should perform push_back. You may fix this by adding following code after for loop
if(!tmp.empty()){
// when it is not a character, turn to integer, empty string
int value = atoi(tmp.c_str());
a.push_back(value);
tmp = "";
}
Before starting the loop, add a space to the string to be sure to push the last number: inp.push_back(' ')
Your loop is finished after last digit is read, so the last digit is never turned to integer. Just add some code after original for loop.
for(int i = 0; i < inp.length(); i++)
{
/* ...... */
}
// add this to read the last digit
if(tmp.length() > 0){
int value = atoi(tmp.c_str());
a.push_back(value);
tmp = "";
}
You never push back your last value. For instance, consider this input
40 36
Then while you are reading, you push back at the first space. But you never push 36 since there are no more characters.
After the end of your for() loop you can try this:
if(!tmp.empty()) {
a.push_back(tmp);
}
When the last digit of the last number is stored in tmp, after that the loop ends because you have read the last character of the entire string. When the loop ends tmp still contains the last number.
1) You can convert and add the last number to vector after the loop. The last number still available in tmp.
2) Or you can explicitly add non-digit character in the end of the string before the loop.
you ommit input. change your code to reflrct this:
//this vector will store the integers
vector<int> a;
// this will store the user input
string inp;
getline(cin, inp);
// this string will temporarily store the digits
string tmp;
//be sure that the reading part is okay
cout << inp << endl;
//until you meet something different than a digit, read char by char and add to string
for(int i = 0; i < inp.length(); i++)
{
if(isdigit(inp[i]))
{
tmp =inp[i];
int value = atoi(tmp.c_str());
a.push_back(value);
}
else
{
tmp = "";
}
}
// paste the entire vector of integers
for(int i = 0; i < a.size(); i++)
{
cout << a[i] << endl;
}
return 0;
or replace in loop:
for(int i = 0; i <inp.length(); i++)
by
for(int i = 0; i <= inp.length(); i++)
Related
I am aware of why segmentation faults occur, but I am not able to find out the error with the following code I made to split a string on the basis of whitespaces.
#include<iostream>
#include<string>
#include<vector>
#include<typeinfo>
using namespace std;
vector<string> split(const string& s)
{
//cout << "HERE";
vector<string> tab;
for(unsigned int a = 0; a < s.size(); a++)
{
string temp = to_string(s[a]);
while(to_string(s[a]) != " ")
{
a++;
temp = temp + s[a];
}
tab.push_back(temp);
}
return tab;
}
int main()
{
int n;
cin >> n;
while(n--)
{
string s;
cin >> s;
vector<string> temp = split(s);
for(unsigned int i = 0; i < temp.size(); i++)
{
cout << temp[i] << endl;
}
}
return 0;
}
Also, if I comment out the while loop in the split function, I get numbers when I print out the resulting strings. Is it because of to_string? If I use typeid(variable).name() on the resulting strings that I get when I am printing them in the main function, I get this: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE.
To answer your final question:
C++ often, but not always, treats a char value as numeric, and certainly does if you pass it to to_string. So to_string(' ') will return "32" (generally), which is the character code for a space converted to a string in decimal.
To convert a character to the corresponding single-element string use e.g. string(1, ' ').
For your segmentation fault the debugger is the right tool.
Your split function is buggy. Your program will always crush because while(to_string(s[a]) != " ") condition on the while loop will cause infinite loop.
It seems strange to me you are using to_string(s[a]). Lets say s[a] is really space char even in this case toString(" ") will return a std::string which contains "32". And "32" is not equal " " so this will cause your loop run infinitely.
And since in the loop you are increasing the index like below.
a++; ---> You increase the index in infite loop so a can go to millions
temp = temp + s[a]; ---> you are using the index and causing index out of range.
You will cause index out of range error.
I'm writing a program to help solve crossword puzzles. So I'm getting a word from a text list of all words in the english language, making each one a vector of chars, and comparing that vector to a vector of whatever starting letters I have. It runs fine and gives me good output, but every time I'm getting an error "libc++abi.dylib: terminating with uncaught exception of type std::length_error: vector".
Here's my code:
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
using namespace std;
string getLetters() {
string word; // Get user letter, put in variable word
cout << "Enter a set of letters" << endl;
cin >> word;
return word;
}
int getLengthOfWord() {
int length; // Get length of word
cout << "Enter the number of letters in the word" << endl;
cin >> length;
return length;
}
// Change strings to vectors of chars
vector<char> stringToVector(string word) {
std::vector<char> v(word.begin(), word.end());
return v;
}
bool compareVectors(vector<char> userWord, vector<char> listWord, int length) {
if (listWord.size() != length) // Make sure the word from the list is the right length
{
return false;
}
int counter = 0; // Counter
for (int i = 0; i < userWord.size(); i++) { // Iterating through the two words
for (int j = 0; j < listWord.size(); j++) {
if (listWord[j] == userWord[i]) { // If the letters match
listWord.erase(listWord.begin() - 1 + j); // Erase the letter from the word
counter++; // Increase counter
break; // Break out of for loop
}
}
}
if (counter == userWord.size()) { // If there were as many matches as letters in user set
return true;
}
else {
return false;
}
}
int main() {
string example; // variable to put words
ifstream wordList; // New ifstream object
wordList.open("/Users/alexray/Dropbox/C++ Practice/WordJumbleSolver/wordsEn.txt"); //open word list
int length = getLengthOfWord(); // Get user input
string word = getLetters();
vector<char> vector1(stringToVector(word));
while (wordList.is_open()) {
getline(wordList, example); // Get word, put it in example variable
vector<char> vector2(stringToVector(example)); // Make word from list a vector
vector2.erase(vector2.end() - 1); // Erase escape character from end of word
if(compareVectors(vector1, vector2, length)) { // compare the vectors
cout << example << endl;
}
}
wordList.close(); // Close stream
return 0;
}
From googling around, I thought that it was a matter of my vector wasn't initially large enough to handle some of the words, but doing vector.reserve(some_number) before assigning a value to the vector didn't help anything. Also, I couldn't imagine that a vector would have any problems with <20 elements.
Thanks for the help! (I'm new to C++ so if there's something I should obviously be doing differently, let me know).
Edit: The file I'm working with is the wordsEn.txt file from this website: http://www-01.sil.org/linguistics/wordlists/english/
In my case it was a mismatch between C++ standard on two vcxproj projects.
I've simply aligned both projects to the same C++ standard (17) and it worked.
project ➤ Properties ➤ C/C++ ➤ Language ➤ C++ Language Standard
One issue I see is that you are not erasing the character you claim you want to erase:
listWord.erase(listWord.begin() - 1 + j);
This does not erase the jth character in the sequence.
The easiest example of this failing is if j == 0 at the start of the loop, and the first character matches.
Just simply do this instead:
listWord.erase(listWord.begin() + j);
I was looking in the wrong place the whole time. I looked at the number of words/lines in the file (109582) and changed the
while (wordList.is_open()) {
getline(wordList, example); // Get word, put it in example variable
vector<char> vector2(stringToVector(example)); // Make word from list a vector
vector2.erase(vector2.end() - 1); // Erase escape character from end of word
if(compareVectors(vector1, vector2, length)) { // compare the vectors
cout << example << endl;
}
counter++;
}
to
while (counter < 109582) {
getline(wordList, example); // Get word, put it in example variable
vector<char> vector2(stringToVector(example)); // Make word from list a vector
vector2.erase(vector2.end() - 1); // Erase escape character from end of word
if(compareVectors(vector1, vector2, length)) { // compare the vectors
cout << example << endl;
}
counter++;
}
It seems I was getting some sort of overflow error by trying to read in more lines than were available in the file.
Input: 3 Books
Substrings should be: Boo, ook, oks
I want to add them in the substring array but only "Boo" is added..
Also how can I cout the most repeated string in that array?
#include <iostream>
#include <string>
using namespace std;
int main ()
{
int n;
int sub_index=0;
string input;
string substrings [1000];
while ( cin >> n >> input != NULL )
{
for(int i=0; i<= input.size()-n ; i++)
{
string sub = input.substr(i,n);
substrings [sub_index]= sub;
sub_index += n+1;
}
//cout the most repeated in substrings array
}
getchar();
return 0;
}
If you add a debugging print in your loop, you can see the problem immediately. This change:
string sub = input.substr(i,n);
std::cout << "storing \"" << sub << "\" at index "
<< sub_index << std::endl;
substrings [sub_index]= sub;
sub_index += n+1;
generates this output:
storing "Boo" at index 0
storing "ook" at index 4
storing "oks" at index 8
I'm not sure why you're incrementing your index the way you are, but that's likely the problem you're hitting.
To determine the most repeated string, you'll need to store both the substring and a count. I suggest you look into using a std::map with the string as a key and the count as a value, and once you have all your input, iterate through the map to find the key/value pair with the highest count.
I am trying to iterate through a char array using a while loop using '\0' as the terminating condition, but my problem is that its not finding the '\0' until index position 481, the array is declared as 200 long and I cant see what I am doing wrong!! I cannot use strings or any form of string functions for this before anyone asks. Can anyone help??
#include <iostream>
using namespace std;
int main()
{
char fullString[200]={'\0'}; // Declare char string of 200, containing null characters
int alphaCount = 0;
int charCount = 0;
int wordCount = 0;
cin.getline(fullString,200); //
cout << "\n\n" << fullString;
cout << "\n\n\n";
int i=0;
int i2 = 0;
while(fullString[i]!='\0'){ //iterate through array until NULL character is found
cout << "\n\nIndex pos : " << fullString[i]; //Output char at 'i' position
while(fullString[i2]!= ' '){ //while 'i' is not equal to SPACE, iterate4 through array
if(isalpha(fullString[i2])){
alphaCount++; // count if alpha character at 'i'
}
charCount++; // count all chars at 'i'
i2++;
}
if(charCount == alphaCount){ // if charCount and alphaCount are equal, word is valid
wordCount++;
}
charCount = 0; // zero charCount and alphaCount
alphaCount = 0;
i=i2;// Assign the position of 'i2' to 'i'
while(fullString[i] == 32){ //if spaces are present, iterate past them
i++;
cout << "\n\ntest1";
}
i2 = i; // assign value of 'i' to 'i2' which is the next position of a character in the array
if(fullString[i] == '\0')
{
cout << "\n\nNull Character " << endl;
cout << "found at pos: " << i << endl;
}
}
cout << "\n\ni" << i;
cout << "\n\nWord" << wordCount;
return 0;
}
As others have pointed out, your problem is with the inner loop. You test for a space character but not for NULL, so it's iterating past the end of the last word because there is no space character after the last word.
This is easily fixed by changing your while condition from this:
while(fullString[i2]!= ' ')
... to this:
while(fullString[i2] && fullString[i2]!= ' ')
This will change your inner while loop to first test for non-NULL, and then test for non-space.
I'm not correcting the rest of your code because I presume this is a class project (it looks like one) so I'm limiting my answer to the scope of your question.
You do not check in the inner loop
while(fullString[i2]!= ' '){ //while 'i' is not equal to SPACE, iterate4 through array
if(isalpha(fullString[i2])){
alphaCount++; // count if alpha character at 'i'
}
charCount++; // count all chars at 'i'
i2++;
}
...
i=i2;// Assign the position of 'i2' to 'i'
whether the next character is equal to '\0'
It's because the inner loops don't check for the termination, they just continue looping even past the end of the string.
By the way, if you want to count the number of words, spaces and non-space characters, there are easier ways in C++. See e.g. std::count and std::count_if for the spaces and characters. For example:
std::string input = "Some string\twith multiple\nspaces in it.";
int num_spaces = std::count_if(std::begin(input), std::end(input),
[](const char& ch){ return std::isspace(ch); });
For counting words, you can use std::istringstream, std::vector, std::copy, std::istream_iterator and std::back_inserter:
std::istringstream iss(input);
std::vector<std::string> words;
std::copy(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter(words));
After the code above, the size of the words vector is the number of words.
If you use e.g. std::copy_if then you can use the above code for the other cases as well (but std::count_if is better for single character classes).
So I'm trying to make a brute force string generator to match and compare strings in CUDA. Before I start trying to mess around with a language I don't know I wanted to get one working in C++. I currently have this code.
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
int sLength = 0;
int count = 0;
int charReset = 0;
int stop = 0;
int maxValue = 0;
string inString = "";
static const char charSet[] = //define character set to draw from
"0123456789"
"!##$%^&*"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int stringLength = sizeof(charSet) - 1;
char genChars()
{
return charSet[count]; //Get character and send to genChars()
}
int main()
{
cout << "Length of string to match?" << endl;
cin >> sLength;
cout << "What string do you want to match?" << endl;
cin >> inString;
string sMatch(sLength, ' ');
while(true)
{
for (int y = 0; y < sLength; y++)
{
sMatch[y] = genChars(); //get the characters
cout << sMatch[y];
if (count == 74)
{
charReset + 1;
count = 0;
}
if (count == 2147000000)
{
count == 0;
maxValue++;
}
}
count++;
if (sMatch == inString) //check for string match
{
cout << endl;
cout << "It took " << count + (charReset * 74) + (maxValue*2147000000) << " randomly generated characters to match the strings." << endl;
cin >> stop;
}
cout << endl;
}
}
Now this code runs and compiles but it doesn't exactly do what I want it to. It will do 4 of the same character, EX. aaaa or 1111 and then go onto the next without incrementing like aaab or 1112. I've tried messing around with things like this
for (int x = 0; x < sLength; x++)
{
return charSet[count-sLength+x];
}
Which in my mind should work but to no avail.
You basically just need to increment a counter, than convert the count number to base (size of char array)
Here's an example which does normal numbers up to base 16.
http://www.daniweb.com/code/snippet217243.html
You should be able to replace
char NUMS[] = "0123456789ABCDEF";
with your set of characters and figure it out from there. This might not generate a large enough string using a uint, but you should be able to break it up into chunks from there.
Imagine your character array was "BAR", so you would want to convert to a base 3 number using your own symbols instead of 0 1 and 2.
What this does is perform a modulus to determine the character, then divide by the base until the number becomes zero. What you would do instead is repeat 'B' until your string length was reached instead of stopping when you hit zero.
Eg: A four character string generated from the number 13:
14%3 = 2, so it would push charSet[2] to the beginning of the empty string, "R";
Then it would divide by 3, which using integer math would = 4. 4%3 is again 1, so "A".
It would divide by 3 again, (1) 1%3 is 1, so "A".
It would divide by 3 again, (0) -- The example would stop here, but since we're generating a string we continue pushing 0 "B" until we reach 4 our 4 characters.
Final output: BAAR
For an approach which could generate much larger strings, you could use an array of ints the size of your string, (call it positions), initialize all the ints to zero and do something like this on each iteration:
i = 0;
positions[i]++;
while (positions[i] == base)
{
positions[i] = 0;
positions[++i]++;
}
Then you would go through the whole array, and build the string up using charSet[positions[i]] to determine what each character is.