This version is working. I've made // comments throughout my code to better illustrate what I am having trouble with. The program is dependent on reading a text file. In the format of paragraphs with punctuation included.
One could copy this and the above into a text file and run the program.
// Word.cpp
#define _CRT_SECURE_NO_WARNINGS // disable warnings for strcpy
#define ARRY_SZ 100
#include <iostream>
#include <fstream>
#include "Word.h"
using namespace std;
Word::Word( const char* word )
{
ptr_ = new char[ strlen( word ) + 1 ];
strcpy( ptr_, word );
len_ = strlen( ptr_ );
}
Word::Word( const Word* theObject )
{
ptr_ = theObject->ptr_;
len_ = theObject->len_;
}
Word::~Word()
{
delete [] ptr_;
ptr_ = NULL;
}
char Word::GetFirstLetterLower()
{
// I want to use ptr_ and len_ here
// but the information is gone!
char c = 0;
return c;
}
char* Word::GetWord()
{
for (int x = 0; x < strlen( (char*)ptr_ ); x++)
ptr_[x]; // Results in a crash.
return ptr_;
}
// Word.h
const int FILE_PATH_SZ = 512;
class Word
{
private:
char* ptr_;
int len_;
public:
Word( const Word* ); // an appropriate default constructor
Word( const char* );
~Word( );
char GetFirstLetterLower( );
char* GetWord( );
static char fileEntry[ FILE_PATH_SZ ];
};
// main.cpp
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <iostream>
#include <fstream>
#include <string>
#endif
#include "Word.h"
using namespace std;
const int WORD_SZ = 100;
Word** g_wordArray;
int g_arrSz;
static char filePath[ FILE_PATH_SZ ] = {};
void FreeWordArray();
int main( const int argc, const char **argv )
{
int wrdCount = 0;
char usrMenuOption = 0,
getFirstLetter = 0,
tmpArray[WORD_SZ] = {},
*getWord = 0;
string str,
str2;
ifstream inFile,
inFile2;
do
{
cout << "Please make a selection: \n\
a) Read a text file\n\
b) Remove words starting with letter\n\
c) Print words to console\n\
d) Quit\n";
cin >> usrMenuOption;
switch( usrMenuOption )
{
case'A':
case'a':
cout << "Enter a file name: ";
cin.sync();
cin >> filePath;
inFile.open( filePath );
if ( !inFile ) return -1;
inFile >> str; // prime the eof flag
while ( !inFile.eof() )
{
inFile >> str;
wrdCount++;
g_wordArray = new Word *[wrdCount];
}
inFile.close();
inFile2.open( filePath );
while( !inFile2.eof() )
{
inFile2 >> str2;
for ( unsigned x = 0; x < str2.length(); x++ )
g_wordArray[x] = new Word( str2.c_str() );
}
cout << wrdCount << " Words read from the file " << endl;
inFile2.close();
break;
case'B':
case'b':
getFirstLetter = g_wordArray[wrdCount]->GetFirstLetterLower();
//getWord = g_wordArray[wrdCount]->GetWord();
cout << getWord << endl;
break;
case'C':
case'c':
break;
case'D':
case'd':
cout << "Quit Requested. " << endl;
break;
default:
cout << '"' << usrMenuOption << '"' << " Not Defined! " << endl;
}
} while (usrMenuOption != 'D' && usrMenuOption != 'd');
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
cin.ignore();
return 0;
}
void FreeWordArray()
{
// free the memory that is allocated
return;
}
EDIT: I've put this edit at the top since it directly answers your question of why Word is broken. Your copy constructor is wrong:
Word::Word( const Word* theObject )
{
ptr_ = theObject->ptr_;
len_ = theObject->len_;
}
this doesn't make a copy of what theObject->ptr_ points to, just the pointer. So you effectively have two Word objects pointing to the same internal string. This gets very bad when a Word object gets deleted. A proper implementation (using the techniques you did, I recomend against them) would be like this:
Word::Word( const Word* theObject )
{
ptr_ = new char[theObject->len_ + 1 ];
strcpy( ptr_, theObject->ptr_ );
len_ = theObject->len_;
}
EDIT: Earwicker noted the following as well:
... although that "copy constructor"
is not a copy constructor. So the
compiler-generated one will still
exist, and does the same memberwise
copy, and therefore the same problem
still exists.
To remedy that, you'll need to make a proper copy constructor which should have the prototype:
Word::Word(const Word &theObject);
Also this code here:
while ( !inFile.eof() )
{
inFile >> str;
wrdCount++;
g_wordArray = new Word *[wrdCount];
}
leaks like sieve! You reallocate g_wordArray after every word is read and entirely forget to delete the previous one. Once again I'll show a sane implementation using the techniques you are attempting to use.
while (inFile >> str)
{
inFile >> str;
wrdCount++;
}
g_wordArray = new Word *[wrdCount];
Notice how it counts the words, then allocates room once, after it knows how much to allocate. Now g_wordArray is ready to be used for up to wrdCount word objects.
ORIGINAL ANSWER:
why don't you just replace the Word class with std::string? It would make the code much smaller and easier to work with.
If it makes it easier for you, just do this:
typedef std::string Word;
then you can do like this:
Word word("hello");
char first_char = word[0];
plus it has the added bonus of removing the need for you to use the .c_str() member to get the c-style string.
EDIT:
I would also change your g_wordArray to just be a std::vector<Word>. That way you can simple do this:
g_wordArray.push_back(Word(str));
no more dynamic allocation! it's all done for you. Size of the word array will be limited only by the amount of RAM you have, because std::vector grows as needed when you use push_back().
Also, if you do this...guess what to get the word count you simply do this:
g_wordArray.size();
no need to manually keep track of the number of them!
EDIT:
Also, this code is broken:
while( !inFile2.eof() )
{
inFile2 >> str2;
...
}
because eof isn't set till after you attempt a read, you are better off using this pattern:
while(inFile2 >> str2)
{
...
}
which will correctly stop on EOF.
Bottom line, if you do this right, there should be very little actual code you need to write.
EDIT:
Here's a sample straight forward implementation of what I think you want. From the menu items it seems that the intention is for the user to choose option 'a' first, then 'b' zero or more times to filter out some words, then finally c to print the results (one word per line). Also, option 'D' isn't really needed since hitting Ctrl+D sends an EOF to the program and makes the "while(std::cin >> option)" test fail. Thus ending the program. (At least in my OS, it might be Ctrl+Z` for windows).
Also it makes no effort (neither did yours) to deal with punctuation, but here it is:
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <functional>
#include <iterator>
struct starts_with : public std::binary_function<std::string, char, bool> {
bool operator()(const std::string &s, char ch) const {
return s[0] == ch;
}
};
void print_prompt() {
std::cout << "Please make a selection: \na) Read a text file\nb) Remove words starting with letter\nc) Print words to console" << std::endl;
}
int main( const int argc, const char **argv) {
std::vector<std::string> file_words;
char option;
print_prompt();
while(std::cin >> option) {
switch(option) {
case 'a':
case 'A':
std::cout << "Enter a file name: ";
// scope so we can have locals declared
{
std::string filename;
std::string word;
std::cin >> filename;
int word_count = 0;
std::ifstream file(filename.c_str());
while(file >> word) {
file_words.push_back(word);
}
std::cout << file_words.size() << " Words read from the file " << std::endl;
}
break;
case 'b':
case 'B':
// scope so we can have locals declared
{
std::cout << "Enter letter to filter: ";
char letter;
std::cin >> letter;
// remove all words starting with a certain char
file_words.erase(std::remove_if(file_words.begin(), file_words.end(), std::bind2nd(starts_with(), letter)), file_words.end());
}
break;
case 'c':
case 'C':
// output each word to std::cout separated by newlines
std::copy(file_words.begin(), file_words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
break;
}
print_prompt();
}
return 0;
}
Without slogging through the entirety of your main.cpp, I did notice a problem here in your constructor/destructor set:
Word::Word( const Word* theObject )
{
ptr_ = theObject->ptr_;
len_ = theObject->len_;
}
Word::~Word()
{
delete [] ptr_;
ptr_ = NULL;
}
Notice that in your copy constructor, you simply assign your internal pointers to match the object you're copying from. But in your destructor, you delete data at your internal pointers.
Here's where that can get ulgy:
Word w1 = Word("hello");
Word w2 = Word(w1);
delete w2;
Now, what does your first word contain? The pointer will exist, but the data it references was deleted in w2's destructor.
They call it a "copy constructor" because that's what you're supposed to do: copy the thing.
Is g_wordArray[wrdCount] a valid Word object? My C++ is rusty, but it looks to me like this is beyond the end of the array. Also, you appear to be repeatedly trampling over the contents of g_wordArray in the inFile2 loop: for each string you read into str2, you're going to reinitialise the first str2.length() entries in g_wordArray. That would explain the apparent clobbering of the member variables. Finally, I'm not sure about this, but is the pointer returned by str2.c_str() still valid after you've read a new value into str2? So your GetWord() loop may be getting a garbage value from strlen and trundling off into never never land.
Related
I'm just going through some textbook c++ questions and one of them is to write a function which recursively replaces all instances of a certain letter in a string with another letter. I know that there are pre-existing functions for this, but because this chapter focuses on recursion, this question insists that the solution must be recursive. So I wrote all this out in c++ and it was fine but then I read the footnote to the question and what it says is: "for the manipulation of string objects, only the methods at and length (i.e. size) are allowed as well as the operator +". whaaa? I just don't see how you could make this happen without str.substr(pos,len) but I would be thrilled if somebody could find a way. Thanks a bunch to that special someone yo.
Here's the best code my hamster brain could come up with (also a small iterative alternative commented out at the beginning).
#include <iostream>
#include <string>
using namespace std;
// iterative solution
/* string replace (string in, char from, char to) {
string res;
for (int i{0}; i < in.length(); i++) {
if (in.at(i) == from)
res += to;
else
res += in.at(i);
}
return res;
} */
// recursive solution
string replace (string in, char from, char to) {
if (in.empty())
return "";
char first{in.at(0)};
if (first == from)
return to + replace (in.substr(1), from, to);
else
return in.at(0) + replace (in.substr(1), from, to);
}
int main () {
string in;
char from, to;
cout << "Word: ";
cin >> in;
cout << "from: ";
cin >> from;
cout << "to: ";
cin >> to;
cout << in << " --> " << replace (in, from, to) << '\n';
return 0;
}
Just provide a default argument that keeps track of the index:
string replace(string in, char from, char to, int i = 0)
{
if (i == in.length())
return in;
if (in.at(i) == from)
in.at(i) = to;
return replace(in, from, to, i + 1);
}
Here's a demo.
This only uses at(), and length(), and doesn't even use +.
Also, avoid using namespace std;, it's bad practice.
Taking into account the footnote
I read the footnote to the question and what it says is: "for the
manipulation of string objects, only the methods at and length (i.e.
size) are allowed as well as the operator +".
it seems the function should look the following way
std::string & replace( std::string &in, char from, char to, std::string::size_type pos = 0 )
{
if ( pos < in.size() )
{
if ( in.at( pos ) == from )
{
in.at( pos ) = to;
}
replace( in, from, to, pos + 1 );
}
return in;
}
Here is a demonstrative program
#include <iostream>
#include <string>
std::string & replace( std::string &in, char from, char to, std::string::size_type pos = 0 )
{
if ( pos != in.size() )
{
if ( in.at( pos ) == from )
{
in.at( pos ) = to;
}
replace( in, from, to, pos + 1 );
}
return in;
}
int main()
{
std::string in( "Hell& W&rld!" );
char from = '&';
char to = 'o';
std::cout << in << " --> ";
std::cout << replace( in, from, to ) << '\n';
return 0;
}
Its output is
Hell& W&rld! --> Hello World!
Take into account that "to replace all instances of a letter in a string with another letter" means that the source string must be changed. This in turn means that the source string must be passed to the function by reference.
#include<iostream>
#include<string>
using namespace std;
int STRLEN(char* s){
cout<<"\n1.";
int i=0;
while(s[i] != '\0'){
cout<<"\n2.";
i++;
}
return i;
}
int main(){
int i,j;
char* s1;
char* s2;
cout<<"\nEnter string : ";
cin.getline(s1,50);
cout<<s1;
cout<<"\nEnter string : ";
cin.getline(s2,50);
cout<<s2;
int L1=STRLEN(s1);
int L2=STRLEN(s2);
cout<<"\nL1 = "<<L1;
cout<<"\nL2 = "<<L2;
/*
for*(i=L1,j=0; i<L1+L2; i++,j++)
{
s1[i] = s2[j];
j++;
}
cout<<s1;*/
return 0;
}
the above code is giving me segmentation fault at line int L1=STRLEN(s1);
Please provide a solution , i want my string to be dynamically manipulated, so that i can extend the given string, also append new string to existing string without using inbuilt methods.
Also without using string data type
Actually, your function STRLEN looks norm (except couts inside and lack of const for s)
int STRLEN(const char* s)
{
int i=0;
while(s[i] != '\0')
{
i++;
}
return i;
}
The problem in memory allocation :getline does not allocate memory for you - you must allocate memory for strings
char* s1;
char* s2;
E.g. like:
char* s1 = malloc(100);
char* s2 = malloc(100);
Actually for your case with cin.getline(s2,50); 50 bytes will be enough:
char* s2 = (char*)malloc(50);
Here (char*) is explicit cast of a pointer type (see also static_cast for C++, and be informed that for C implicit cast is working in that case)
UPDATE:
Just to give you more examples and provoke more questions... the following is my modification of your program with comments for each section:
#include<iostream>
#include<string>
using namespace std;
int STRLEN(const char* s)
{
int i=0;
while(s[i] != '\0')
{
i++;
}
return i;
}
int main(void)
{
int i; // one counter will be enough
char* s1;
char* s2;
// allocation the memory
s1 = static_cast<char*>(malloc(50));
s2 = static_cast<char*>(malloc(50));
// check results of memory allocation
if(!s1 || !s2)
{
cerr << "Something went wrong!" << endl;
return 1;
}
// getting strings
cout<<"\nEnter the first string : ";
cin.getline(s1,50);
cout<< "S1 : [" << s1 << "]" << endl;
// clean input buffer before next string input
cin.clear(); // reset state of cin
cin.ignore(INT_MAX, '\n'); // clean the input buffer
// continue input
cout<<"\nEnter the second string : ";
cin.getline(s2,50);
cout<< "S2 : [" << s2 << "]" << endl;
// count sizes (like strlen)
int L1=STRLEN(s1);
int L2=STRLEN(s2);
// reallocate memory for resulting string in s1
if( !(s1 = static_cast<char*>(realloc(s1, L1+L2+1))) )
{
cerr << "Something went wrong while reallocating memory!" << endl;
return 1;
}
// manipulations with strings (like strcat)
for(i=0; i <= L2; i++) // <= to copy '\0' also
{
s1[i + L1] = s2[i];
}
cout<< "Result : [" << s1 << "]" << endl;
// deallocate memory
free(s1);
free(s2);
return 0;
}
And as molbdnilo rightly noted in the comments, in C++ it is better to use new and delete for memory allocation and deallocation, so after you figure out with my example try to get rid of C functions: malloc, realloc and free.
After that, like making your program even more C++ solution, consider changing the type of strings from char * to std::string this will definitely save you from memory allocation problem and make all other parts of program simpler (e.g. s1 += s2 operation will be possible). When you get to that read about getline for string
Hi I'm trying to take a c-string from a user, input it into a queue, parse the data with a single space depending on its contents, and output the kind of data it is (int, float, word NOT string).
E.g. Bobby Joe is 12 in 3.5 months \n
Word: Bobby
Word: Joe
Word: is
Integer: 12
Word: in
Float: 3.5
Word: months
Here's my code so far:
int main()
{
const int maxSize = 100;
char cstring[maxSize];
std::cout << "\nPlease enter a string: ";
std::cin.getline(cstring, maxSize, '\n');
//Keyboard Buffer Function
buffer::keyboard_parser(cstring);
return EXIT_SUCCESS;
}
Function:
#include <queue>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <vector>
namespace buffer
{
std::string keyboard_parser(char* input)
{
//Declare Queue
std::queue<std::string> myQueue;
//Declare String
std::string str;
//Declare iStringStream
std::istringstream isstr(input);
//While Loop to Read iStringStream to Queue
while(isstr >> str)
{
//Push onto Queue
myQueue.push(str);
std::string foundDataType = " ";
//Determine if Int, Float, or Word
for(int index = 0; index < str.length(); index++)
{
if(str[index] >= '0' && str[index] <= '9')
{
foundDataType = "Integer";
}
else if(str[index] >= '0' && str[index] <= '9' || str[index] == '.')
{
foundDataType = "Float";
break;
}
else if(!(str[index] >= '0' && str[index] <= '9'))
{
foundDataType = "Word";
}
}
std::cout << "\n" << foundDataType << ": " << myQueue.front();
std::cout << "\n";
//Pop Off of Queue
myQueue.pop();
}
}
}
Right now with this code, it doesn't hit the cout statement, it dumps the core.
I've read about using the find member function and the substr member function, but I'm unsure of how exactly I need to implement it.
Note: This is homework.
Thanks in advance!
UPDATE: Okay everything seems to work! Fixed the float and integer issue with a break statement. Thanks to everyone for all the help!
Your queue is sensible: it contains std::strings. Unfortunately, each of those is initialised by you passing cstring in without any length information and, since you certainly aren't null-terminating the C-strings (in fact, you're going one-off-the-end of each one), that's seriously asking for trouble.
Read directly into a std::string.
std::istreams are very useful for parsing text in C++... often with an initial read of a line from a string, then further parsing from a std::istringstream constructed with the line content.
const char* token_type(const std::string& token)
{
// if I was really doing this, I'd use templates to avoid near-identical code
// but this is an easier-to-understand starting point...
{
std::istringstream iss(token);
int i;
char c;
if (iss >> i && !(iss >> c)) return "Integer";
}
{
std::istringstream iss(token);
float f;
char c; // used to check there's no trailing characters that aren't part
// of the float value... e.g. "1Q" is not a float (rather, "word").
if (iss >> f && !(iss >> c)) return "Float";
}
return "Word";
}
const int maxSize = 100; // Standard C++ won't let you create an array unless const
char cstring[maxSize];
std::cout << "\nPlease enter a string: ";
if (std::cin.getline(cstring, maxSize, '\n'))
{
std::istringstream iss(cstring);
std::string token;
while (iss >> token) // by default, streaming into std::string takes a space-...
token_queue.push(token); // ...separated word at a time
for (token_queue::const_iterator i = token_queue.begin();
i != token_queue.end(); ++i)
std::cout << token_type(*i) << ": " << *i << '\n';
}
I want to keep this code but now I am just wondering if there is a way when i read in the file in my while loop if i can remove the blanks within that loop
I am having a ton of problems with removing blanks
I do not have a large understanding on reading in files
to my program so this has been very
difficult for me, can anybody tell me where
I am making my mistakes?
#include <iostream>
#include <cassert>
#include <string>
#include <fstream>
#include <cstdio>
using namespace std;
int main (void)
{
int i=0;
int current=0;
int len;
int ch;
string s1;
string s2;
ifstream fileIn;
cout << "Enter name of file: ";
cin >> s1;
fileIn.open(s1.data() );
assert(fileIn.is_open() );
while (!(fileIn.eof() ) )
{ ch=fileIn.get();
s1.insert(i,1,ch);
s1.end();
i++;}
cout << s1;
len=s1.length();
cout << len;
while (current < len-1)
{
if (!(s1[current] == ' ' && s1[current + 1] == ' ') &&
!(s1[current] == '\n' && s1[current + 1] == '\n')
)
{
s2.append(s1[current]);
}
current++;
}
return 0;
}
There are a number of things that I would do differently. Without going into details, here is what I propose; it requires C++11 (pass the -std=c++11 also to the compiler if you are using gcc or clang):
#include <algorithm>
#include <cctype>
#include <fstream>
#include <functional>
#include <iostream>
#include <locale>
using namespace std;
// trim from left
static string ltrim(string s) {
s.erase(s.begin(), find_if(s.begin(), s.end(), [](char c) { return !isblank(c); } ));
return s;
}
int main() {
string file_name;
cout << "Please enter the file name: " << flush;
cin >> file_name;
ifstream in(file_name);
if (!in.good()) {
cout << "Failed to open file \"" << file_name << "\"" << endl;
return 1;
}
string buffer;
while (getline(in, buffer)) {
buffer = ltrim(buffer);
if (!buffer.empty()) {
cout << buffer << '\n'; // <-- or write into a file as you need
}
}
return 0;
}
Now the title says you want to remove only the leading spaces but to my question you answered that you want to remove the trailing spaces as well from the end of the lines. If it is like that, use trim() instead of ltrim(). The necessary functions are:
// trim from left
static string ltrim(string s) {
s.erase(s.begin(), find_if(s.begin(), s.end(), [](char c) { return !isblank(c); } ));
return s;
}
// trim from right
static string rtrim(string s) {
s.erase(find_if(s.rbegin(), s.rend(), [](char c) { return !isblank(c); }).base(), s.end());
return s;
}
// trim from both left and right
static string trim(string s) {
return ltrim(rtrim(s));
}
There are other, most likely faster trim implementations. See, for example: What's the best way to trim std::string?
The standard library already has most of the functionality you want, so I'd do my best to rely on that to do most of the job.
Copying some data with a specified subset removed is what std::remove_copy_if is supposed to do, so I'd use it for the main loop:
std::remove_copy_if(std::istream_iterator<line>(std::cin),
std::istream_iterator<line>(),
std::ostream_iterator<std::string>(std::cout, "\n"),
[](std::string const &s){return s.empty(); });
So, given an appropriate definition of a line, that will copy lines with any empty ones removed.
Our next step is to define a line class that removes leading white-space when we extract one from a stream, and can be converted to a string. For that, I'd "cheat" a little. When we extract a character from a stream like mystream >> mychar;, it automatically skips any leading white-space. I'd use that by reading a char, then putting it back into the stream1, so I had the stream starting from the first non-whitespace character. Then I'd use getline to read the rest of the line.
1. Reading a character, then immediately putting it back into the stream is probably unusual enough to merit either a comment, or being put into a function with a descriptive name like skip_leading_blanks:
void skip_leading_blanks(std::istream &is){
char ch;
is >> ch;
is.putback(ch);
}
im trying to count the characters inside a text file in c++, this is what i have so far, for some reason im getting 4. even thou i have 123456 characters in it. if i increase or decrease the characters i still get 4, please help and thanks in advance
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const char FileName[] = "text.txt";
int main ()
{
string line;
ifstream inMyStream (FileName);
int c;
if (inMyStream.is_open())
{
while( getline (inMyStream, line)){
cout<<line<<endl;
c++;
}
}
inMyStream.close();
system("pause");
return 0;
}
You're counting the lines.
You should count the characters. change it to:
while( getline ( inMyStream, line ) )
{
cout << line << endl;
c += line.length();
}
There are probably hundreds of ways to do that.
I believe the most efficient is:
inMyStream.seekg(0,std::ios_base::end);
std::ios_base::streampos end_pos = inMyStream.tellg();
return end_pos;
First of all, you have to init a local var, this means:
int c = 0;
instead of
int c;
I think the old and easy to understand way is to use the get() function till the end char EOF
char current_char;
if (inMyStream.is_open())
{
while(inMyStream.get(current_char)){
if(current_char == EOF)
{
break;
}
c++;
}
}
Then c will be the count of the characters
this is how i would approach the problem:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main ()
{
string line;
int sum=0;
ifstream inData ;
inData.open("countletters.txt");
while(!inData.eof())
{
getline(inData,line);
int numofChars= line.length();
for (unsigned int n = 0; n<line.length();n++)
{
if (line.at(n) == ' ')
{
numofChars--;
}
}
sum=numofChars+sum;
}
cout << "Number of characters: "<< sum << endl;
return 0 ;
}
Just use good old C FILE pointers:
int fileLen(std::string fileName)
{
FILE *f = fopen(fileName.c_str(), "rb");
if (f == NULL || ferror(f))
{
if (f)
fclose(f);
return -1;
}
fseek(f, 0, SEEK_END);
int len = fell(f);
fclose(f);
return len;
}
I found out this simple method , hope this helps
while(1)
{
if(txtFile.peek() == -1)
break;
c = txtFile.get();
if(c != txtFile.eof())
noOfChars++;
}
This works for sure, it is designed to read character by character.
It could be easily put into a class and you may apply function for every char, so you may check for '\n', ' ' and so on. Just have some members in your class, where they can be saved, so you may only return 0 and use methods to get what exactly you want.
#include <iostream>
#include <fstream>
#include <string>
unsigned long int count(std::string string)
{
char c;
unsigned long int cc = 0;
std::ifstream FILE;
FILE.open(string);
if (!FILE.fail())
{
while (1)
{
FILE.get(c);
if (FILE.eof()) break;
cc++; //or apply a function to work with this char..eg: analyze(c);
}
FILE.close();
}
else
{
std::cout << "Counter: Failed to open file: " << string << std::endl;
}
return cc;
};
int main()
{
std::cout << count("C:/test/ovecky.txt") << std::endl;
for (;;);
return 0;
}
C++ provides you with a simple set of functions you can use to retrieve the size of stream segment.
In your case, we want to find the file end, which can be done by using fstream::seekg, and providing the fstream::end.
note that fstream is not implementing the end iterator overload, this is it's own end constant
When we've seeked towards the end of the file, we want to get the position of the stream pointer, using tellg (also known as the character count in our case).
But we're not done yet. We need to also set the stream pointer to its original position, otherwise we'll be reading from the end of the file. Something we don't want to do.
So lets call fstream::seekg again, but this time set the position to the begining of the file using fstream::beg
std::ifstream stream(filepath);
//Seek to end of opened file
stream.seekg(0, stream.end);
int size = stream.tellg();
//reset file pointer to the beginning of the file
stream.seekg(0, stream.beg);