I tried to write myself a textcounter which tells me how many characters and words are in a piece of text. Every time I try to paste in a long piece of text for it to count, it will crash or display something random.
Does anyone have any suggestions?
This is what I have written:
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "Text counter\nPlease insert text.\n";
string text = "";
getline(cin, text);
double countTotal = text.size();
cout << "Total characters: " << countTotal << "\n";
int wordCount = 1;
for (int chrSearch = 0; chrSearch < (int)text.size(); chrSearch++)
{
char chr = text.at(chrSearch);
if(chr == ' ')
{
wordCount++;
}
}
cout << "Total words: " << wordCount << "\n";
return 0;
}
First of all, the code reads at most one line: std::getline(std::cin, line) stops reading upon receiving the first newline. You can specify a character where to stop, e.g, the character '\0' is unlikely to be present in typical text. For example, you could use:
std::string text;
if (std::getline(std::cin, text, '\0')) {
// do something with the read text
}
You should also always check that input was successful. While the above would work with short texts, when the texts become large it makes more sense to read them one line at a time and eventually reading a line will fail when the end of the stream is reached.
In case you don't like the approach of reading everything up to a null character, you could read the entire stream using code like this:
std::istreambuf_iterator<char> it(std::cin), end;
std::string text(it, end);
if (!text.empty()) {
// do something with the read text
}
A few notes on the other parts of the code:
Don't use double where you mean to use an integer. You may want to use a bigger integer, e.g., unsigned long or unsigned long long but double is for floating point values.
When iterating through a sequence you should either use an unsigned integer type when dealing with indices, e.g., unsigned int or std::size_t. This way there is no need to cast the size(). Preferably you'd use iterators:
for (auto it(text.begin()), end(text.end()); it != end; ++it) {
char chr(*it);
// ...
}
or
for (char chr: text) {
// ...
}
Note that your word count is wrong if there are two consecutive spaces. Also, if you don't break your text using line breaks, you need to use '\n' as an additional whitespace character separating words. If you want to consider all spaces, you should actually use something like this to determine if a character is a space:
if (std::isspace(static_cast<unsigned char>(chr)) { ... }
The static_cast<unsigned char>(chr) is needed because char tends to be signed and using a negative value with std::isspace() results in undefined behavior. Casting the character to unsigned char avoids any problems. Note that negative characters are not entirely uncommon: for example, the second character of my last name (the u-umlaut 'ΓΌ') normally result in a negative char, e.g., when UTF-8 or ISO-Latin-1 encoding is used.
Related
I am 90% done with a homework project of mine but this last step is kicking my butt.
I have a text file that I'm going to be reading from for my program with commands on each line.
Most of the commands are a single letter, but one of them is a letter with an integer behind it.
I ideally need to read the line, if it's just a char go right into a function I've already written for the "Command". If it has a specific character, "F" in this case, I need it to also read the integer that will be separated by a space and pass that into my other function for that command.
Example;
.txt file;
R
L
L
F 20
R
R
For those who are curious I'm mimicking the function of the Logo language that used the little "turtle" to make logo animations for my homework.
Edit
I did try researching some methods to do this but most that I came up with either grabbed just the one char, or involved strings with which I could pull each "line" but then have to read and convert what was in string to separate char and int. If that is truly the "best" way to do it I'll suck it up and do it but I wanted to see if there was something that wasn't initially obvious to me.
This would be my approach:
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
int main() {
ifstream readFromFile("test.txt");
vector<string> fileWords;
string word;
while (readFromFile >> word) {
try {
int number = stoi(word); // here is your number
cout << number << endl;
} catch (const invalid_argument& exception) {
cout << exception.what() << endl; // just for debug
}
fileWords.emplace_back(word);
}
for (const auto& word: fileWords) {
cout << word << ' ';
}
readFromFile.close();
}
It reads word by word, saves it on an array and it also checks if a word is an integer (using the std::stoi function).
Solution by OP.
Resolved Kinda.
I ended up changing my fstream input to;
integer = 0;
char ch;
while(infile >> ch)
if (ch == "F")
{
infile >> integer;
}
// do stuff with code, I used a switch
Then after the switch I put I put integer back to 0.
This pulls the data I needed and stored it in the correct variables.
How to find out how many words are in line? I now that method where you count how many there are spaces. But what if someone hit 2 spaces or start line with space.
Is there any other or smarter way to solve this?
And is there any remark on my way of solving it or my code?
I solved it like this:
#include <iostream>
#include <cctype>
#include <cstring>
using namespace std;
int main( )
{
char str[80];
cout << "Enter a string: ";
cin.getline(str,80);
int len;
len=strlen(str);
int words = 0;
for(int i = 0; str[i] != '\0'; i++) //is space after character
{
if (isalpha(str[i]))
{
if(isspace(str[i+1]))
words++;
}
}
if(isalpha(str[len]))
{
words++;
}
cout << "The number of words = " << words+1 << endl;
return 0;
}
The std one-liner is:
words= distance(istream_iterator<string>(istringstream(str)), istream_iterator<string>());
streams by default skip spaces (multiple also).
So if you do something like:
string word;
int numWords = 0;
while (cin >> word) ++numWords;
That should count the number of words for simple cases (not considering what the format of a word is, skipping spaces).
If you want per line, you could read first the line, create a stream from a string, and do a similar thing like this:
string line, word;
int wordCount = 0;
getline(cin, line);
stringstream lineStream(line);
while (lineStream >> word) ++wordCount;
You should not use cin.getline and should prefer the free function std::getline, which takes a string that can be grown up and prevents stack overflows (lol). Stick to the free function for better safety.
First, you need a very specific definition of "word." Most of the answers will give slightly different counts than your attempt because you're using different definitions of what constitutes a word. Your example specifically requires alpha characters in certain positions. The answers based on streams will allow any non-space character to be part of a word.
The general solution is to come up with a precise definition of a word, transform this into a regular expression or finite state machine, and then count each instance of a match.
Here's a sample state machine solution:
std::size_t CountWords(const std::string &line) {
std::size_t count = 0;
enum { between_words, in_word } state = between_words;
for (const auto c : line) {
switch (state) {
case between_words:
if (std::isalpha(c)) {
state = in_word;
++count;
}
break;
case in_word:
if (std::isspace(c)) state = between_words;
break;
}
}
return count;
}
Some test cases to consider (and that highlight the differences among the definitions of a word):
"" empty string
" " just spaces
"a"
" one "
"count two"
"hyphenated-word"
"\"That's Crazy!\" she said." punctuation between alpha characters and adjacent spaces
"the answer is 42" should the number count as a word?
I was given a code from my professor that takes multiple lines of input. I am currently changing the code for our current assignment and I came across an issue. The code is meant to take strings of input and separate them into sentences from periods and put those strings into a vector.
vector<string> words;
string getInput() {
string s = ""; // string to return
bool cont = true; // loop control.. continue is true
while (cont){ // while continue
string l; // string to hold a line
cin >> l; // get line
char lastChar = l.at(l.size()-1);
if(lastChar=='.') {
l = l.substr(0, l.size()-1);
if(l.size()>0){
words.push_back(s);
s = "";
}
}
if (lastChar==';') { // use ';' to stop input
l = l.substr(0, l.size()-1);
if (l.size()>0)
s = s + " " + l;
cont = false; // set loop control to stop
}
else
s = s + " " + l; // add line to string to return
// add a blank space to prevent
// making a new word from last
// word in string and first word
// in line
}
return s;
}
int main()
{
cout << "Input something: ";
string s = getInput();
cout << "Your input: " << s << "\n" << endl;
for(int i=0; i<words.size(); i++){
cout << words[i] << "\n";
}
}
The code puts strings into a vector but takes the last word of the sentence and attaches it to the next string and I cannot seem to understand why.
This line
s = s + " " + l;
will always execute, except for the end of input, even if the last character is '.'. You are most likely missing an else between the two if-s.
You have:
string l; // string to hold a line
cin >> l; // get line
The last line does not read a line unless the entire line has non-white space characters. To read a line of text, use:
std::getline(std::cin, l);
It's hard telling whether that is tripping your code up since you haven't posted any sample input.
I would at least consider doing this job somewhat differently. Right now, you're reading a word at a time, then putting the words back together until you get to a period.
One possible alternative would be to use std::getline to read input until you get to a period, and put the whole string into the vector at once. Code to do the job this way could look something like this:
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>
int main() {
std::vector<std::string> s;
std::string temp;
while (std::getline(std::cin, temp, '.'))
s.push_back(temp);
std::transform(s.begin(), s.end(),
std::ostream_iterator<std::string>(std::cout, ".\n"),
[](std::string const &s) { return s.substr(s.find_first_not_of(" \t\n")); });
}
This does behave differently in one circumstance--if you have a period somewhere other than at the end of a word, the original code will ignore that period (won't treat it as the end of a sentence) but this will. The obvious place this would make a difference would be if the input contained a number with a decimal point (e.g., 1.234), which this would break at the decimal point, so it would treat the 1 as the end of one sentence, and the 234 as the beginning of another. If, however, you don't need to deal with that type of input, this can simplify the code considerably.
If the sentences might contain decimal points, then I'd probably write the code more like this:
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>
class sentence {
std::string data;
public:
friend std::istream &operator>>(std::istream &is, sentence &s) {
std::string temp, word;
while (is >> word) {
temp += word + ' ';
if (word.back() == '.')
break;
}
s.data = temp;
return is;
}
operator std::string() const { return data; }
};
int main() {
std::copy(std::istream_iterator<sentence>(std::cin),
std::istream_iterator<sentence>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
Although somewhat longer and more complex, at least to me it still seems (considerably) simpler than the code in the question. I guess it's different in one way--it detects the end of the input by...detecting the end of the input, rather than depending on the input to contain a special delimiter to mark the end of the input. If you're running it interactively, you'll typically need to use a special key combination to signal the end of input (e.g., Ctrl+D on Linux/Unix, or F6 on Windows).
In any case, it's probably worth considering a fundamental difference between this code and the code in the question: this defines a sentence as a type, where the original code just leaves everything as strings, and manipulates strings. This defines an operator>> for a sentence, that reads a sentence from a stream as we want it read. This gives us a type we can manipulate as an object. Since it's like a string in other ways, we provide a conversion to string so once you're done reading one from a stream, you can just treat it as a string. Having done that, we can (for example) use a standard algorithm to read sentences from standard input, and write them to standard output, with a new-line after each to separate them.
Here is the problem I'm working on:
Given an inputted line of text, print it without any spaces.
This is my attempt at a solution:
#include <iostream>
int main()
{
using namespace std;
std::string text;
cin >> text;
for(int i=0; i<text.size(); i++) {
if(text[i]==' ') {
text.erase(text.begin()+i);
}
}
cout << text <<"\n";
}
This code prints the string and stop at the first space. What have I missed?
The question is actually a bit vague: by "without any spaces",
do you mean "without any white space" or "without any space
characters"; the expression is widely used with both meanings.
(The space character is white space, but so it a tab, or even
a new line.)
At any rate, std::cin >> text will never put any white space into
text; that's how it is defined. If you want to read
a complete line, you need std::getline. And while you're on
the right track with your loop, you don't test the character
immediately after the one you erased. This is a classic problem
when removing elements; when you remove an element, you don't
want to increment.
For the rest, of course: I'm assuming you're doing this as an
exercise: a professional would probably write:
text.erase( std::remove( text.begin(), text.end(), ' ' ), text.end() );
(or use std::remove_if and a functional object if the goal was
to remove all white space).
And finally, if you switch to using std::isspace: you cannot
call it directly with a char without risking undefined
behavior. You must convert your char to unsigned char
first.
From cin >> reference: http://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt2
The extraction stops if one of the following conditions are met: a whitespace character (as determined by the ctype<CharT> facet) is found. The whitespace character is not extracted. (...)
tl;dr - using cin >> text; will stop extraction at first whitespace. You should use e.g. getline(), as already mentioned.
BTW, as a mental excercise: why would your code be equal to the one below?
#include <iostream>
int main() {
std::string text;
std::cin >> text;
std::cout << text << '\n';
}
what happens when you cin>> letter to int variable? I tried simple code to add 2 int numbers, first read them, than add them. But when I enter letter, it just fails and prints tons of numbers to screen. But what causes this error? I mean, I expected it to load and use ASCII code of that letter.
I assume you have code like this:
int n;
while (someCondition) {
std::cin >> n;
.....
std::cout << someOutput;
}
When you enter something that cannot be read as an integer, the stream (std::cin) enters a failed state and all following attempts at input fail as long as you don't deal with the input error.
You can test the success of an input operation:
if (!(std::cin >> n)) //failed to read int
and you can restore the stream to a good state and discard unprocessed characters (the input that caused the input failure):
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Another possibility is to accept input into a string variable (which rarely fails) and try to convert the string to int.
This is a very common problem with input and there should be tons of threads about it here and elsewhere.
if you explicitly cast your char to an int, it'll use the ASCII code, else it's not doing the cast by itself to int, so you get the strange results you are getting.
You could use int istream::get();
The issue is that when you read into an integer variable, using C++ streams, the input method expects characters that can make up a number, such as '+', '-', '0' ... '9'. Any character that is not in this set terminates the input and a number is created from these characters.
To get the ASCII value of a character, you need to input the data into a char variable. Internally, it has the ASCII value (provided the program in not reading 16-bit chars or EBCDIC). If you want to see the ASCII value, you will need to output the char variable as an integer (generally requiring a cast before output).
If reading an integer fails, your program needs to inform the User that the input was incorrect. The program may require the User to input the number again before the program can process the input data.
When you are doing something like:
int x;
cin >> x;
You are instructing C++ to expect to read an int from keyboard. Actually, when you enter something not an int, the input stream (standard input, in this case) renders unusable due to its error state.
That's why I personally prefer to always read strings, and convert them to numbers if needed. There are conversion functions in the C part of the C++ standard library that can help with this.
double cnvtToNumber(const std::string &num)
{
char * ptr;
double toret = strtod( num.c_str(), &ptr );
if ( *ptr != 0 ) { // If it got to the end of the string, then it is correct.
throw std::runtime_error( "input was not a number" );
}
return toret;
}
Here you are converting a number, and detecting if the conversion was right. strtod(s) will only stop when finding the end of the string, or a space. In order to avoid trailing spaces fooling the function, you could need a trim function:
std::string &trim(std::string &str)
{
// Remove trailing spaces
unsigned int pos = str.length() -1;
while( str[ pos ] == ' ' ) {
--pos;
}
if ( pos < ( str.length() -1 ) ) {
str.erase( pos + 1 );
}
// Remove spaces at the beginning
pos = 0;
while( str[ pos ] == ' ' ) {
++pos;
}
if ( pos < 1 ) {
str.erase( 0, pos );
}
return str;
}
Now you can safely read from console (or whatever other input stream):
int main()
{
std::string entry;
try {
std::getline( std::cin, entry );
int age = cnvtToNumber( trim( entry ) );
std::cout << age << std::endl;
} catch(const std::exception &e) {
std::cerr << e.what() << std::endl;
}
}
Of course you can lose precision if you always read double's and then convert them. There are specific functions for integers (strtol(s)), in case you want to investigate further.