How to ignore non-letters from std::cin - c++

So, I am trying to read a string from cin, then loop through the string to count which characters in that string are actually letters in the English alphabet. I have wrote a program that works just fine, but I want to know if there is a more efficient way of doing this, without looping through the entire English alphabet.
#include <iostream>
#include <string>
using namespace std;
int main() {
string my_str; //will use to store user input
getline(cin, my_str); //read in user input to my_str
int countOfLetters = 0; //begine count at 0
string alphabet = "abcdefghijklmnopqrstuwxyz"; //the entire english alphabet
for(int i = 0; i < my_str.length(); i++){
for(int j = 0; j < alphabet.length(); j++){
if (tolower(my_str.at(i)) == alphabet.at(j)){ //tolower() function used to convert all characters from user inputted string to lower case
countOfLetters += 1;
}
}
}
cout << countOfLetters;
return 0;
}
EDIT: Here is my new and improved code:
#include <iostream>
#include <string>
using namespace std;
int main() {
string my_str; //will use to store user input
getline(cin, my_str); //read in user input to my_str
int countOfLetters = 0; //begine count at 0
string alphabet = "abcdefghijklmnopqrstuwxyz"; //the entire english alphabet
for(unsigned int i = 0; i < my_str.length(); i++){
if (isalpha(my_str.at(i))){ //tolower() function used to convert all characters from user inputted string to lower case
countOfLetters += 1;
}
}
cout << countOfLetters;
return 0;
}
enter code here

Use isalpha() to see which characters are letters and exclude them.
So, you could modify your code like this:
#include <iostream>
#include <string>
using namespace std;
int main() {
string my_str;
getline(cin, my_str);
int countOfLetters = 0;
for (size_t i = 0; i < my_str.length(); i++) { // int i produced a warning
if (isalpha(my_str.at(i))) { // if current character is letter
++countOfLetters; // increase counter by one
}
}
cout << countOfLetters;
return 0;
}

You could perhaps use isalpha:
for(int i = 0; i < my_str.length(); i++)
if (isalpha(my_str.at(i))
countOfLetters++;

You can use the std::count_if() algorithm along with the iterator interface to count characters for which some predicate returns true. The predicate can use std::isalpha() to check for alphabetical characters. For example:
auto count = std::count_if(std::begin(str), std::end(str),
[&] (unsigned char c) { return std::isalpha(c); });

You could also check if the int cast is between 65-90 or 97-122
at example
(int)'a'
Should give 97
This will be the most performant method without any doubt.
Its better than using isalpha().
Check http://www.asciitable.com/ for ASCI Numbers

isalpha works well for this particular problem, but there's a more general solution if the list of characters to accept wasn't so simple. For example if you wanted to add some punctuation.
std::set<char> good_chars;
good_chars.insert('a'); good_chars.insert('A');
good_chars.insert('b'); good_chars.insert('B');
// ...
good_chars.insert('z'); good_chars.insert('Z');
good_chars.insert('_');
// the above could be simplified by looping through a string of course
for(int i = 0; i < my_str.length(); i++){
countOfLetters += good_chars.count(my_str[i]);
}

Related

C++ String Subcript Out of Range when using string.length() as end of for loop

I have a C++ function that is supposed to iterate through the characters in a string and change it to lower case. I am using the length of the input string as the end of the sequence, which works in example functions in the textbook, but gives me a "subscript out of range" error when trying to implement it in the following:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
string makeLower(const string& str);
int main() {
string userString, lowerCaseUserString;
cout << "Please enter a word: ";
cin >> userString;
lowerCaseUserString = makeLower(userString);
cout << lowerCaseUserString;
return 0;
}
string makeLower(const string& str) {
int i;
string lowerCaseCopy;
for (i = 0; i < str.length(); i += 1)
lowerCaseCopy[i] = tolower(str[i]);
return lowerCaseCopy;
}
Could someone explain what I'm doing wrong? Thanks!
You lowerCaseCopy string is empty, so indexing into it is undefined behavior.
You could use push_back to add elements:
string lowerCaseCopy;
for (i = 0; i < str.length(); i += 1)
lowerCaseCopy.push_back(tolower(str[i]));
Or you could allocate enough space for it before indexing:
string lowerCaseCopy(str.length());
for (i = 0; i < str.length(); i += 1)
lowerCaseCopy[i] = tolower(str[i]);
You could also simplify the first loop with a range-for:
string lowerCaseCopy;
for (auto c : str)
lowerCaseCopy.push_back(tolower(c));

How to read in an array of chars as ints with spaces included?

I'm trying to create a program that reads in and solves incomplete 9x9 sudoku boards from a text file. One of the boards might look like this:
N
145369287
629785431
783412569
567148392
938527 14
214936758
851 74623
492853 76
376291845
I need to print out the board read in, which I'm doing by just using getline and printing the string, and then store each digit into an array, blanks can be converted to zeroes for the purpose of evaluation. If I tried to read the board in as straight ints then it would try to read all the digits of a row as one int until a space or a newline was reached, and if I try to read it in char by char with get(), I run into problems again with newlines, and I would then have to convert the array of chars to an array of ints for evaluation which I think I will run into problems with as well. This is my code so far, I figured using an istringstream would be convenient but then realized that I would have to have more for loops, so a solution without it would be ideal. Not allowed to use vectors or fancy modules or structs anything like that, it is a class assignment.
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <fstream>
using namespace std;
int main() {
ifstream infile("sudoku_infile.txt");
ofstream outfile("sudoku_outfile.txt");
int board[9][9];
char n;
char c;
string str;
stringstream into;
while (infile >> n){
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++){
getline(infile, str);
cout << str << "\n";
into << str;
}
return 0;
}
EDIT:
Ok, I've devised a solution on my own by trying to convert chars to ints and putting them in an array, but it doesn't seem to be working:
while (infile >> str){
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++){
getline(infile, str);
cout << str << "\n";
for (int z = 0; z < 9; z++){
if (isdigit(str[z])){
d = str[z] - '0';
}
else{
d = 0;
}
board[i][j] = d;
}
}
for (int m = 0; m < 9; m++){
for (int f = 0; f < 9; f++)
cout << board[m][f];
cout << endl;
}
}
I get this as output:
145369287
629785431
783412569
567148392
938527 14
214936758
851 74623
492853 76
376291845
071924836
555555555
555555555
555555555
555555555
555555555
555555555
555555555
555555555
You have to make sure, that your file just contains up to 9*9 characters - otherwise it will run out of bounds this way - but it's easy to add a bounds check in there. Because '0' starts in ASCII at index 48, I'm calculating the char value minus the magic number 48.
However you still have to add a check for ' ' by yourself (otherwise it gets initalized with -16), but I'm sure you can do it!
#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>
int main(int argc, char **argv) {
std::ifstream infile("sudoku_infile.txt");
std::ofstream outfile("sudoku_outfile.txt");
int board[9][9];
size_t index = 0;
std::string str;
while (std::getline(infile, str)){
//std::cout << str << "\n";
for (size_t i = 0; i < str.size(); i++, index++){
board[index%9][index/9] = str[i] - '0';
}
}
return 0;
}
This can easily be done with one of the standard algorithm functions, namely std::copy. You can use it with iterator helpers like std::istream_inserter and std::back_inserter.
Use the above functions to put the integers into a std::vector.
After you're done with the basics, learning to use the standard library will really help you.
For example, even though you can't use it for this assignment, the above mentioned functions could be used like this:
std::vector<std::string> vs;
std::copy(std::istream_iterator<std::string>(infile),
std::istream_iterator<std::string>(),
std::back_inserter(vs));
After the above, the vector vs will contain all white-space delimited string from the infile stream.
To then get it into a board, you could to like this:
std::array<std::array<int, 9>, 9> board;
int i = 0;
for (const std::string& s : vs)
{
int j = 0;
for (const char& c : s)
board[i][j++] = c - '0';
++i;
}

