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).
Related
I'm trying to invert the case of some strings, and I did it, but I have some extra characters in my return, is it a memory problem? Or because of the length?
char* invertirCase(char* str){
int size = 0;
char* iterator = str;
while (*iterator != '\0') {
size++;
iterator++;
}
char* retorno = new char[size];
for (int i = 0; i < size; i++) {
//if capital letter:
if (str[i] < 96 && str[i] > 64) {
retorno[i] = str[i] + 32;
}
// if lower case:
else if (str[i] > 96 && str[i] < 123) {
retorno[i] = str[i] - 32;
}
//if its not a letter
else {
retorno[i] = str[i];
}
}
return retorno;
}
For example, if I try to use this function with the value "Write in C" it should return "wRITE IN c", but instead it returns "wRITE IN cýýýýÝݱ7ŽÓÝ" and I don't understand where those extra characters are coming from.
PS: I know I could use a length function, but this is from school, so I can't do that in this case.
add +1 to the size of the char array.
char* retorno = new char[size+1];
add a null-terminated string before returning retorno.
retorno[size] = '\0';
Your output string is not null-terminated
When you iterate through the input string, you increment size until you reach null. That means the null is not copied to the output string. After you exit the loop, you should increment size once more to capture the end.
As an aside, it's probably a good idea to constrain size to some maximum (while(*iterator != '\0' && size < MAXSIZE)) in case someone passes a non-terminated string into your function. If you hit the max size condition, you'd need to explicitly add the null at the end of your output.
Your string should be null terminated; which is what you are looking for when you get the initial size of the string. When you create the new string, you should allocated size+1 chars of space, then retorno[size] should be set to a null terminating character (i.e. '\0'). When you attempt to print a char* using printf or cout (or similar mechanisms), it will keep printing characters until it find the null terminating character, which is why you are getting the garbage values after your expected output.
On another note, c++ has helpful functions like std::islower / std::isupper and std::tolower / std::toupper
From what I can tell, there could be 2 things going on here:
Like everyone here mentioned, the absence of a null terminating character ('\0') at the end of your char array could be causing this.
It could be the way you are printing results of your retorno character string outside of your invertirCase() function.
I tested out your function in C++14, C++17 and C++20 and it returned the correct result each time, both with the null terminating character at the end of the retorno char array and without it.
Try printing your result inside of your function before returning it, to identify if this is being caused inside of your function or outside of it. Like so:
char* invertirCase(char* str){
// [... truncated code here]
for (int i = 0; i < size; i++) {
// [... truncated code here]
}
cout << " **** TESTING INSIDE FUNCTION ****" << endl;
cout << "-- Testing index iteration" << endl;
for (int i = 0; i < size; i++) {
cout << retorno[i];
}
cout << endl;
cout << "-- Testing iterator iteration" << endl;
for (char* iterator = retorno; *iterator != '\0'; iterator++) {
cout << *iterator;
}
cout << endl;
cout << "-- Testing advanced for loop" << endl;
for (char character : retorno) {
cout << character;
}
cout << " **** END TESTING ****" << endl;
cout << endl;
return retorno;
}
This way you could possibly identify both if the problem occurs inside of your function or if the problem is occurring because of the way you may be printing your result as well.
so I am having a problem with getting my program to increment values properly.
My program needs to take a file in main(), and then pass that to a function-set to print that is called in main.
The key thing is that I need to use loops within the functions to get Letter Count, Space-Count, and Word Count.
I have the output configured right
cout << line_number << ": " << line << " [" << letter_count << " letters, " << space_count << " spaces, " << word_count << " words]" << endl;
Which results for example
0: Words go here. [# letters, # spaces, # words.]
But with my current functions for Letters and spaces, it doesn't work.
My non-space function for example
int count_non_space(string line) {
int non_space = 0;
for (int i = 0; i < line.length(); i++) {
if (line.c_str() != " ") {
non_space++;
}
}
return non_space;
It counts all of the characters in the line instead and the counterpart (space_count) counts nothing.
And that's not to mention that I don't know how to count the words in the line.
Any advice as to what is going on? as I am certain that count_space and count_non_space should be inverses of each other (count_space being the same function but with == instead of !=)
EDIT: Got the Letter and Space count correct.
Now, how would I get the word count from that sort of method?
EDIT 2: Okay so letter count is off.
It is counting puncutation-characters (commas, periods, dashes, hiphons.etc) as leters.
I have managed to redact periods, dashes.etc from the code manually with a reduction if statement in the count_non_characters function.
But I can't add ' to it as it already uses '' to catch the char comparison
Is there catch-all term for punctuation characters in C++ that I can use for
if (line[i] == "Puncutation") {
non_space--;
}
?
As UnholySheep said, when you compare a c string (char *) you can't use standard logical operators. You will need to use strcmp(). However, if you use a c++ std::string then you can use compare() or logical operators.
As for finding words in a string. Here are a few resources.
c++ counting how many words in line
C++ function to count all the words in a string
C++ Program to find number of Digits and White Spaces in a String
Count words in a given string
For further help. Google: "Get word count per line c++"
Reminder, these two are different data types and have different library support:
std::string myStr
myStr.c_str()
If the goal is to count characters in a string that are not spaces, then there is a way to do this using the STL and lambdas that is much cleaner than writing a bunch of loops and worrying about updating variables.
int count_non_space(std::string line) {
return std::count_if(line.begin(), line.end(),
[](auto ch) {
return ch != ' ';
});
}
This also makes is very straightforward to accommodate for things like spaces and tabs.
int count_non_space(std::string line) {
return std::count_if(line.begin(), line.end(),
[](auto ch) {
return ch != ' ' && ch != '\t';
});
}
To count the opposite (just the spaces) we simply need to change the condition in the lambda.
int space_count(std::string line) {
return std::count_if(line.begin(), line.end(),
[](auto ch) {
return ch == ' ' || ch == '\t';
});
}
As Remy Lebeau helpfully points out, we don't even have to write the lambda. We can simply use the std::isspace function directly instead of the lambda.
int space_count(std::string line) {
return std::count_if(line.begin(), line.end(), std::isspace);
}
Documentation on std::count_if.
Here's how I would revise the function you gave:
int count_non_space(string line) {
int non_space = 0;
for (int i = 0; i < line.length(); i++) {
if (line[i] != ' ') {
non_space++;
}
}
return non_space;
}
Notes:
I changed line.c_str() to line[i] in order to access the ith character of line
I changed " " to ' ' so that it's comparing against the space char, not a string which only contains the space. The comparison would fail if we were comparing the ith char to a string
As for this:
And that's not to mention that I don't know how to count the words in the line.
I don't know how your requirements define a word, but if we assume a word is any contiguous clump of non-space characters, you could use this logic:
initialize bool in_word to false
initialize int word_count to 0
for each char in the string:
if in_word is false and the current char is not a space, then set in_word to be true and increase word_count by 1
if in_word is true and the current char is a space, then set in_word to be false
return word_count
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.
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++)
This is a bit complicated, but basically I'm making a program and one of my functions is acting a bit strange. The function is fed an array of characters, the first time it's
new_sensor_node SN42 42 3.57 5.0 7.
right now the function just prints out each individual "token" (each set of characters separated by spaces). Then prints a space, and then prints the number of characters in the token. But for some reason, the last token is always printed weird, and 1 character extra is counted.
Here's the function:
int parseCommandLine(char cline[], char *tklist[]){
int i;
int length;
int count = 0; //counts number of tokens
int toklength = 0; //counts the length of each token
length = strlen(cline);
for (i=0; i < length; i++) { //go to first character of each token
if (((cline[i] != ' ' && cline[i-1]==' ') || i == 0)&& cline[i]!= '"') {
while ((cline[i]!=' ')&& (cline[i] != '\0')){
toklength++;
cout << cline[i];
i++;
}
cout << " " << toklength << "\n\n";
cout << "\n";
toklength = 0;
count ++;
}
if (cline[i] == '"') {
do {
i++;
} while (cline[i]!='"');
count++;
}
}
//cout << count << "\n";
return 0;
And here's the output (for that first array):
new_sensor_node 15
SN42 4
42 2
3.57 4
5.0 3
7.
3
Any thoughts on what could be causing this? I suspect it might have to do with how I'm dealing with the null character
It's very likely that the input string actually contains the newline at the end. Depending on how you read the input, it may or may not be in the input. For example, the fgets function reads the newline and leaves it in the buffer.
Especially since you don't actually do any actual tokenization or modification of the input string, you just print character by character, this is a very likely scenario.