Making strcmp() case insensitive without using strcmpi() (C++) - c++

I am working a program that searches for a string (in this case a name) in a file. I wanted the program not to be case sensitive but strcmp is. I was thinking to convert bot the file and user input to lowercase. But that would be inefficient. Any other suggestions to overcome this?
This is a fraction of code to just get an idea of the program
cout << "\n Enter the Guests name: ";
cin.getline(look_4_person, 256); //name that is being looked up
cout << "\n Searching... \n";
while(!name_file.eof())
{
++place;
name_file.getline(person,255);
if(strcmpi (person,look_4_person)==0)
{
found=place;
}
}
cout << "\n" << look_4_person << " is number " << found <<
" on the list \n";

was thinking to convert bot the file and user input to lowercase. But that would be inefficient. Any other suggestions to overcome this?
To re-think this.
This is a typical way of dealing with case sensitiveness. I mean converting both strings (file name and user input) to lowercase.
This takes O(n), where n = max(filename.size, userInput.size).
As for performance, the filename and the user input are usually tiny data, thus I am pretty sure that converting them to lower case, will be definitely not be the bottleneck of your algorithm.

while(!name_file.eof()){
++place;
name_file.getline(person,256);
for(i=0; i<200; i++)
{
person[i] = tolower(person[i]); //changes to lower case to compare
}
if(strcmp (person,look_4_person)==0){ //compares
found=place;
}

Related

Issue Comparing strings for an Answer Key (C++)

I'm working on a midterm project for my coding class, and while I've gotten the majority of kinks worked out I'm struggling with comparing two string values and determining if they are equal or not. The strings in question are ANSWERKEYand studentAnswers. The former is a constant that the latter is compared to.
The code in question is as follows.
if (studentAnswers == ANSWERKEY)
{
percentScore = 100.0;
cout << "Score: " << percentScore << " % " << 'A' << endl;
}
else if (studentAnswers != ANSWERKEY)
{
int count = 0;
double answerCount = 0.0;
while (count < ANSWERKEY.length())
{
if (studentAnswers.substr(count, count+1) == ANSWERKEY.substr(count, count+1)
{
answerCount++;
count++;
}
else
{
cout << "Incorrect answer." << endl;
count++;
}
}
percentScore = ((answerCount) / (double)ANSWERKEY.length()) * 100.0;
cout << "Percent score is " << percentScore << "%" << endl;
}
The exact issue I'm facing is that I can't work out a better way to compare the strings. With the current method, the output is the following:
The intro to the code runs fine. Only when I get to checking the answers against the key, in this case "abcdefabcdefabcdefab", do I run into issues. Regardless of what characters are changed, the program marks roughly half of all characters as mismatching and drops the score down because of it.
I've thought of using a pair of arrays, but then I can't find a solution to setting up the array when some values of it are empty. If the student's answers are too short, e.g. only 15 characters long, I don't know how to compare the blank space, or even store it in the array.
Thank you for any help you can give.
First:
if (studentAnswers == ANSWERKEY)
{...}
else if (studentAnswers != ANSWERKEY)
{ ...}
looks like an overkill when comparing strings. And where is the else part ?
Second, this is risky. Read the IEE754 and articles about cancellation, or even SO:
double answerCount = 0.0;
...
answerCount++
Third:
You are checking character by character using substr. To me it feels like using a hammer to kill a bacteria.
studentAnswers.substr(count, count+1) == ANSWERKEY.substr(count, count+1)
Fourth:
What if studentAnswers is shorter than ANSWERKEY ?
Conclusion:
You need to clarify inputs/expected outputs and use the debugger to better understand what is happening during execution. Carefully check all your variables at each step fo your program.

Baby names popularity program

I have to write a program that allows the user to input a name from the keyboard. The program should then read from the file and search for matching name among the girls and boys. If a match is found it should output the rank of the name. The program should also indicate if there is no match.
Here is my program :
ifstream fin;
fin.open( "/Users/fashiontekk/Downloads/Assignment 3 Instructions/babyNames2017.dat" );
string nameInput;
string boyName;
string girlName;
int rank= 0;
int boyRank= 0;
int girlRank =0;
cout << " Which name would you like to check? < no space in names please > " << endl;
cin >> nameInput;
fin >> boyName;
fin >> girlName;
rank++;
cout << " After going through an extensive search here is what we found out - " << endl;
if (nameInput == boyName) {
cout << nameInput << " is ranked " << rank << " in popularity among boys. " << endl;
boyRank = rank;
}
if (nameInput == girlName) {
cout << nameInput << " is ranked " << rank << " in popularity among girls. " << endl;
girlRank = rank;
}
if (boyRank < 1 || boyRank > 1000) {
cout << nameInput << " is not ranked among the top 1000 boys name. " << endl;
}
if (girlRank < 1 || girlRank > 1000) {
cout << nameInput << " is not ranked among the top 1000 girls name. " << endl;
}
cout << " Hope that is the result you were looking for ... Ending program. " << endl;
fin.close();
return 0;
}
However, my output window says : Which name would you like to check? < no space in names please >
Program ended with exit code: 0Liam
After going through an extensive search here is what we found out -
Liam is ranked 1 in popularity among girls.
Liam is not ranked among the top 1000 boys name.
Hope that is the result you were looking for ... Ending program.
I tried to type in Liam which the most popular boys name according to the file provided. I feel like my coding is right however I can't spot the error.
It is my first year in Computer Science and I don't can't find my mistake.
OK, we've all been there at some point. You need to work on your debugging skills — you're gonna need them. In particular, spend some time learning to use gdb or whatever debugger you have available. A good debugger will let you step through a program a line at a time, watch variables, and generally checkout every possible thing that could be a problem.
So let's take a look at your code with an eye toward debugging it. It's handy that the message that's emitted comes right up near the top of the program — that really narrows down the places where you could be going wrong. Here's the first part of your program:
ifstream fin;
fin.open( "babyNames2017.dat" );
if (!fin) {
cout << " File not processed ";
return 0;
}
So, the first line just declares the variable for your input file. There's not much that can go wrong there. The next line opens the file... hmmm... I'm not sure if that might be a problem or not, so let's stick a pin in it for now and keep going. The next line, if (!fin) {, is a condition that only succeeds if !fin is true, which means that fin must evaluate to false to enter this block. And it clearly does enter this block, because that's the part of the code that emits the "File not processed" message. So fin must be 0, right? OK, so how can fin possibly be 0?
I don't have the C++ iostreams documentation handy, but you should go look up what that fin.open(...) call does if it fails. Given the way you've written the code, it looks very much like you'd expect failure to set fin to 0, right? So how can that call fail? Well, for starters, you're only supplying the file name... the working directory when you run the program might be set to something you don't expect, so the file isn't found. Or the file name might not match the name of the actual file. Remember that some file systems are case sensitive, and if you're working with such a file system then the open call will fail if the file is just named babynames2017.dat or BabyNames2017.dat or anything else that doesn't exactly match your file.

How to break out of this for loop: `for (; cin >> A;);`

for (cout << "\nEnter the Sentence now:";
cin >> Ascii;
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)Ascii + RandomNumberSubtract << endl);
Probably the best advice is don't be clever. Not only do you make it hard for anyone else* to read, understand, and modify your code, you run a real risk of outsmarting yourself.
Thus, don't try to do weird and clever things to implement your loop. Just do things naturally. If they don't naturally fit into how for or while or do ... while statements are structured, then just write a generic loop and use break statements to deal with leaving the loop. e.g.
while (true) {
// Some stuff
if (i_should_break_out_of_the_loop) {
break;
}
// Some more stuff
}
This is pretty much always better than doing things like torturing the for statement in the way you have.
Once you have a clear, easily comprehensible loop, it should be relatively easy to modify it to suit your needs. (or to ask a clearer and more focused question)
*: "anyone else" also includes you three weeks from now, after you've had time for it leave your short term memory.
I strongly advise you to turn this loop into a while loop. However the following is true whether or not you do:
Just enter an EOF, then the loop will terminate.
How an EOF is input will depend on your OS (and possibly also on your terminal settings). On Linux (under default terminal settings) you get an EOF pressing Ctrl+D at the beginning of the line. On Windows, I think it's Ctrl+Z. On Mac I have no idea.
Of course you could also redirect stdin for your program to come from a file (in which case EOF is — as you would guess — generated at the end of file), or from a pipe (in which case EOF is generated as soon as the writing program closes the pipe).
If the variable Ascii is not of type char or string, you may also enter something that cannot be parsed as that variable's data type (e.g. if reading an int, anything other than a number will cause the stream to report failure and thus the loop to terminate).
You also might want to add another end condition then in the loop body (which in your for loop is currently just an empty statement). For example, you might decide that a percent sign should terminate your loop; then you could write (I'm still assuming the type of Ascii which you didn't provide is char):
cout << "\nEnter the Sentence now:";
while(cin >> Ascii)
{
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)Ascii + RandomNumberSubtract << endl);
if (Ascii == '%')
break;
}
However note that normally operator<< skips whitespace; I guess you don't want whitespace skipped. Therefore you probably shouldn't use operator<< but get; this will also allow you to use the end of line as end condition:
cout << "\nEnter the Sentence now:";
while(std::cin.get(Ascii) && Ascii != '\n')
{
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)Ascii + RandomNumberSubtract << endl);
}
However in that case, it's better to read the line in one step and then iterate through it:
cout << "\nEnter the Sentence now:";
std::string line;
std::getline(std::cin, line);
for (std::string::iterator it = line.begin; it != line.end(); ++it)
{
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)*it + RandomNumberSubtract << endl;
}
Note that in C++11, you can simplify this into
cout << "\nEnter the Sentence now:";
std::string line;
std::getline(std::cin, line);
for (auto ch: line)
{
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)ch + RandomNumberSubtract << endl;
}

