Fixing syntax of number of line reading function - c++

I tried making a program earlier that tells the user then number of char, words, and lines in a text file. I made functions to determine the numbers of each, yet I was passing them by value. This resulted in an error since after reading the number of char it would be at the end of the file and then output zero for the other two. Now I cant seem to rewrite my functions so that the file is open and closed each time its checked for char, words, and lines. Any one see where my errors are?? Thanks! (just copied and pasted one of my functions for now).
int num_of_lines(ifstream file)
{
string myfile;
myfile = argv[1];
ifstream l;
l.open(myfile);
int cnt3 = 0;
string str;
while(getline(file, str))cnt3++;
l.close();
return(cnt3);
}
int main(int argc, char **argv)
{
int num_of_char(ifstream file);
string file;
file = argv[1];
if(argc == 1)die("usage: mywc your_file");
ifstream ifs;
ifs.open(file);
if(ifs.is_open())
{
int a, b, c;
a = num_of_lines(ifs);
cout <<"Lines: " << a << endl;
}
else
{
cerr <<"Could not open: " << file << endl;
exit(1);
}
ifs.close();
return(0);
}

There is no way to "reopen" a file other than knowing the name and creating a new ifstream, but you can use the seekg member function to set your read position in the file, and setting it to 0 will have the next read operation start from the beginning of the file.
A stream is not possible to copy, so you can't pass it "by value", but must pass it by reference.
int num_of_lines(ifstream &file)
{
int count = 0;
string str;
while (getline(file, str)) {
count++;
}
file.seekg(0);
return count;
}
For the full problem, I agree with Mats Petersson, though. Counting both characters, lines and words in one pass will be much more efficient than reading through the file three times.

Related

Why am I getting a conversion function error?

I have to read in an external file called text.txt. The file could contain a lot of data (and def > 83 chars) and I want to ignore the spaces. Then I need to get the total amount of chars in the file (not incl. spaces). I have to use strlen. I am getting a conversion type error which is confusing because I thought strlen returns an int. The error is on strlen(inputString) and more specifically on (inputString).
int main() {
fstream inFile;
string inputString;
inFile.open("text.txt", ios::in);
if (inFile) {
getline(inFile, inputString, ' ');
while (inFile)
{
int tempStringLength = strlen(inputString);
int total = 0;
total += tempStringLength;
}
}
else
cerr << "Unable to open file text.txt";
exit(1);
return 0;
}
I expect the total to be the total # of chars in the file.
The strlen function returns a size_t result which is usually equivalent to unsigned int on your platform.
Also, I haven't tried your code but it reads like an infinite loop. You might want to rearrange it like the following:
int main() {
fstream inFile;
string inputString;
inFile.open("text.txt", ios::in);
unsigned int total = 0;
if (inFile) {
while (inFile)
{
getline(inFile, inputString, ' ');
unsigned int tempStringLength =inputString.length();
total += tempStringLength;
//Or maybe combine the two previous statements into:
//total += inputString.length();
}
}
else
cerr << "Unable to open file text.txt";
exit(1);
return 0;
}
P.S.: Also, strlen expects a char[] as input. I've changed that to be string.length() instead.

How can I add a zero at the end of a string?

I'm trying to read some text out of a file called "file.dat". The problem is, that the string in the file does not include a zero at the end as for standard C. So I need something that adds the zero, so I can work with the string without getting random symbols after the string when I print it.
void cSpectrum::readSpectrum(const std::string &filename, double
tubeVoltage, double &minEnergy, std::string &spectrumName)
{
//Object with the name "inp" of the class ifstream
ifstream inp(filename, ios::binary);
//Checks if the file is open
if (!inp.is_open()) {
throw runtime_error("File not open!");
}
cout << "I opened the file!" << endl;
//Check the title of the file
string title;
char *buffer = new char[14];
inp.read(buffer, 14);
cout << buffer << endl;
}
At the moment I get the following output, I would like to get it without the ²²²²┘.
I opened the file!
x-ray spectrum²²²²┘
Simply allocate +1 more char for your array, but don't read into that char, just set it to 0:
char buffer[15];
inp.read(buffer, 14);
buffer[14] = '\0';
cout << buffer << endl;
Or, simply don't use a char[] at all, use std::string instead, see:
What is the best way to read an entire file into a std::string in C++?
I did it with the std::string now. If you want you can replace the 14 by an integer variable.
void cSpectrum::readSpectrum(const std::string & filename, double tubeVoltage, double
& minEnergy, std::string const & spectrumName){
ifstream inp(filename, ios::binary);
//Checks if the file is open
if (!inp.is_open()) {
throw runtime_error("ERROR: Could not open the file!");
}
//Reads the title
string title(14, '\0');
inp.read(&title[0], 14);
//If it is not the correct file throw an ERROR
if (title != spectrumName)
throw runtime_error("ERROR: Wrong file title");
readSpectrum(inp, tubeVoltage, minEnergy, spectrumName);
}

