Redirecting input using stdin - c++

I am writing a short program to sort an array of integers. I am having trouble opening my input file which is "prog1.d". The assignment has asked to create a symbolic link in the programs directory, and I after creating the object & executable, we invoke the program as follows...
prog1.exe < prog1.d &> prog1.out
I know my bubble sort works correctly & efficiently because I have used my own test 'txt' file.
The assignment says:
Your program gets the random integers from stdin and puts them in an array, sorts the integers in the array in ascending order, and then displays the contents of the array on stdout.
How do I read the file using 'cin' until EOF & add the integers to my array a[] ?
Here is my code so far:
int main( int argc, char * argv[] )
{
int a[SIZE];
for ( int i=1; i<argc; i++)
{
ifstream inFile; // declare stream
inFile.open( argv[i] ); // open file
// if file fails to open...
if( inFile.fail() )
{
cout << "The file has failed to open";
exit(-1);
}
// read int's & place into array a[]
for(int i=0; !inFile.eof(); i++)
{
inFile >> a[i];
}
inFile.close(); // close file
}
bubbleSort(a); // call sort routine
printArr(a); // call print routine
return 0;
}
I know that opening a stream is the wrong way to do this, I just was using it for a test 'txt' file I was using to make sure my sorting worked. The teacher said we should redirect the input to 'cin' like someone was entering integers on a keyboard.
Any help would be greatly appreciated.