count specific things within a code in c++

can anyone help me make this more generalised and more pro?
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
// open text file for input:
string file_name;
cout << "please enter file name: ";
cin >> file_name;
// associate the input file stream with a text file
ifstream infile(file_name.c_str());
// error checking for a valid filename
if ( !infile )
{
cerr << "Unable to open file "
<< file_name << " -- quitting!\n";
return( -1 );
}
else cout << "\n";
// some data structures to perform the function
vector<string> lines_of_text;
string textline;
// read in text file, line by
while (getline( infile, textline, '\n' ))
{
// add the new element to the vector
lines_of_text.push_back( textline );
// print the 'back' vector element - see the STL documentation
cout << lines_of_text.back() << "\n";
}
cout<<"OUTPUT BEGINS HERE: "<<endl<<endl;
cout<<"the total capacity of vector: lines_of_text is: "<<lines_of_text.capacity()<<endl;
int PLOC = (lines_of_text.size()+1);
int numbComments =0;
int numbClasses =0;
cout<<"\nThe total number of physical lines of code is: "<<PLOC<<endl;
for (int i=0; i<(PLOC-1); i++)
//reads through each part of the vector string line-by-line and triggers if the
//it registers the "//" which will output a number lower than 100 (since no line is 100 char long and if the function does not
//register that character within the string, it outputs a public status constant that is found in the class string and has a huge value
//alot more than 100.
{
string temp(lines_of_text [i]);
if (temp.find("//")<100)
numbComments +=1;
}
cout<<"The total number of comment lines is: "<<numbComments<<endl;
for (int j=0; j<(PLOC-1); j++)
{
string temp(lines_of_text [j]);
if (temp.find("};")<100)
numbClasses +=1;
}
cout<<"The total number of classes is: "<<numbClasses<<endl;
Format the code properly, use consistent style and nomenclature and throw out the utterly redundant comments and empty lines. The resulting code should be fine. Or “pro”.
Here, I’ve taken the efford (along with some stylistic things that are purely subjective):
Notice that the output is actually wrong (just run it on the program code itself to see that …).
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string file_name;
cout << "please enter file name: ";
cin >> file_name;
ifstream infile(file_name.c_str());
if (not infile) {
cerr << "Unable to open file " << file_name << " -- quitting!" << endl;
return -1;
}
else cout << endl;
vector<string> lines_of_text;
string textline;
while (getline(infile, textline)) {
lines_of_text.push_back(textline);
cout << lines_of_text.back() << endl;
}
cout << "OUTPUT BEGINS HERE: " << endl << endl;
cout << "the total capacity of vector: lines_of_text is: "
<< lines_of_text.capacity() << endl << endl;
int ploc = lines_of_text.size() + 1;
cout << "The total number of physical lines of code is: " << ploc << endl;
// Look for comments `//` and count them.
int num_comments = 0;
for (vector<string>::iterator i = lines_of_text.begin();
i != lines_of_text.end();
++i) {
if (i->find("//") != string::npos)
++num_comments;
}
cout << "The total number of comment lines is: " << num_comments << endl;
// Same for number of classes ...
}
I'm not really sure what you're asking, but I can point out some things that can be improved in this code. I'll focus on the actual statements and leave stylistic comments to others.
cin >> file_name;
To handle file names with spaces, better write
getline(cin, file_name);
int PLOC = (lines_of_text.size()+1);
Why do you claim that there's one more line than there actually is?
if (temp.find("//")<100)
with some complicated comment explaining this. Better write
if (temp.find("//")<temp.npos)
to work correctly on all line lengths.
cout<<"The total number of comment lines is: "<<numbComments<<endl;
Actually, you counted the number of end-of-line comments. I wouldn't call a comment at the end of a statement a "comment line".
You don't count /* */ style comments.
Counting the number of classes as };? Really? How about structs, enums, and plain superfluous semicolons? Simply count the number of occurences of the class keyword. It should have no alphanumeric character or underscore on either side.
Use proper indentation, your code is very difficult to read in its current form. Here is a list of styles.
Prefer ++variable instead of variable += 1 when possible; the ++ operator exists for a reason.
Be consistent in your coding style. If you're going to leave spaces between things like cout and <<, function arguments and the function parantheses do it, otherwise don't, but be consistent. Pick one naming convention for your variables and stick to it. There is a lot about styles you can find on google, for example here and here.
Don't use the entire std namespace, only what you need. User either using std::cout; or prefix all of your cout statements with std::
Avoid needless comments. Everyone knows what ifstream infile(file_name.c_str()); does for example, what I don't know is what your program does as a whole, because I don't really care to understand what it does due to the indentation. It's a short program, so rather than explaning every statement on its own, why not explain what the program's purpose is, and how to use it?
These are all stylistic points. Your program doesn't work in its current form, assuming your goal is to count comments and classes. Doing that is a lot more difficult than you are considering. What if I have a "};" as part of a string for example? What if I have comments in strings?
Don't import the whole std namespace, only things you need from it:
using std::string;
Use a consistent naming convention: decide whether you prefer name_for_a_variable or nameforavariable or nameForAVariable. And use meaningful names: numbComments makes me associate to very different things than would numberOfComments, numComments or commentCount.
If your original code looks like this, I strongly recommend to select a single consistent indentation style: either
if ( ... )
{
...
}
or
if ( ... )
{
...
}
bot not both in the same source file.
Also remove the useless comments like
// add the new element to the vector
This is "only" about the readability of your code, not even touching its functionality... (which, as others have already pointed out, is incorrect). Note that any piece of code is likely to be read many more times than edited. I am fairly sure that you will have trouble reading (and understanding) your own code in this shape, if you need to read it even a couple of months after.
"More professional" would be not doing it at all. Use an existing SLOC counter, so you don't reinvent the wheel.
This discussion lists a few:
http://discuss.techinterview.org/default.asp?joel.3.207012.14
Another tip: Don't use "temp.find("};}) < 100)", use "temp.find("};") != temp.npos;"
Edit: s/end()/npos. Ugh.