Encode a string of characters given a custom code table

I want to programmatically convert a string of characters stored in a file to a string of character codes (encode) by following a code table. The string of binary codes should then go to a file, from which I can revert it back to the string of characters later (decode). The codes in the code table were generated using Huffman algorithm and the code table is stored in a file.
For example, by following a code table where characters and its corresponding codes are single spaced like this:
E 110
H 001
L 11
O 111
encoding "HELLO" should output as "0011101111111"
My C++ code cannot seem to complete the encoded string. Here is my code:
int main
{
string English;
ifstream infile("English.txt");
if (!infile.is_open())
{
cout << "Cannot open file.\n";
exit(1);
}
while (!infile.eof())
{
getline (infile,English);
}
infile.close();
cout<<endl;
cout<<"This is the text in the file:"<<endl<<endl;
cout<<English<<endl<<endl;
ofstream codefile("codefile.txt");
ofstream outfile ("compressed.txt");
ifstream codefile_input("codefile.txt");
char ch;
string st;
for (int i=0; i<English.length();)
{
while(!codefile_input.eof())
{
codefile_input >> ch >> st;
if (English[i] == ch)
{
outfile<<st;
cout<<st;
i++;
}
}
}
return 0;
}
For an input string of "The_Quick_brown_fox_jumps_over_the_lazy_dog", the output string is 011100110, but it should be longer than that!
output image
Please help! Is there anything I have missed?
(n.b. my C++ code has no syntax errors)
Let's take a look at the main loop, you are doing your work in:
for (int i=0; i<English.length();)
{
while(!codefile_input.eof())
{
codefile_input >> ch >> st;
if (English[i] == ch)
{
outfile<<st;
cout<<st;
i++;
}
}
}
Your code, will read through the codefile_input once, and then will get stuck in codefile_input.eof () == true condition, and then, for (int i=0; i<English.length();) will become an infinite loop, due to the fact, that there won't be a code path, in which i is increased, and it will never reach the value equal to English.length ().
As a side note, take a read on Why is iostream::eof inside a loop condition considered wrong?.
To avoid the issue, explained above, consider reading the dictionary file, to a data container (e.g. std::map), and then, use that, while iterating through the string, that you want to encode.
For example:
std::ifstream codefile_input("codefile.txt");
char ch;
std::string str;
std::map<char, std::string> codes;
while (codefile_input >> ch >> str)
{
codes[ch] = str;
}
codefile_input.close ();
for (int i=0; i<English.length(); ++i)
{
auto it = codes.find (English[i]);
if (codes.end () != it)
{
outfile << codes->second;
cout << codes->second;
}
}
Note, you will need to #include <map> to use std::map.
In addition to solving the issue, about which, your question, was actually, about, your loop:
while (!infile.eof())
{
getline (infile,English);
}
only reads the last line of the file, while discarding all other lines, that came prior to it. If you want to process all the lines in a file, consider changing that loop to:
while (std::getline (infile, English))
{
/* Line processing goes here */
}
And, since, your dictionary is unlikely to be different for different lines, you can move that logic, to the front of this loop:
std::ifstream codefile_input("codefile.txt");
char ch;
std::string str;
std::map<char, std::string> codes;
while (codefile_input >> ch >> str)
{
codes[ch] = str;
}
codefile_input.close ();
ifstream infile("English.txt");
if (!infile.is_open())
{
cout << "Cannot open file.\n";
exit(1);
}
ofstream outfile ("compressed.txt");
string English;
while (std::getline (infile, English))
{
for (int i=0; i<English.length(); ++i)
{
auto it = codes.find (English[i]);
if (codes.end () != it)
{
outfile << codes->second;
cout << codes->second;
}
}
}
In addition, consider adding error checking for all of the files that you open. You check if you can open file English.txt, and exit if you can't, but you don't check if you could open any other file.
On unrelated note #2, considering reading Why is “using namespace std” considered bad practice? (that's why you see me using std:: explicitly in the code, that I added).