When you're using redirection on the command line, argv does not contain the redirection. Instead, the specified file simply becomes your stdin/cin. So you don't need to (and shouldn't try to) open it explicitly -- just read from the standard input, as you would read from the terminal when input isn't redirected.

Since you are piping the file on the stdin, you don't have the file name on argv[1], just read the stdin as the user was typing at the console, for example using cin:
cin.getline (...);

The other answers are completely correct, but here's the rewritten code to claify:
int main( int argc, char * argv[] )
{
int a[SIZE];
int count = 0;
// read int's & place into array a[]
//ALWAYS check the boundries of arrays
for(int i=0; i<SIZE; i++)
{
std::cin >> a[i];
if (std::cin)
count = count + 1;
else
break;
}
bubbleSort(a, count); // call sort routine
printArr(a, count); // call print routine
return 0;
}

As everyone has stated, use std::cin directly -- you don't need to open the input file, your shell has already done that for you.
But, please, please, please, don't use cin.eof() to test to see if you have reached the end of your input. If your input is flawed, your program will hang. Even if your input isn't flawed, your program may (but won't necessarily) run the loop one extra time.
Try this loop instead:
int a[SIZE];
int i = 0;
while( std::cin >> a[i]) {
++i;
}
Or, add robustness by using std::vector which will automatically grow:
std::vector<int> a;
int i;
while(std::cin >> i) {
a.push_back(i);
}
Or, use generic algorithms:
#include <iterator>
#include <algorithm>
...
std::vector<int> a;
std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(a));

Related

Creating 2D String Vector from text file

I'm having slight trouble creating a 2D Vector of String that's created by reading values from a text file. I initially thought I needed to use an array. however I've come to realise that a vector would be much more suited to what I'm trying to achieve.
Here's my code so far:
I've initialised the vector globally, but haven't given it the number of rows or columns because I want that to be determined when we read the file:
vector<vector<string>> data;
Test data in the file called "test" currently looks like this:
test1 test2 test3
blue1 blue2 blue3
frog1 frog2 frog3
I then have a function that opens the file and attempts to copy over the strings from text.txt to the vector.
void createVector()
{
ifstream myReadFile;
myReadFile.open("text.txt");
while (!myReadFile.eof()) {
for (int i = 0; i < 5; i++){
vector<string> tmpVec;
string tmpString;
for (int j = 0; j < 3; j++){
myReadFile >> tmpString;
tmpVec.push_back(tmpString);
}
data.push_back(tmpVec);
}
}
}
However, when I attempt to check the size of my vector in my main function, it returns the value '0'.
int main()
{
cout << data.size();
}
I think I just need a pair of fresh eyes to tell me where I'm going wrong. I feel like the issues lies within the createVector function, although I'm not 100% sure.
Thank you!
You should use std::getline to get the line of data first, then extract each string from the line and add to your vector. This avoids the while -- eof() issue that was pointed out in the comments.
Here is an example:
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
typedef std::vector<std::string> StringArray;
std::vector<StringArray> data;
void createVector()
{
//...
std::string line, tempStr;
while (std::getline(myReadFile, line))
{
// add empty vector
data.push_back(StringArray());
// now parse the line
std::istringstream strm(line);
while (strm >> tempStr)
// add string to the last added vector
data.back().push_back(tempStr);
}
}
int main()
{
createVector();
std::cout << data.size();
}
Live Example

Reading number list from file to a dynamic array

I'm having trouble reading a number list from a .txt file to a dynamic array of type double. This first number in the list is the number of numbers to add to the array. After the first number, the numbers in the list all have decimals.
My header file:
#include <iostream>
#ifndef SORT
#define SORT
class Sort{
private:
double i;
double* darray; // da array
double j;
double size;
public:
Sort();
~Sort();
std::string getFileName(int, char**);
bool checkFileName(std::string);
void letsDoIt(std::string);
void getArray(std::string);
};
#endif
main.cpp:
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
int main(int argc, char** argv)
{
Sort sort;
std::string cheese = sort.getFileName(argc, argv); //cheese is the file name
bool ean = sort.checkFileName(cheese); //pass in file name fo' da check
sort.letsDoIt(cheese); //starts the whole thing up
return 0;
}
impl.cpp:
#include <iostream>
#include <fstream>
#include <cstring>
#include <stdlib.h>
#include "main.h"
Sort::Sort(){
darray[0];
i = 0;
j = 0;
size = 0;
}
Sort::~Sort(){
std::cout << "Destroyed" << std::endl;
}
std::string Sort::getFileName(int argc, char* argv[]){
std::string fileIn = "";
for(int i = 1; i < argc;)//argc the number of arguements
{
fileIn += argv[i];//argv the array of arguements
if(++i != argc)
fileIn += " ";
}
return fileIn;
}
bool Sort::checkFileName(std::string userFile){
if(userFile.empty()){
std::cout<<"No user input"<<std::endl;
return false;
}
else{
std::ifstream tryread(userFile.c_str());
if (tryread.is_open()){
tryread.close();
return true;
}
else{
return false;
}
}
}
void Sort::letsDoIt(std::string file){
getArray(file);
}
void Sort::getArray(std::string file){
double n = 0;
int count = 0;
// create a file-reading object
std::ifstream fin;
fin.open(file.c_str()); // open a file
fin >> n; //first line of the file is the number of numbers to collect to the array
size = n;
std::cout << "size: " << size << std::endl;
darray = (double*)malloc(n * sizeof(double)); //allocate storage for the array
// read each line of the file
while (!fin.eof())
{
fin >> n;
if (count == 0){ //if count is 0, don't add to array
count++;
std::cout << "count++" << std::endl;
}
else {
darray[count - 1] = n; //array = line from file
count++;
}
std::cout << std::endl;
}
free((void*) darray);
}
I have to use malloc, but I think I may be using it incorrectly. I've read other posts but I am still having trouble understanding what is going on.
Thanks for the help!
Your use of malloc() is fine. Your reading is not doing what you want it to do.
Say I have the inputfile:
3
1.2
2.3
3.7
My array would be:
[0]: 2.3
[1]: 3.7
[2]: 0
This is because you are reading in the value 1.2 as if you were rereading the number of values.
When you have this line:
fin >> n; //first line of the file is the number of numbers to collect to the array
You are reading in the count, in this case 3, and advancing where in the file you will read from next. You are then attempting to reread that value but are getting the first entry instead.
I believe that replacing your while() {...} with the code below will do what you are looking for.
while (count != size && fin >> n)
{
darray[count++] = n; //array = line from file
std::cout << n << std::endl;
}
This should give you the correct values in the array:
[0]: 1.2
[1]: 2.3
[2]: 3.7
You appear to be writing the next exploitable program. You are mistakenly trusting the first line of the file to determine your buffer size, then reading an unlimited amount of data from the remainder of the file into a buffer that is not unlimited. This allows an evil input file to trash some other memory in your program, possibly allowing the creator of that file to take control of your computer. Oh noes!
Here's what you need to do to fix it:
Remember how much memory you allocated (you'll need it in step #2). Have a variable alleged_size or array_length that is separate from the one you use to read the rest of the data.
Don't allow count to run past the end of the array. Your loop should look more like this:
while ((count < alleged_size) && (cin >> n))
This both prevents array overrun and decides whether to process data based on whether it was parsed successfully, not whether you reached the end-of-file at some useless point in the past.
The less problematic bug is the one #bentank noticed, that you didn't realize that you kept your position in the file, which is after the first line, and shouldn't expect to hit that line within the loop.
In addition to this, you probably want to deallocate the memory in your destructor. Right now you throw the data away immediately after parsing it. Wouldn't other functions like to party on that data too?

Read Numeric Data from a Text File in C++

This is the program that needs to write a code to read data from file. The file looks like:
1234 200.55
5678 1234.56
9876 2.33
I need to store the first number as the account number and the second one as the balance of the account.
#include<iostream>
#include<fstream>
using namespace std;
const int MAX_NUM=100;
int read_accts(int acctnum[], double balance[], int max_accts);
int main()
{
int acctnum[MAX_NUM];
double balance[MAX_NUM];
int max_accts=0;
int num_accts;
num_accts = read_accts(acctnum,balance,max_accts);
return 0;
}
int read_accts(int acctnum[],double balance[],int max_accts)
{
ifstream infile;
infile.open("information");
while (infile >> acctnum[max_accts] && max_accts < MAX_NUM)
{
infile >> balance[max_accts];
max_accts++;
}
for (int i=0; i<=max_accts; i++)
{
cout << acctnum[i]<<endl;
cout << balance[i]<<endl;
}
infile.close();
return max_accts;
}
The output of this program is
0
0
It should be same as the text file has. it shouldn't be 0 and 0.
Thanks in advance Any help is appreciated.
First of all, you're printing one too many elements in each array. The printing loop should run from 0 to max_accts (not including), like so:
for (int i=0; i < max_accts; i++)
Now, since you're attempting to read a file named "information", I've created such file with the contents described in your question and ran your code. The output I get is:
1234
200.55
5678
1234.56
9876
2.33
This looks like what you wanted to get (and not zeroes), so your code seems to be working alright. Perhaps you're not opening the right file?
Either correct the name of the file from "information" to something else, or make sure you have a file named "information" in the current working directory.
For the file part of you program, the code below would be a better version of your function. It uses end of file and has the ability to handle of the file doesn't exits
int read_accts(int acctnum[], double balance[], int max_accts)
{
ifstream infile;
infile.open("information");
if (!infile)//check if file exists
{
//add code to handle missing file here
}
while (!infile.eof())
{
infile >> acctnum[max_accts];
infile >> balance[max_accts];
max_accts++;
}
infile.close();
return max_accts;
}
How about using the old school stdio API for a simple task like that?:
int accounts[3], i;
float balance[3];
FILE *fp = fopen("file.txt", "r");
for (i = 0; i < 3; i++)
fscanf(fp, "%d %f", &account[i], &balance[i]);
fclose(fp);

ifstream wont read all integer

When i read TestData.txt file it gives me wrong output. What am i doing wrong. I am using int array so i can do MergeSort after saving data into array.
TestData.txt
-------------------
31791 564974 477059 269094 972335
739154 206345 634644 227684 398536
910177 507975 589785 67117 395140
598829 372499 364165 450187 996527
700285 263407 918021 661467 457544
656297 846316 221731 240676 68287
913 141702 845802 477617 109824
{
int myArray[1000];
int i;
//reading givin data
const char* filename= "TestData.txt";
ifstream file(filename);
if(file.is_open())
{
for(i = 0; i <=999; ++i)
{
file >> myArray[i];//storing data to array
}
}
Need to check if you ifstream is end of file, in that case you get garbage value from out of the file bound.
With One modification, the code would be OK.
Change:
for(i = 0; i <=999; ++i)
to:
for(i = 0; i <=999 && !file.eof(); ++i)
You are reading 1000 enties from your file which contains clearly less than 1000 integers.
The first values of your array must be correct, but after you reach the end of your file the operator>> will not ready anything.
For example here is one way to write it:
const char* filename= "TestData.txt";
std::vector<int> myArray;
std::ifstream file(filename);
if(file.is_open())
{
int v;
while(file >> v) {
myArray.push_back(v);
}
}
int if I'm not wrong can keep data from -32768 to 32767.
So if u have bigger values than that (which you have, from your source file), you won't have the results you are expecting.
btw, it would be nice to know also what output you are getting.

regarding file existence check within a for loop in C++

I am attempting to check if a file exists, and then if so proceed with a task and if not to just output that there is no such file. I have done this in other code but it doesn't seem to be working with my current code.
The basics of it read:
count=argc;
for(i=0; i < count-1; i++)
{
filename[i] = argv[i+1];
}
for( i=0; i < count-1; i++)
{
int tempi=i;
ifstream infile(filename[i].c_str());
if(infile)
{
//do things
}
else
{
cout<<"no file"<<endl;
}
You need to call infile.is_open(). Also, do you plan to do something with the file if it exists, or not?
The canonical way to access argv is:
int main( int argc, char * argv[] ) {
for ( int i = 1; i < argc; i++ ) {
// do something with argv[i]
}
}
infile, in the conditional, evaluates to false when the stream is in a "bad" state.
However, merely failing to open a file does not leave the stream in a bad state (welcome to C++!). Only after attempting to read from the stream would this mechanism work for you.
Fortunately, you can use infile.is_open() to explicitly test for whether the stream was opened or not.
Edit
The above is not true.
Testing the stream state is sufficient, and I can't see anything wrong with your code.