String output gives weird letters

#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
string s = "Too many tags";
for(int i = 0; i < s.size(); i++){
if(!(isspace(s[i]))){
s[i] = '#' + s[i];
}
}
cout << s << endl;
return 0;
}
I'm trying to make a program which adds # tag before each letter in the string, but on output I get weird letters.. where is my mistake?
s[i] = '#' + s[i];
modifies the value of an existing character. If you want to add new characters into your string, you should use insert:
s.insert(i, "#");
As Mark Ransom points out, you also need to move one further char through your string to avoid constantly adding "#" before the same letter. You could do this using
s.insert(i++, "#");
Note that you could always take VladimirM's advice and make slightly larger changes to something like
int i=0;
while (i<s.size()) {
if (!isspace(s[i])) {
s.insert(i++, "#");
}
i++;
}
This line:
s[i] = '#' + s[i];
isn't doing what you think it is. s[i] is a char, # is also a char. Adding these together doesn't give you the concatenation of the two characters, it gives you the addition of the integer code of the characters (so 35 for # and the ASCII code for whatever s[i] happens to be).
I add more: I think the simpler way is to use temporary variable otherwise your loop with 'insert' will go to endless loop and will hang:
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
string s = "Too many tags";
string res = "";
for(int i = 0; i < s.size(); i++){
if(!(isspace(s[i]))){
res += "#";
}
res += s[i];
}
cout << res << endl;
return 0;
}

Concatenating 2 strings together