String manipulation and File IO in C++?

What I am trying to do is open a file containing a string, replace every character in that file with [character + 37], and output it on a different file "output.txt". What I'm guessing is a problem with the at function...
#include "stdafx.h"
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
void encrypt(string text, int size) {
int i;
for (i = 0; i < size; i++) {
text.at(i) = text.at(i) + 37;
}
}
int main()
{
string string;
int length = string.length();
ifstream infile;
infile.open("input.txt");
if (infile.fail()) {
cerr << "Error Opening File. " << endl;
exit(1);
}
infile >> string;
infile.close();
encrypt(string, length);
ofstream outfile;
outfile.open("output.txt");
if (infile.fail()) {
cerr << "Error Opening File. " << endl;
exit(1);
}
outfile << string;
outfile.close();
return 0;
}
First thing, you should change your encrypt function string parameter to reference type:
void encrypt(string& text, int size) {
otherwise string text is a local variable in this function, and any changes will be lost after it ends.
Another thing is that you dont need int size, use text.size() instead. Also I see you are passing as size, result of this code:
string string;
int length = string.length();
here length will always be zero, also - as you can see your variable name is the same as the type std::string, why you use such name? This is one of the reason you should not use using namespace std;
Third thing, after :
ofstream outfile;
outfile.open("output.txt");
you check if it is in fail state by using infile instead of outfile
if (infile.fail()) {
better change this check to if (!infile) {
As said by #marcinj, your encrypt routine only modifies a copy of original string, and the copy is lost when function exists.
But anyway, what you do is rather dangerous: your encrypt function can take printable characters and transform then into non printable ones while you use the text interface of streams. Even if it works, the file might not contain a correct string when done.
For example the character ù (which is common in french) will become a space which is the string delimiter, and even worse, the õ (common in portuguese) will become a \n not speaking of the Û that will give a null character!

Why does my countlines function always return 0?

So I am making a program for a simple calendar app which reads in inputs from a file input.csv (its a text file with two columns which are seperated using commas and new lines for each command).
The first thing i want to do is count the number of lines from the input file, which is passed as the third argument in the command line, so I can make an array to hold each line separately but the function countLines always returns 0!
Project code:
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
//Prototypes
int countLines (ifstream& countfiles);
int countLines(ifstream& countfile)
//counts number of lines in file passed to function
{
string line;
int numberOfLines;
numberOfLines = 0;
//reads through each line until end of file
while(getline(countfile, line))
{
numberOfLines++;
}
return numberOfLines;
}
int main (int argc, char* argv[])
{
if(argc != 3) cout << "Usage: calendar.out datafile inputfile";
//Create input streams to both files
ifstream apptsfp;
ifstream inputfp;
//Open streams to both files
apptsfp.open(argv[2]);
inputfp.open(argv[3]);
int numberOfInputs=0;
numberOfInputs = countLines(inputfp)-1;
cout << "number of input commands: " << numberOfInputs << endl;
return 0;
}
Almost certainly because you are failing to open your input file.
inputfp.open(argv[3]);
if (!inputfp.is_open())
{
cerr << "failed to open input file " << argv[3] << '\n';
return 1;
}
Files can fail to open for all sorts of reasons, you should always check for this.
BTW don't use an array to hold the input lines, use std::vector<std::string>. Then you can use push_back to add the lines to the vector. This will be easier and more efficient because you won't have to read the file twice. What more could you ask for!
std::vector<std::string> lines;
std::string line;
while (getline(inputfp, line))
lines.push_back(line);
It seems you only want two arguments, not three as you say in the question (the "first" argument is the program name). This means that the input file is in argc[2] instead, and argv[3] is a NULL pointer.
This means that your open call will fail, but you do not check for that.
Your access to argv[3] is incorrect. The second file name (third arg, including the program name in arg[0]) is in slot 2 (the array is zero-based).
Try:
apptsfp.open(argv[1]);
inputfp.open(argv[2])
You are trying to access argv[3] which is null. Try this :-
int main (int argc, char* argv[])
{
if(argc != 3)
cout << "Usage: calendar.out datafile inputfile";
//Create input streams to both files
ifstream apptsfp;
ifstream inputfp;
//Open streams to both files
apptsfp.open(argv[1]);
inputfp.open(argv[2]);
int numberOfInputs=0;
numberOfInputs = countLines(inputfp)-1;
cout << "number of input commands: " << numberOfInputs << endl;
return 0;
}