I am a beginner in C++. This is an assignment I am completing therefore I must use a the function problem1() as there is another function that I need to create to find max and min. The following text file was provided to me for the assignment.
I am having a hard time with having the program read a text file that contains a set of numbers such as the following :
[253, 676, 114]
[527, 160, 277]
[364, 193, 169]
[246, 651, 378]
[536, 479, 695]
[569, 548, 83]
my code is :
using namespace std;
void problem1(string);
int main()
{
string s= "data2.txt";
problem1(s);
return 0;
}
void problem1(string s)
{
double num, sum = 0, avg;
int count=0;
ifstream file;
file.open(s);
if (file.is_open())
{
while(file>>num)
{
sum= sum + num;
count++;
}
avg= sum/count;
cout <<"Average is " << avg << endl;
file.close();
}
}
It works for text files that contine the numbers without [].
For example:
text file contains the following..
1
2
3
4
5
and finds the avg of 3.
If you look at one line of the input file, then you will see the following. It consists of
opening square bracket
first number
comma
second number
comma
third number
closing bracket
If you want to read the line and all lines in a file, then you must read everything. For the character that you are not interested in, you could use a char temp temporary variable and read those characters into it, like so:
fileStream >> temp >> first >> temp >> second >> temp >> third >> temp;
Of course you can use also more "speaking" and separate variables like so:
fileStream >> openingBracket >> first >> comma1 >> second >> comma2 >> third >> closingBracket;
Then you could also validate in an if statement, if the variables contain the expected values:
if (openingBracket == '[' and comma1 == ',' and comma2 ==',' and closingBracket==']') ....
But forget this at the moment.
So, now we know how to read a line. If we want to read all lines, we put the above statement in a while. This will then run, until it hits an End-Of-File or some other error occurs.
In the while loop body, you can do your calculations. And after the while loop body, you can output the resulting average as a double value.
Please note:
The constructor of the std::ifstream opens the file for you automatically. The destructor will close it automatically
The bool operator of the streams is overwritten. It returns, if the state is ok, or if one of the status bits is set. Therefore if (fileStream) is sufficient for a check.
the extractor operator >> returns a reference to the stream. So, if you write fileStream >> openingBracket then the result will be "fileStream", this will be used for the next extraction operation fileStream >> first and so on and so on. At the end, you will have something similar like while(fileStream). And this will run until "eof" or other failure bits are set. (See bool operator above).
Your whole program could then look like this:
#include <iostream>
#include <fstream>
#include <string>
void problem1(std::string fileName) {
// Open the file
std::ifstream fileStream{ fileName };
// Check, if everything is ok with the stream
if (fileStream) {
// These are dummy variables
char openingBracket{}, comma1{}, comma2{}, closingBracket{};
// The values in one line
int first{}, second{}, third{};
// For summing up and claculating the average
int sum{}, counter{};
// Read data, as long as there are any
while (fileStream >> openingBracket >> first >> comma1 >> second >> comma2 >> third >> closingBracket) {
// Do the necessary calculations
sum += first + second + third;
counter += 3;
}
// Output avaerage value as double
std::cout << "Average is " << static_cast<double>(sum) / static_cast<double>(counter) << '\n';
}
}
int main() {
std::string s = "data2.txt";
problem1(s);
return 0;
}
Edit:
Searching for max and min:
#include <iostream>
#include <fstream>
#include <string>
#include <limits>
void problem1(std::string fileName) {
// Open the file
std::ifstream fileStream{ fileName };
// We want to know the min max value
int max = std::numeric_limits<int>::min();
int min = std::numeric_limits<int>::max();
// Check, if everything is ok with the stream
if (fileStream) {
// These are dummy variables
char openingBracket{}, comma1{}, comma2{}, closingBracket{};
// The values in one line
int first{}, second{}, third{};
// For summing up and claculating the average
int sum{}, counter{};
// Read data, as long as there are any
while (fileStream >> openingBracket >> first >> comma1 >> second >> comma2 >> third >> closingBracket) {
// Do the necessary calculations
sum += first + second + third;
counter += 3;
if (first > max) max = first;
if (second > max) max = second;
if (third > max) max = third;
if (first < min) min = first;
if (second < min) min = second;
if (third < min) min = third;
}
// Output average value as double
std::cout << "Average is " << static_cast<double>(sum) / static_cast<double>(counter)
<< "\nMax = " << max << "\nMin = " << min << '\n';
}
}
Related
I have a program that opens a file, reads from the file and I need to ignore the strings from the file and save only the integers into variables.
So far I have tried with file.ignore(' '), but it ignores only the first line.
Does anyone has an idea how I can fix this?
My code is:
ifstream file1;
int num1, num2, num3;
int intervalNumber1, intervalNumber2, intervalNumber3;
file1.open("file1.txt");
if (file1.fail()) {
cout << "Error" << endl;
exit(1);
} else {
if (file1.is_open()) {
file1 >> num1;
file1 >> num2;
file1 >> num3;
file1 >> intervalNumber1;
file1 >> intervalNumber2;
file1 >> intervalNumber3;
}
}
File1.txt
Number 40
Number1 34
Number2 100
Interval Number [20, 50]
Interval Number1 [60, 100]
Interval Number2 [110, 300]
The simple approach is to read the strings the same way you read the integers. Assuming the format of the file is always the same and there is one word in the first the lines and two words in the next three lines and numbers are enclosed in [] and seperated by , you can just read the strings and the [ and ] and , in a char:
#include <sstream>
#include <utility>
#include <iostream>
int main() {
std::stringstream ss{R"(Number 40
Number1 34
Number2 100
Interval Number [20, 50]
Interval Number1 [60, 100]
Interval Number2 [110, 300])"};
std::string dummy_string;
char dummy_char;
int number0=0;
int number1=0;
int number2=0;
std::pair<int,int> interval0;
std::pair<int,int> interval1;
std::pair<int,int> interval2;
ss >> dummy_string >> number0;
ss >> dummy_string >> number1;
ss >> dummy_string >> number2;
ss >> dummy_string >> dummy_string >> dummy_char >> interval0.first >> dummy_char >> interval0.second >> dummy_char;
ss >> dummy_string >> dummy_string >> dummy_char >> interval1.first >> dummy_char >> interval1.second >> dummy_char;
ss >> dummy_string >> dummy_string >> dummy_char >> interval2.first >> dummy_char >> interval2.second >> dummy_char;
std::cout << number0 << " " << number1 << " " << number2 << "\n";
std::cout << interval0.first << " " << interval0.second << "\n";
std::cout << interval1.first << " " << interval1.second << "\n";
std::cout << interval2.first << " " << interval2.second << "\n";
}
Output:
40 34 100
20 50
60 100
110 300
Reading from a file stream instead of the string stream works the same.
You should consider to use a std::vector instead of naming variables foo0,foo1, foo2 etc. Also consider to use a custom struct, for example if the numbers and the intervals belong together:
struct my_data {
int number;
int interval_start;
int interval_stop;
};
For this type you can then overload the input operator <<.
You might want to use a regular expression to solve this, if there is a known pattern.
If you just want to extract all numeric characters, using std::copy_if to another array (eg a string) would do the job.
Another direct and performant option is, to read the file content into an array, (eg std::string) and iterate over the content, checking for numbers.
std::string file_content (... load file content);
for(char& c : file_content) {
if (isdigit(c)) {
have_fun_with_digit(c);
}
}
Similar actions using regular expressions
i am using two classes std::regex (std regular expression class), std::smatch (match results class) and algorithm regex_search. To read lines from a file, I use the getNewLine function, which skips empty lines
std::regex ref
where I apply a pattern that is suitable for both cases NUMBER and [NUMBER, NUMBER]. In all cases, the numbers are placed in separate groups. Pattern: [ \\[](\\d+)[ ,]*(\\d+)?
std::smatch ref
which contains at position 0 the entire search string and further indices contain the found groups
ReadNumber function read line from file and get from line NUMBER or [NUMBER, NUMBER] match_results which are converted to int by match group
stoi ref function converts found string of digits to int
#include <iostream>
#include <regex>
#include <fstream>
using namespace std;
ifstream file1;
regex re("[ \\[](\\d+)[ ,]*(\\d+)?");
bool getNewLine(ifstream& f, string& s) {
while (getline(f, s))
if (!s.empty()) return true;
return false;
}
/*
* t1 and t2 pointers for numbers read from the file,
* if t2 is nullptr then only one number is needed
*/
void ReadNumber(int *n1, int *n2=nullptr) {
smatch m;
string newLine;
if (!getNewLine(file1, newLine))
exit(1);
//Checking if a string contains a match
if (!regex_search(newLine, m, re))
exit(1);
*n1 = stoi(m[1]);
cout << *n1;
if (n2) {
//Checking that the second group contains digits
if(!m[2].length())
exit(1);
*n2 = stoi(m[2]);
cout << " " << *n2;
}
cout << endl;
}
int main()
{
const int ArrSize = 3;
int Numbers[ArrSize] = { 0,0,0 };
pair<int, int> intervalNumber[ArrSize] = { {0,0},{0,0},{0,0} };
file1.open("c:\\file1.txt");
if (file1.fail()) {
cout << "Error" << endl;
exit(1);
}
for (int i = 0; i < ArrSize; i++)
ReadNumber(&Numbers[i]);
for (int i = 0; i < ArrSize; i++)
ReadNumber(&(intervalNumber[i].first), &(intervalNumber[i].second));
file1.close();
return 0;
}
results
40
34
100
20 50
60 100
110 300
There's a series of coordinates I'm trying to write to an array so I can perform calculations on, but I haven't been able to read the file correctly since I can't ignore the headers, and when I do remove the headers it also doesn't seem to correctly write the values to the array.
The coordinate file is a txt as below.
Coordinates of 4 points
x y z
-0.06325 0.0359793 0.0420873
-0.06275 0.0360343 0.0425949
-0.0645 0.0365101 0.0404362
-0.064 0.0366195 0.0414512
Any help with the code is much appreciated. I've tried using .ignore to skip the two header lines but they don't seem to work as expected.
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
using namespace std;
int main() {
int i = 1;
int count = 1;
char separator;
const int MAX = 10000;
int x[MAX];
int y[MAX];
int z[MAX];
int dist[MAX];
char in_file[16]; // File name string of 16 characters long
char out_file[16];
ifstream in_stream;
ofstream out_stream;
out_stream << setiosflags(ios::left); // Use IO Manipulators to set output to align left
cout << "This program reads a series of values from a given file, saves them into an array and performs calculations." << endl << endl;
// User data input
cout << "Enter the input in_file name: \n";
cin >> in_file;
cout << endl;
in_stream.open(in_file, ios::_Nocreate);
cout << "Enter the output file name: \n";
cin >> out_file;
cout << endl;
out_stream.open(out_file);
// While loop in case in_file does not exist / cannot be opened
while (in_stream.fail()) {
cout << "Error opening '" << in_file << "'\n";
cout << "Enter the input in_file name: ";
cin >> in_file;
in_stream.clear();
in_stream.open(in_file, ios::_Nocreate);
}
while (in_stream.good) {
in_stream.ignore(256, '\n');
in_stream.ignore(256, '\n');
in_stream >> x[i] >> separator >>y[i] >> separator >> z[i];
i++;
count = count + 1;
}
cout << x[1] << y[1] << z[1];
in_stream.close();
out_stream.close();
return 0;
}
Within your reading of the file, you are using in_stream.ignore(256, '\n'); correctly, but you want to use it outside the while loop. When you have it inside the while loop, every time it runs, you will ignore the first two lines, then read the third. Your output would actually read in only a third of what you expect. To fix this, just move those 2 lines outside the while loop.
in_stream.ignore(256, '\n');
in_stream.ignore(256, '\n');
while (in_stream.good)
{
in_stream >> x[i] >> separator >>y[i] >> separator >> z[i];
i++;
count = count + 1;
}
This should fix your problem, but you should generally use a vector instead of an array. Vectors automatically manage memory and check for bounds instead of you having to do that.
Also, good practice is to read values out of the stream as the while condition instead of in_stream.good:
while(stream >> var)
{
//Your code here
}
Here is a good resource on why that is.
I'm reading in values from a text file and printing the to screen as strings. The idea is to read in each individual string and print them to screen along with the running average of strings read in printed next to it.
i have this for my string to float declaration
int main()
{
string inputfile, intstring;
float counter;
counter = 0;
float average;
average = 0;
float stringconv = stof(intstring);
cout << "Enter the name of a file to open\n";
cin >> inputfile;
ifstream inFile;
inFile.open(inputfile.c_str());
and later on to calculate the average
while (!inFile.eof())
{
getline(inFile, intstring, ' ');
cout << intstring <<","<<average<< endl;
//increments counter to keep average output correct
counter = counter +1;
//goes to next line at each space encountered in text file
average = (counter + stringconv) /2;
}
I've included this just in case my issue lies there. Can anyone tell me how to properly declare my conversion?
and here is a full version which compiles
#include <math.h>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
string inputfile, intstring;
float counter;
counter = 0;
float average;
average = 0;
float dividor;
dividor = 1;
cout << "Enter the name of a file to open\n";
cin >> inputfile;
ifstream inFile;
inFile.open(inputfile.c_str());
if (!inFile)
{
cout << "Error opening file " << inputfile << endl;
char stopchar;
cin >> stopchar;
return -1;
}
while (!inFile.eof())
{
//goes to next line at each space encountered in text file
getline(inFile, intstring, ' ');
cout << intstring <<","<<average<< endl;
float stringconv;
stringconv = stof(intstring);
average = (counter + stringconv)/dividor ;
dividor = dividor +1;
//increments counter to keep average output correct
}
inFile.close();
char stopchar;
cin >> stopchar;
}
Here:
string inputfile, intstring;
...
float stringconv = stof(intstring);
You can't do that. I mean, you can, but it doesn't do what you think it does. You think you're creating a macro or function or something, so that you can change intstring and then stringconv will automatically change. But what you're actually doing is converting the uninitialized string into an integer once, and never changing it again. You must do the conversion inside the read loop.
EDIT: If you're not required to use stof(), then you can save yourself a lot of headaches by using the stream input operator:
float number;
inFile >> number; // this is the basic form
while(inFile >> number) // this is how to do it as a loop
...
In C++, float stringconv = stof(intstring); won't meen automatic conversion like assign in Verilog.
Call stof() each time you need to convert.
Try this:
while (!inFile.eof())
{
getline(inFile, intstring, ' ');
cout << intstring <<","<<average<< endl;
//increments counter to keep average output correct
counter = counter +1;
//goes to next line at each space encountered in text file
stringconv = stof(intstring); // add this here
average = (counter + stringconv) /2;
}
I have a .csv file that has 3 rows and 5 columns with values of 0,1,2,3,50, or 100. I saved it from an excel sheet to a .csv file. I am trying to use C++ to read in a .csv file and output the first two column values in the .csv file into a text file based on the last three column values. I am assuming the .csv file looks like
1,1,value,value,value
1,2,value,value,value
1,3,value,value,value
But I couldn't find a whole lot of documentation on the format of .csv files.
I looked at Reading Values from fields in a .csv file? and used some of the code from there.
Here is my code:
#include <iostream>
#include <fstream>
using namespace std;
char separator;
int test_var;
struct Spaxel {
int array1;
int array2;
int red;
int blue_o2;
int blue_o3;
};
Spaxel whole_list [3];
int main()
{
// Reading in the file
ifstream myfile("sample.csv");
Spaxel data;
int n = 0;
cout << data.array1<< endl;
myfile >> data.array1; // using as a test to see if it is working
cout << data.array1<< endl;
while (myfile >> data.array1)
{
// Storing the 5 variable and getting rid of commas
cout<<"here?"<< endl;
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> data.array2;
// Skip the separator
myfile >> separator;
// Read in next value.
myfile >> data.red;
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> data.blue_o2;
// Skip the separator
myfile >> separator;
// Read in next value.
myfile >> data.blue_o3;
// Ignore the newline, as it is still in the buffer.
myfile.ignore(10000, '\n');
// Storing values in an array to be printed out later into another file
whole_list[n] = data;
cout << whole_list[n].red << endl;
n++;
}
myfile.close();
// Putting contents of whole_list in an output file
//whole_list[0].red = whole_list[0].array1 = whole_list[0].array2 = 1; this was a test and it didn't work
ofstream output("sample_out.txt");
for (int n=0; n<3; n++) {
if (whole_list[n].red == 1)
output << whole_list[n].array1 <<","<< whole_list[n].array2<< endl;
}
return 0;
}
When I run it in Xcode it prints three 0's (from the cout << data.array1<< endl; and cout << data.array1<< endl; in the beginning of the main() and from the return 0) but does not output any file. Apparently the .csv file isn't getting read in correctly and the output file is not getting written correctly. Any suggestions?
Thanks for your time!
There are a couple of problem areas in the code you presented:
Hard coded filename. Running your program in a directory that doesn't have "sample.csv" could cause the ifstream failure you're seeing.
No checking whether myfile opened successfully or not.
Loop can access an out-of-bound index in whole_list if "sample.csv" has more lines.
The refactored code below, while not completely foolproof, corrects many of the issues mentioned. It should get you most of the way there.
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
using namespace std;
struct Spaxel
{
int array1;
int array2;
int red;
int blue_o2;
int blue_o3;
};
ostream& operator << (ostream &os, const Spaxel &rhs)
{
os << rhs.array1
<< ','
<< rhs.array2
<< ','
<< rhs.red
<< ','
<< rhs.blue_o2
<< ','
<< rhs.blue_o3;
return os;
}
istream& operator >> (istream &is, Spaxel &rhs)
{
char delim;
is >> rhs.array1
>> delim
>> rhs.array2
>> delim
>> rhs.red
>> delim
>> rhs.blue_o2
>> delim
>> rhs.blue_o3;
return is;
}
int main(int argc, const char *argv[])
{
if(argc < 2)
{
cout << "Usage: " << argv[0] << " filename\n";
return 1;
}
const char *infilename = argv[argc - 1];
// Reading in the file
ifstream myfile(infilename);
if(!myfile)
{
cerr << "Couldn't open file " << infilename;
return 1;
}
vector<Spaxel> whole_list;
string line;
while( getline(myfile, line) )
{
Spaxel data;
stringstream linestr (line);
linestr >> data;
whole_list.push_back(data);
cout << data << '\n';
}
}
Edit: Just to help clarify some things from the comment.
As you know main is the entry point of your program so it isn't something called by your own code. The extra optional parameters int argc, const char *argv[], is how options and parameters get passed in when you run your program with arguments. First parameter argc indicates how many arguments were passed in. The second argv is an array of char * with each element being the argument passed. The first argument argv[0] is your program name and so argc is always >= 1.
Say you execute your sample program from the shell:
./sample sample.csv
then argc and argv will have the following:
argc = 2;
argv[0] = "sample"
argv[1] = "sample.csv"
So const char *infilename = argv[argc - 1]; gets the last argument passed in which should be the filename to read in.
Sorry i am not doing it within struct but i hope you will got it and resolve your problem.
char separator;
int value1;
int value2;
int value3;
while (myfile >> value1)
{
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> value2;
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> value3;
// Ignore the newline, as it is still in the buffer.
myfile.ignore(10000, '\n');
}
The above code fragment is not robust but demonstrates the concept of reading from a file, skipping non-numeric separators and processing the end of the line. The code is optimized either.
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
int hmlines(ifstream &a){
int i=0;
string line;
while (getline(a,line)){
cout << line << endl;
i++;
}
return i;
}
int hmwords(ifstream &a){
int i=0;
char c;
while ((c=a.get()) && (c!=EOF)){
if(c==' '){
i++;
}
}
return i;
}
int main()
{
int l=0;
int w=0;
string filename;
ifstream matos;
start:
cout << "give me the name of the file i wish to count lines, words and chars: ";
cin >> filename;
matos.open(filename.c_str());
if (matos.fail()){
goto start;
}
l = hmlines(matos);
matos.seekg(0, ios::beg);
w = hmwords(matos);
/*c = hmchars(matos);*/
cout << "The # of lines are :" << l << ". The # of words are : " << w ;
matos.close();
}
The file that i am trying to open has the following contents.
Twinkle, twinkle, little bat!
How I wonder what you're at!
Up above the world you fly,
Like a teatray in the sky.
The output i get is:
give me the name of the file i wish to count lines, words and chars: ert.txt
Twinkle, twinkle, little bat!
How I wonder what you're at!
Up above the world you fly,
Like a teatray in the sky.
The # of lines are :4. The # of words are : 0
int hmwords(ifstream &a){
int i;
You've forgotten to initialize i. It can contain absolutely anything at that point.
Also note that operator>> on streams skips whitespace by default. Your word counting loop needs the noskipws modifier.
a >> noskipws >> c;
Another problem is that after you call hmlines, matos is at end of stream. You need to reset it if you want to read the file again. Try something like:
l = hmlines(matos);
matos.clear();
matos.seekg(0, ios::beg);
w = hmwords(matos);
(The clear() is necessary, otherwise seekg has no effect.)
Formatted input eats whitespaces. You can just count tokens directly:
int i = 0;
std::string dummy;
// Count words from the standard input, aka "cat myfile | ./myprog"
while (cin >> dummy) ++i;
// Count files from an input stream "a", aka "./myprog myfile"
while (a >> dummy) ++i;