string program for ice cream shop (Edited again) [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
With the assistance of others, I have redone the code from scratch due to them pointing out numerous errors and things that wouldn't work. Thus I have changed the code massively.
I have the program working other than two formatting settings that I can't figure out how to get to work.
I need to only print "DAILY SCOOP REPORT" once at the top of the output, but I've moved it around but due to the way the arrays are set up I don't know where to put it.
Here is my code:
#include <iostream>
include
include
include
include
include
using namespace std;
int main()
{
string flavor_input, Capitalize;
string flavors[] = { "Chocolate", "Vanilla", "Strawberry", "Mint", "Rocky Road", "Mocha" };
int scoop_count [6] = { 0, 0, 0, 0, 0, 0 }, scoops = 0, j, k;
bool valid_option;
cout << "Welcome to Frozen Tongue Ice Cream Shop\n"<<endl;
cout << "Flavors avaliable: "<<endl;
cout << "Chocolate "<<endl;
cout << "Valnilla "<<endl;
cout << "Strawberry "<<endl;
cout << "Mint "<<endl;
cout << "Rocky Road "<<endl;
cout << "Mocha \n"<<endl;
while(true) {
cout << "Please enter the flavor of icecream sold or 'STOP' to exit.\n"<<endl;
getline (cin, flavor_input); // getline causes rocky road to be accepted with a space between the words.
string::iterator it( flavor_input.begin()); //converting the first letter of input to uppercase if not already.
if (it != flavor_input.end())
flavor_input[0] = toupper((unsigned char)flavor_input[0]);
while(++it != flavor_input.end())
{
*it = tolower((unsigned char)*it);
}
if (flavor_input == "STOP" || flavor_input == "Stop")
break;
valid_option = false;
for(int i=0;i<6;i++) //Checks to see if input matches those of possible inputs.
if(!flavor_input.compare(flavors[i]))
{
valid_option = true;
break;
}
if(!valid_option)
{
cout << "Invalid Flavor. Try again.\n\n";
flavor_input.clear();
continue;
}
for(int i=0;i<6;i++)
{
if(!flavor_input.compare(flavors[i]))
{
cout << "Enter how many scoops were sold: ";
cin >> scoops;
cin.ignore();
scoop_count[i] += scoops;
scoops = 0;
cout << '\n';
break;
}
}
}
for(int i=0;i<6;i++)
{
if(!scoop_count[i])
continue;
else
{
cout << "\nDAILY SCOOP REPORT: "<<endl;
cout << setiosflags( ios::left )
<< setw( 11 ) << flavors[i]
<< resetiosflags( ios::left )
<< setw( 4 ) << scoop_count[i] << endl;
}
}
cin.get();
return 0;
}
Thanks again for all of the assistance. It is greatly appreciated.
Thanks to all the assistance and pointing me in the direction of what to study, I have the program completed other than one last part.
I figured out that why it wasn't working when I moved the "DAILY SCOOP REPORT" line around. I had renamed the file and when I compiled it, it was outputing the "last working configuration" kinda deal if that makes sense. So I created a new project (the .cpp file has to have a certain name for submission) and put the code in it. Now the line is printed only once.
In the code block below, I have it where it lowers casing for all other letters other than the first or so it seems to be doing. The reason I have the case coding the way I do is that the instructions want the flavor report to print out with first letter of each word cap and lower after that. I am going to look into how to cap the 2nd "R" in Rocky Road, but other than the ignore white-space I don't really know how. Do I need to parse the line?
Anyone to point me in the right direction would be appreciated.
I tried but it gives error that in the first if statement "syntax error : identifier 'flavor_input'".
//converting the first letter of input to uppercase if not already.
string::iterator it( flavor_input.begin());
if flavor_input = "rocky road"
(it != flavor_input.end())
flavor_input[6] = toupper((unsigned char)flavor_input[6]);
if (it != flavor_input.end())
flavor_input[0] = toupper((unsigned char)flavor_input[0]);
while(++it != flavor_input.end())
{
*it = tolower((unsigned char)*it);
}
switch doesn't work with strings.
You need to use the operator == to select the right choice like so:
string count = // ... something
if (count == "choc") {
}
else if (count == "van") {
}
else if (count == "str") {
} // .. etc
A few other things: make sure you spell string with a consistent case, all lower case and no upper case. String is something different than string.
Make sure you surround strings with double quotes "" and not single quotes ''. single quotes are for single characters like 'a' or 'b'. double quotes are for multiple characters strings like "abc" and "hello"
Having the word count as both the function name and an argument name is probably a bad idea and will not compile because the same name means two different things.
You can't return multiple values from a function. writing something like return a,b,c; doesn't mean what you probably want it to mean. the comma (,) operator allows several expressions to be evaluated in the same statement and the result is the value of the last expression so writing return 1,2,3; is exactly the same as writing return 3;
C++ cannot switch on a string. Replace your switch(count) {...} with if/else if statements. Additionally the proper format for a string is "string", not 'string' (single quotes designate a single character, like: 'a'). Also, ensure that you always use the correct casing for string objects (string as opposed to String, like you have as your return values)
Other than that, it would be helpful to see the compiler errors you are getting.
Another thing I noticed in your source: you will have to omit the semicolons (-cola?) at the end of the following lines:
cout << "Please enter the flavor of icecream sold or 'STOP' to exit.\n"<<endl
<< "Flavors avaliable:\n"<<endl
<< "chocolate\n"<<endl
<< "valnilla\n"<<endl
<< "strawberry\n"<<endl
<< "mint\n"<<endl
<< "rocky road\n"<<endl
<< "mocha\n"<<endl
<< "Enter flavor : ";
This is just a single huge expression. The same applies to
cout << "\nDAILY SCOOP REPORT: \n"<<endl
<< "chocolate :"<<chocolate<<"\n"<<endl
<< "vanilla :"<<vanilla<<"\n"<<endl
<< "strawberry :"<<strawberry<<"\n"<<endl
<< "mint :"<<mint<<"\n"<<endl
<< "rocky road :"<<rocky_road<<"\n"<<endl
<< "mocha :"<<mocha<<"\n"<<endl;
Also: the endl and the "\n" are redundant. You will see the choices being separated by empty lines.
I haven't looked at the whole thing, but this isn't going to do what you want:
if (flavor = 'STOP' || 'stop')
I think you need:
if (flavor.compare("STOP") == 0 || flavor.compare("stop") == 0)
Let's go down the problems I see.
String count (string& flavor, string& count, string& chocolate, string& vanilla, string& strawberry, string& mint, string& rocky_road, string& mocha);
You're using String here, I'm sure you meant std::string or just string.
Inside the count function (SO is truncating the code when pasted), you're ending the line with a semicolon after endl yet trying to continue the stream output. I think you meant
Next:
if (flavor = 'STOP' || 'stop')
I think you meant to use the operator== instead of operator=, which is assignment not comparison. Also, there are no junctions in c++, so you will have to write that out as:
if (flavor == 'STOP' || flavor == 'stop')
Next:
switch (count) { case 'choc' :
Two problems here. First, you can only use plain-old-data (pod) in switch statements. Using std::string in a switch will not automatically call operator==; you will have to use if/else statements. Also, string literals are double quoted whereas character literals are single quoted.
Next:
chocCount + count;
This isn't really a statement. I'm sure you meant chocCount += count;
Next:
if (flavor = chocolate) chocCount + count;
Again, you want to use == and chocCount += count;.
Most of these problems are repeated. You should fix each of these problems everywhere they exist. There may be other problems, but I was basically compiling that in my head.
I didn't read through all of it to find semantic problems, but your count function is clearly not returning a count (at least what I currently see posted). You are returning a String, which I assume you meant string.
That's all this human compiler is going to solve for 1 homework assignment. I could recommend you go read a good C++ tutorial.