I have two strings:
string word;
string alphabet;
word has some input that I passed on to; let's say "XYZ".
alphabet has the alphabet except XYZ, so it's "ABCDEFGHIJKLMNOPQRSTUVW"
When I tried to concat them with "+=", all I get is word (i.e. "XYZ"). I want to make it look like this:
XYZABCDEFGHIJKLMNOPQRSTUVW
What am I doing wrong? Code is basicly this vvvv
===========================encryption.cpp=================================================
#include <cstdlib>
#include <iostream>
#include "encryption.h"
#include <string>
encryption::encryption()
{
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
}
string encryption::removeletter(string word,char letter)
{
//remove letter
string temp;
int indis=0;
for(int i = 0; word[i] != '\0'; i++)
{
if(word[i] != letter)
{
temp[indis]=word[i] ;
indis++;
}
}
word=temp;
return word;
}
This is the function i have proplems with :
void encryption::encrypt(string word)//main.cpp is just calling this atm
{
string temp;
int pos;
//getting rid of the repeating letters
for(int i = 0; i < word.length(); i++)
{
if( (pos = temp.find(kelime[i])) < 0)
temp += word[i];
}
word=temp;//that ends here
//taking words letters out of the alphabet
for(int i = 0; i < word.length(); i++)
{
alfabet=removeletter(alfabe,word[i]);
}
for(int i = 0; i < word.length()-1; i++)
{
for(int j=0;alfabet[j] !='\0'; j++)
if(alfabet[j+1] =='\0') alfabet[j]='\0';
}
/* I tried += here */
}
===========================encryption.h====================================================
#ifndef encryption_h
#define encryption_h
#include<string>
class encryption
{
public:
encryption();
void encrypt(string word);
string removeletter(string word,char letter);
//some other functions,i deleted them atm
public:
string alphabet;
string encryptedalphabet;
//staff that not in use atm(deleted)
};
#endif
===========================main.cpp======================================================
#include <cstdlib>
#include <iostream>
#include "encryption.h"
#include <string>
string word;
encryption encrypted;
cin>>word;
encrypted.encrypt( word);
+= is how you do it, you must be doing something else.
string word = "XYZ";
string alphabet = "ABCDEFGHIJKLMNOPQRSTUVW";
alphabet += word;
cout << alphabet << "\n";
output
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Use
word.append(alphabet);
Note that when you want to concatenate many strings, it's more efficient to use a stringstream to avoid unnecessary string copying.
string word = "XYZ";
string alphabet = "ABCDEFGHIJKLMNOPQRSTUVW";
word += alphabet;
Now you can print 'word', and you will find your answer.

finding if one string ("AB") is subset of another ("ABCD")?

i had my midterm today. this was the first question. i could not solve this.
the exact requirement is as follows :
we have to determine if a string , lets say , "DA" is subset of another, "ABCD". the number of letters is crucial, for exmaple "DAD" is not a subset of "ABCD". because "D" is repeated twice whereas in the parent string "D" occurs once. also it can be assumed that that no. of letters of parent string is always equal to or greater than the other.
i thought a lot about this. my approach towards this was that i will compare the characters of the to-be-found substring with the parent string. if a match is found i will store its index in a third array. so in the end i will have the arrays of characters of the parent array which matched characters from the other array.this is how far i have been able to get.
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char array[] = "ABCD";
char array1[] = "AB";
int size = strlen(array);
int size1 = strlen(array1);
int temp[size];
int no = 0;
for (int i = 0; i< size1; i++)
{
for (int j = 0; j< size; j++)
{
if (array1[i]==array[j])
{
for(int k = 0; k<size ; k++)
{
if (temp[k] != j)
{
temp[no] = j;
no++;
}
}
}
}
}
for (int i = 0; i<size; i++)
cout<<endl<<temp[i]<<" ";
return 0;
}
kindly help in solving this and do tel me if you have another approach to this.
also, are arrays or a string a better approach to this problem.
i am writing in c++
thanks in advance
(I recently used this as a quiz for my students but we're using Groovy and Java.)
A simple aproach: create a copy of the string ("ABCD") and strike matched letters so that they won't match again, for example after matching a "D" and "A", the copy would be "_BC_" and it would not match another "D".
You can also count the number of occurrences of each letter in each string and make sure the count of each letter in the second string is less than or equal to the count of each letter in the first string. This might be better in the case where you want to compare multiple potential substrings to a single collection of letters (e.g. comparing all the words in the dictionary to the current letters in Boggle).
This code will do that. It has the primary limitation that it only works with strings containing the 26 capital letters in the English alphabet. But it gets the idea across.
#include <iostream>
#include <cstring>
using namespace std;
void stringToArray(char *theString, int *countArray) {
int stringLength = strlen(theString);
for (int i=0; i<26; i++) {
countArray[i] = 0;
}
for (int i=0; i<stringLength; i++) {
countArray[theString[i] - 'A']++;
}
}
bool arrayIsSubset(int *superCount, int *subCount) {
//returns true if subCount is a subset of superCount
bool isSubset = true;
for (int i=0; i<26 && isSubset; i++) {
isSubset = subCount[i] <= superCount[i];
}
return isSubset;
}
int main()
{
char array[] = "ABCD";
char array1[] = "AB";
char array2[] = "ABB";
int letterCount[26], letterCount1[26], letterCount2[26];
stringToArray(array, letterCount);
stringToArray(array1, letterCount1);
stringToArray(array2, letterCount2);
cout << "array1 is " << (arrayIsSubset(letterCount, letterCount1) ? "" : "not ") << "a subset" << endl;
cout << "array2 is " << (arrayIsSubset(letterCount, letterCount2) ? "" : "not ") << "a subset" << endl;
}
produces:
array1 is a subset
array2 is not a subset