Code reading extra line in my loop? - c++

My goal is to read from an input file and count the number of lines that have at least 1 lowercase letter and 1 digit. I have already solved the rest of my code which counted all of the lowercase, uppercase, digits, characters and words no problem. I have also read from the input file and reversed the lines word by word. I cannot seem to figure out why the code is counting 8 lines when there are only 7 with 1 lowercase and 1 digit. While using getline() for all of the other loops, I've had no issues. I'm not looking specifically for someone to write the code for me. I'd just like an explanation of why this is happening if possible?
My input file contains:
This is a test file for hw3
How many Uppercase letters are in this f1le?
How many Lowercase letters are in this F1le?
H0W mAnY dIg1ts ar3 1N in this FILe?
Turn the 1npU7 N4m3 int0 its reverse
reverse the Lines to their opp05173 coutnerpart
find tOTal NumbEr of characTer5 in F1le
THIS IS A TEST LINE
My code for this section is:
inFile.clear();
inFile.seekg(0, inFile.beg);
while(getline(inFile, line)){
wordInput.str(line);
wordInput.clear();
wordInput.seekg(0);
while(wordInput.get(c)){
if(islower(c)){
lowerCase++;
}
else if(isdigit(c)){
digit++;
}
}
if(lowerCase >= 1 && digit >= 1){
lineCount++;
}
}
cout << lineCount << endl;
return 0;
}
I have initialized all of my int variables to 0 and the top and I have declared my sstream variables as well. My libraries include <sstream> <fstream> <string> <iostream> and <algorithm> (which was used for earlier parts.
The output I am getting is
8
when it should be 7. The final line should not be counted as it has no lowercase letters and no digits. I am thinking that the first line is being read a second time and then stopping. I am in an intro to C++ class and have yet to learn how to use the debugger. Thank you in advance.

You made it clear that that you initialise all int variables to 0, which is great; however, let's have a look at your code (formatted so the indentation makes more sense):
// read line from file
while(getline(inFile, line))
{
wordInput.str(line);
wordInput.clear();
wordInput.seekg(0);
// read character
while(wordInput.get(c))
{
if(islower(c))
{
lowerCase++;
}
else if(isdigit(c))
{
digit++;
}
}
if(lowerCase >= 1 && digit >= 1){
lineCount++;
}
}
Here you read a line, and go through all characters on that line, and if you find a lowercase character, or a digit, you increment a variable. What happens when you read the next line? You haven't reset those variables back to 0, so upon reading the next line they would both be above 1 already.
You need the following:
while(getline(inFile, line))
{
wordInput.str(line);
wordInput.clear();
wordInput.seekg(0);
// We're about to start reading this line, so obviously we haven't found any yet
digit = 0;
lowerCase = 0;
Better yet, you can probably just declare those variables within the read line while loop:
while(getline(inFile, line))
{
int digit = 0;
int lowerCase = 0;
Although you haven't been taught to use a debugger, a great way of debugging is with cout statements. Put some print statements in to determine what all your variables are at any given time:
while(getline(inFile, line))
{
std::cout << "read line " << line << std::endl;
while(wordInput.get(c))
{
std::cout << "lowercase found so far: " << lowerCase << std::endl;
std::cout << "digits found so far: " << digit << std::endl;
if(islower(c))
{
std::cout << "lowercase character found: " << c << std::endl;
lowerCase++;
}
else if(isdigit(c))
{
std::cout << "digit found: " << c << std::endl;
digit++;
}
}
if(lowerCase >= 1 && digit >= 1)
{
std::cout << "found a lowercase character (" << lowerCase << ") or a digit (" << digit << ")" << std::endl;
lineCount++;
}
}

It's been awhile since I have coded in C++. There are other ways to debug than a debugger program.
I would add cout << line << endl; to your first while loop. That way you can output how the lines are being read and if any are being repeated. Also check your islower(char) and isdigit(char) functions to make sure they are reading appropriate ascii ranges.

Related

How can I remove a newline from inside a string in C++?

I am trying to take text input from the user and compare it to a list of values in a text file. The values are this:
That line at the end is the cursor, not a straight line, but it doesn't matter. Anyway, I sort by word and produce the values, then check the values. Semicolon is a separator between words. All the data is basic to get the code working first. The important thing is that all the pieces of data have newlines after them. No matter what I try, I can't get rid of the newlines completely. Looking at the ASCII values shows why, My efforts remove only the new line, but not the carriage return. This is fine most of the time, but when comparing values they won't be the same because the one with the carriage return is treated as longer. Here is the important parts of the code:
int pos = 0;
while (pos != std::string::npos)
{
std::string look = lookContents.substr(pos+1, lookContents.find("\n", pos + 1) - pos);
//look.erase(std::remove(look.begin(), look.end(), '\n'), look.end());
//##
for (int i = 0; i < look.length(); i++)
{
std::cout << (int)(look[i]) << " ";
}
std::cout << std::endl;
std::cout << look << ", " << words[1] << std::endl;
std::cout << look.compare(0,3,words[1]) << std::endl;
std::cout << pos << std::endl;
//##
//std::cout << look << std::endl;
if (look == words[1])
{
std::cout << pos << std::endl;
break;
}
pos = lookContents.find("\n", pos + 1);
}
Everything between the //## are just error checking things. Heres what is outputs when I type look b:2
As you can see, the values have the ASCII 10 and 13 at the end, which is what is used to create newlines. 13 is carriage return and 10 is newline. The last one has its 10 remove earlier in the code so the code doesn't do an extra loop on an empty substring. My efforts to remove the newline, including the commented out erase function, either only remove the 13, or remove both the 10 and 13 but corrupt later data like this:
Also, you can see that using cout to print look and words1 at the same time causes look to just not exist for some reason. Printing it by itself works fine though. I realise I could fix this by just using that compare function in the code to check all but the last characters, but this feels like a temporary fix. Any solutions?
My efforts remove only the new line, but not the carriage return
The newline and carriage control are considered control characters.
To remove all the control characters from the string, you can use std::remove_if along with std::iscntrl:
#include <cctype>
#include <algorithm>
//...
lookContents.erase(std::remove_if(lookContents.begin(), lookContents.end(),
[&](char ch)
{ return std::iscntrl(static_cast<unsigned char>(ch));}),
lookContents.end());
Once you have all the control characters removed, then you can process the string without having to check for them.

Mashed-up data in string vector

I've been trying to fix this for ages,
I've got a program that takes in a random word from a supplied dictionary (txt file),
and then adds this word to a vector of strings.
The randomWord() function works as intended, but the getWords() seems to mash-up everything when I try to print out the vector.
The vector declaration:
vector<string> pass_words; //Array of words used in password
These are the two functions:
//gets a random word from the wordlist file.
string randomWord()
{
wordfile.open ("wordlist.txt"); //open the wordlist file
string wordonline;
string word;
int wordwant = rand()%58110; //Maximum is 58109 which is the last word, minimum is 0.
for (int linenum = 0; getline (wordfile, wordonline) && linenum <(wordwant+1) ; linenum++) {
if (linenum == wordwant) {
word = wordonline;
}
}
wordfile.close();
return word;
}
// gets random words based on number of words supplied.
void getWords() {
cout << "WORD GET" << endl;
string thisword;
for (int i=0 ; i<num_words; i++) {
thisword = randomWord();
pass_words.push_back(thisword) ;
cout << pass_words[i] << " " ; //debugging
}
cout << endl; //debugging
return;
}
Here is a sample output
WORD GET
posingdsate
What am I doing wrong?
Also apparently, when I put another loop for the pass_words vector, I can see the values, but never after or within the getWords() function.
Here's the other loop's output:
housemaids
--
necessitate
--
contacts
--
kampala
--
pion
--
scooped
--
posing
--
There is a very high probability that you're reading in CR characters from a CRLF delimited file, assuming it is just LF delimited, and printing them, where the carriage return will slam the cursor to the left and write over existing text.
You will need to either read this as CRLF or strip out any CR or LF characters.
You can also force a newline:
std::cout << pass_words[i] << std::endl;

Writing a backspace in a file

int main(){
std::cout << "Insert file name / or path. \n NOTE: ONLY INPUTS. DELETES PREVIOUS DATA.\nV.6" << std::endl;
std::string filen;
std::cin >> filen;
std::ofstream myFile;
try{
myFile.open(filen, std::ios::out);
}
catch(std::fstream::failure){
std::cout << "Could not open file!\n Make sure the name and data type are valid.";
system("pause");
}
while(true){
int press = getch();
if(press == 43) myFile.close();
if(press == 8){myFile << "\b" << " " << "\b";std::cout << "\b" << " " << "\b" << std::flush;}
if(press == 13){ myFile << "\n"; std::cout << "\n" << std::flush;}
if(press != 43 && press != 127 && press != 13 && press != 8){myFile << (char)press;std::cout << (char)press;}
}
return 0;
}
Whenever I choose a text file and I press backspace, and I check the document and when I check the text document, I get random characters like so:
Those are not "random characters"; those are backspace characters! i.e. exactly the input you gave.
This can be verified with a hex editor (or piping the output of your program through hexdump et al).
If you wish to replicate the behaviour of common shells, you'll have to write your own code to identify the backspace character and, instead of appending it to myFile, instead eliminate the previously-entered character.
As #BoundaryImposition pointed out already, writing "\b" to your file, will actually write a binary backspace character to your file. What you probably want instead is myFile.seekp(-1, std::ios_base::cur);. If you are on win/dos machine you likely need extra care with '\n' characters because they are translated into 0x0d 0x0a when written to a text stream (thus they require to seek back 2 positions instead of 1).
But generally, if you are not dealing with very huge files, it will be way easier to just store the content in a std::string (using pop_back or erase, to remove characters if needed) and write it to the file when you are finished.

Trying to output everything inside an exe file

I'm trying to output the plaintext contents of this .exe file. It's got plaintext stuff in it like "Changing the code in this way will not affect the quality of the resulting optimized code." all the stuff microsoft puts into .exe files. When I run the following code I get the output of M Z E followed by a heart and a diamond. What am I doing wrong?
ifstream file;
char inputCharacter;
file.open("test.exe", ios::binary);
while ((inputCharacter = file.get()) != EOF)
{
cout << inputCharacter << "\n";
}
file.close();
I would use something like std::isprint to make sure the character is printable and not some weird control code before printing it.
Something like this:
#include <cctype>
#include <fstream>
#include <iostream>
int main()
{
std::ifstream file("test.exe", std::ios::binary);
char c;
while(file.get(c)) // don't loop on EOF
{
if(std::isprint(c)) // check if is printable
std::cout << c;
}
}
You have opened the stream in binary, which is good for the intended purpose. However you print every binary data as it is: some of thes characters are not printable, giving weird output.
Potential solutions:
If you want to print the content of an exe, you'll get more non-printable chars than printable ones. So one approach could be to print the hex value instead:
while ( file.get(inputCharacter ) )
{
cout << setw(2) << setfill('0') << hex << (int)(inputCharacter&0xff) << "\n";
}
Or you could use the debugger approach of displaying the hex value, and then display the char if it's printable or '.' if not:
while (file.get(inputCharacter)) {
cout << setw(2) << setfill('0') << hex << (int)(inputCharacter&0xff)<<" ";
if (isprint(inputCharacter & 0xff))
cout << inputCharacter << "\n";
else cout << ".\n";
}
Well, for the sake of ergonomy, if the exe file contains any real exe, you'd better opt for displaying several chars on each line ;-)
Binary file is a collection of bytes. Byte has a range of values 0..255. Printable characters that can be safely "printed" form a much narrower range. Assuming most basic ASCII encoding
32..63
64..95
96..126
plus, maybe, some higher than 128, if your codepage has them
see ascii table.
Every character that falls out of that range may, at least:
print out as invisible
print out as some weird trash
be in fact a control character that will change settings of your terminal
Some terminals support "end of text" character and will simply stop printing any text afterwards. Maybe you hit that.
I'd say, if you are interested only in text, then print only that printables and ignore others. Or, if you want everything, then maybe write them out in hex form instead?
This worked:
ifstream file;
char inputCharacter;
string Result;
file.open("test.exe", ios::binary);
while (file.get(inputCharacter))
{
if ((inputCharacter > 31) && (inputCharacter < 127))
Result += inputCharacter;
}
cout << Result << endl;
cout << "These are the ascii characters in the exe file" << endl;
file.close();

removing multiple spaces in c++ from string

I have the following code to open a file and read the data from it, then take the relavent part and print it to screen.
char* search = "model name";
int Offset;
string Cpu;
ifstream CpuInfo;
CpuInfo.open ("/proc/cpuinfo");
if(CpuInfo.is_open())
{
while(!CpuInfo.eof())
{
getline(CpuInfo,Cpu);
if ((Offset = Cpu.find(search, 0)) != string::npos)
{
//cout << "found '" << search << " " << line << endl;
break;
}
}
CpuInfo.close();
}
Cpu.replace (0,13,"");
cout << Cpu
This usually outputs the type of CPU your using, but one problem is that some people have various spaces inbetween the words that it prints out.
My question is how to remove all the spaces from inbetween the words. They can of random ammount and aren't always present.
Thank you in advance.
Since your question states: "how to remove all the spaces from inbetween the words":
You can use std::remove_if from the standard <algorithm> library in addition to std::isspace:
std::string mystring = "Text with some spaces";
std::remove_if(mystring.begin(), mystring.end(), std::isspace);
This now becomes:
Textwithsomespaces
REFERENCES:
http://en.cppreference.com/w/cpp/algorithm/remove