how do I read a textfile into a 2D array in c++? - c++

I am really struggling with trying to pass a comma separated integer textfile into a 2d array in c++.
example, if the textfile looks like
2,4,5,6
3,7,5,3
4,8,4,2
6,7,3,0
how will I be able to put this into a 4 by 4 array please? I will later need to do calculations on it, which I know how to once it is in the 2d array.
thank you in advance
so far I have
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
const int row =4;
const int col =4;
int array[row][col];
int r =0;
int c =0;
ifstream inputfile("numbers.txt");
if (!inputfile.is_open())
{cout<<"error"<<endl;
}
string line,num;
int number;
while(get line(inputfile,line))
{
string stream ss(line);
getline(ss,num,',');
number= stop(num);
for (int r=0; r<row;r++)
{
for (int c=0; c<col; c++)
{
array[row][col] =number;
}
}
inputfile.close();
return 0;
}

The good news is you have collected the right pieces of the puzzle to do what it is you are attempting to do. The bad news is ... you put the puzzle together wrong ... (but not terribly so).
The key is to systematically program each step you need to accomplish to read the csv file into a 2D array. (you should be using std::vector<std::vector<int>>, but I suspect the 2D array is an assignment requirement -- and it is also good to know how to handle arrays of fundamental types -- there are a lot out there in legacy code). Up through the point of reading each line from the file and populating the stringstream and then looking to parse with getline using the ',' as a delimiter everything looks okay.
What you are missing are protections to prevent reading more columns than you have storage for and more rows than you have declared. While this will not impact your read of the values from you file if your file contains exactly the number of values as you have declared for your array, it is vital for using arrays of fundamental types correctly. They are of fixed size and there is no auto-allocation that will save you if you attempt to write beyond the bounds of your array (you will just corrupt your program stack)
How do you protect your arrays bound? Simple, just check the the current row doesn't exceed the number allocated and the same for the columns, e.g.
/* read each line, protect row bounds */
while (r < row && getline (inputfile,line)) {
int c = 0; /* declare c local to loop so it is reset each iteration */
std::string num;
std::stringstream ss (line);
/* loop reading from stringstream, protect column bounds */
while (c < col && getline (ss, num, ',')) {
try { /* try/catch exception handling required for stoi conversion */
array[r][c++] = std::stoi (num); /* convert, increment col count */
}
catch (const std::exception & e) {
std::cerr << "error: invalid conversion " << e.what() << '\n';
return 1;
}
}
if (c != col) { /* validate col number of values read & stored */
std::cerr << "error: invalid number of columns '" << c << "' row: "
<< r << '\n';
return 1;
}
r++; /* increment row count */
}
Also note the use of try/catch exception handling. When using std::stoi that is the only manner you have to validate the conversion, see cppreference.com - std::stoi and note the Exception heading detailing the two exception (std::invalid_argument and std::out_of_range) that must be handled. You cannot simply guess that all input files will be in the needed format with the correct values and hope for the best -- you must validate every input.
This applies to the number of columns in each row as well (and the number of rows filled when your read-loop is done). When you are done reading from the stringstring, you need to validate the number of column-values read is the number expected and that you have not encountered a short row. Note: these are the minimum-validations needed. You are free to write additional validations such as to check to ensure the stringstring is empty and that additional valued do not remain unread (not critical to the operation of the code, but may flag an invalidly formatted input file)
Lastly, while the remainder of the code simply outputs the values, take a look at Why is “using namespace std;” considered bad practice?. Develop good habits early. That also means Don't Hardcode Filenames. The arguments to main() provide a way to pass needed information into your program on startup -- use them, or at minimum, prompt for the filename to open.
Further, always compile with warnings enabled. That means -Wall -Wextra -pedantic -Wshadow for gcc/clang and for VS /W3. Including -Wshadow you would find that you shadow the previous declaration for variable c in for (int c=0; c<col; c++)
Putting it altogether, you could do something similar to:
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#define ROW 4 /* while const int is fine, if you #define your constants */
#define COL ROW /* you have one single location at the top to make changes */
int main (int argc, char **argv) {
const int row = ROW, col = COL; /* optional, you can just use ROW & COL */
int array[row][col], r = 0;
std::string line;
if (argc < 2) { /* validate at least 1 argument given for filename */
std::cerr << "error: insufficient arguments\nusage: ./prog filename\n";
return 1;
}
std::ifstream inputfile (argv[1]); /* open file provided as 1st argument */
if (!inputfile.is_open()) { /* use std::cerr for error output, handle error */
std::cerr << "error: file open failed '" << argv[1] << "'.\n";
return 1;
}
/* read each line, protect row bounds */
while (r < row && getline (inputfile,line)) {
int c = 0; /* declare c local to loop so it is reset each iteration */
std::string num;
std::stringstream ss (line);
/* loop reading from stringstream, protect column bounds */
while (c < col && getline (ss, num, ',')) {
try { /* try/catch exception handling required for stoi conversion */
array[r][c++] = std::stoi (num); /* convert, increment col count */
}
catch (const std::exception & e) {
std::cerr << "error: invalid conversion " << e.what() << '\n';
return 1;
}
}
if (c != col) { /* validate col number of values read & stored */
std::cerr << "error: invalid number of columns '" << c << "' row: "
<< r << '\n';
return 1;
}
r++; /* increment row count */
}
if (r < row) { /* validate row number of arrays stored in 2D array */
std::cerr << "error: invalid number of rows '" << r << "'\n";
return 1;
}
for (r = 0; r < row; r++) { /* loop outputting results */
for (int c = 0; c < col; c++)
std::cout << " " << array[r][c];
std::cout << '\n';
}
}
(note: the #define statements are optional, but provide a convenient place at the top of your code to adjust your constants if the need arises)
Example Use/Output
While your input file in dat/2darr.csv, you would execute and receive the following output:
$ ./bin/read2darrcsv dat/2darr.csv
2 4 5 6
3 7 5 3
4 8 4 2
6 7 3 0
Let me know if you have further questions.

Related

Sum of Multidimensional array in C++ from csv file

Would anyone be able to help me, I am trying to make a C++ program which reads values from csv file and prints them(there are going to be three 4 rows, 3 columns). I want to know how can I add the rows (like the sum of the first row =? the sum of second-row = ?...)
The matrix looks like this:
And my program looks like:
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream sumCol;
sumCol.open("Col.csv");
string line;
int sum;
cout << "The entered matrix: " << endl;
while(sumCol.good()){
string line;
getline(sumCol, line, ',');
cout << line << " ";
}
while(sumCol.good()){
getline(sumCol, line, ',');
int i = stoi(line);
cout << endl;
cout << i;
}
while(getline(sumCol, line, ',')){
int i = stoi(line);
sum = sum + i;
getline(sumCol, line, ',');
i = stoi(line);
sum = sum + i;
getline(sumCol, line);
i = stoi(line);
sum = sum + i;
}
cout << sum << endl;
return 0;
}
Next try. Very simple answer with just basic constructs.
12 statements. But 2 for loops, nested and hardcoded magic numbers for rows and columns.
Please see the below well commented source code:
#include <iostream>
#include <fstream>
int main() {
// Open file
std::ifstream csv("col.csv");
// and check, if it could be opened
if (csv) {
// Now the file is open. We have 3 rows and 4 columns
// We will use 2 nested for loops, one for the rows and one for the columns
// So, first the rows
for (int row = 0; row < 3; ++row) {
// Every row has a sum
int sumForOneRow = 0;
// And every row has 4 columns. Go through all coulmns of the current row.
for (int col = 0; col < 4; ++col) {
// Read an integer value from the current line
int integerValue;
csv >> integerValue;
// Show it on the screen
std::cout << integerValue << ' ';
// Update the sum of the row
sumForOneRow = sumForOneRow + integerValue;
}
// Now, the inner for loop for the 4 columns is done. Show sum to user
std::cout << " --> " << sumForOneRow << '\n';
// Line activities are done now for this line. Go on with next line
}
}
else std::cerr << "\n*** Error: Could not open 'col.csv'\n";
return 0;
}
Third try.
Obviously the matrix, shown in the question, does not reflect the real data. The real data might look like that:
9, 1, 2, 4
9, 2, 8, 0
3, 3, 3, 3
Without using std::getline we can do like the below:
#include <iostream>
#include <fstream>
int main() {
// Open file
std::ifstream csv("r:\\col.csv");
// and check, if it could be opened
if (csv) {
// Now the file is open. We have 3 rows and 4 columns
// We will use 2 nested for loops, one for the rows and one for the columns
// So, first the rows
for (int row = 0; row < 3; ++row) {
// Every row has a sum
int sumForOneRow = 0;
// And every row has 4 columns. Go through all coulmns of the current row.
for (int col = 0; col < 4; ++col) {
// Read an integer value from the current line
int integerValue;
char c; // for the comma
// The last value, the value in column 3 (We start counting with 0) is not followed by a comma
// Therefore we add special treatment for the last column
if (col == 3)
csv >> integerValue; // Read just the value
else
csv >> integerValue >> c; // Read value and comma
// Show it on the screen
std::cout << integerValue << ' ';
// Update the sum of the row
sumForOneRow = sumForOneRow + integerValue;
}
// Now, the inner for loop for the 4 columns is done. Show sum to user
std::cout << " --> " << sumForOneRow << '\n';
// Line activities are done now for this line. Go on with next line
}
}
else std::cerr << "\n*** Error: Could not open 'col.csv'\n";
return 0;
}
4th try. Based on the evolution of this thread and the request of the OP to use std::getline
#include <iostream>
#include <fstream>
#include <string>
int main() {
// Open file
std::ifstream csv("r:\\col.csv");
// and check, if it could be opened
if (csv) {
// Now the file is open. We have 3 rows and 4 columns
// We will use 2 nested for loops, one for the rows and one for the columns
// So, first the rows
for (int row = 0; row < 3; ++row) {
// Every row has a sum
int sumForOneRow = 0;
// And every row has 4 columns. Go through all coulmns of the current row.
for (int col = 0; col < 4; ++col) {
// Read a substring up to the next comma or end of line
std::string line;
// Special handling for last column. This is not followed by a comma
if (col == 3)
std::getline(csv, line);
else
std::getline(csv, line, ',');
// Convert string to line
int integerValue = std::stoi(line);
// Show it on the screen
std::cout << integerValue << ' ';
// Update the sum of the row
sumForOneRow = sumForOneRow + integerValue;
}
// Now, the inner for loop for the 4 columns is done. Show sum to user
std::cout << " --> " << sumForOneRow << '\n';
// Line activities are done now for this line. Go on with next line
}
}
else std::cerr << "\n*** Error: Could not open 'col.csv'\n";
return 0;
}
IMHO this is more complicated than 3rd try
Yes, I can help you with a piece of code, but I am not sure, if you will understand the more modern C++ language elements.
So, what do we need to do?
Read line by line of the source csv string
Get all integer values for this line
The interger values are arranged in columns and separated by white space
Show the values and the screen
Sum up the values in onbe line and show the sum
And in the resulting code, we will do exactly that.
So, first, we open the file, and check, it it could be opened. Then, in a simple for loop, we read all lines that are present in the source code. Line by line.
In the body of the for loop, we take the current line that we just read, and put it into an std::istringstream. We do that to make extraction of the integer value simpler. And for the extraction, we use the std::istream_iterator. This will iterate over all integers in the line and return them.
We will store the values temporary in a std::vector. We use the range constructor of the std::vector. The begin iterator is the std::istream_iterator for the data type int and and for our std::istringstream. The end iterator is the default constructed std::istream_iterator, so, simply {}. This will copy all values into the std::vector
That is a powerful and compact on-liner.
We copy the values in the std::vector, so, the integer values of one line to the console.
Then, we add up all values in the std::vector (all integers from one line) and show the result on the screen.
And the beauty of it: It doesn't matter, how many rows and columns are present. It will work always. The only thing we need is space separated integer values in lines.
And all this can be done with just 7 statements . . .
Please see:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <numeric>
int main() {
// Open file and check, if it could be opened
if (std::ifstream csv{ "col.csv" }; csv) {
// Read all lines from the source file
for (std::string line{}; std::getline(csv, line); ) {
// Put the current line in a stringstream for better extraction
std::istringstream iss{ line };
// Exctract all integer values from this line and put them int a vector
std::vector values(std::istream_iterator<int>(iss), {});
// Show values on display
std::copy(values.begin(), values.end(), std::ostream_iterator<int>(std::cout, " "));
// Claculate the sum for one line on show on display.
std::cout << " --> " << std::accumulate(values.begin(), values.end(), 0) << '\n';
}
}
else std::cerr << "\n*** Error: Could not open 'col.csv'\n";
return 0;
}
Language is C++ 17

2D vector array gets filled with zeros instead of double values [duplicate]

This question already has answers here:
vector string push_back is not working in c++
(2 answers)
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
(5 answers)
Closed 3 years ago.
I am writing a program which reads information about molecule from an input file (charge, multiplicity, atom types and their coordinates) but for some reason a 2D vector-array gets filled with zeroes and finds a non-existing atom which coordinates are also zeroes. I don't know how to debug it properly so I can't track a problem.
#include <iostream>
#include <cstdlib>
#include <string>
#include <fstream>
#include <cassert>
#include <vector>
int main(int argc, char* argv[])
{
if (argc > 1)
{
std::cout << "The input file is: " << argv[1] << std::endl;
} else
{
std::cout << "No arguments" << std::endl;
return 1;
}
const char *filename = argv[1];
std::ifstream inputfile(filename);
assert(inputfile.good());
int charge, multiplicity;
inputfile >> charge >> multiplicity;
std::cout << charge << " " << multiplicity << std::endl;
int natom;
std::vector<std::string> atomSymbol;
std::vector< std::vector<double> > position;
for (int i = 0; !inputfile.eof(); i++) {
std::string bufferAtomicSymbol;
double bufferPositionX;
double bufferPositionY;
double bufferPositionZ;
std::vector<double> bufferPosition(3);
inputfile >> bufferAtomicSymbol >> bufferPositionX >> bufferPositionY >> bufferPositionZ;
atomSymbol.push_back(bufferAtomicSymbol);
bufferPosition.push_back(bufferPositionX);
bufferPosition.push_back(bufferPositionY);
bufferPosition.push_back(bufferPositionZ);
position.push_back(bufferPosition);
}
inputfile.close();
natom = position.size();
std::cout << "There are " << natom << " atoms" << std::endl;
for (int i = 0; i < natom; i++)
std::cout << atomSymbol[i] << " " << position[i][0] << " " << position[i][1] << " " << position[i][2] << std::endl;
return 0;
}
Input file sample:
0 1
C 1.11988 -0.11356 -0.04893
C -0.22149 0.53742 0.15390
N -1.36703 -0.23693 -0.04570
O -0.39583 1.70537 0.48392
H 1.93813 0.59458 0.13709
H 1.23188 -0.48457 -1.07645
H 1.25795 -0.96373 0.63239
H -2.27205 0.14808 0.07622
H -1.29145 -1.18667 -0.31244
Output of program:
The input file is: acetamide.xyz
0 1
There are 10 atoms
C 0 0 0
C 0 0 0
N 0 0 0
O 0 0 0
H 0 0 0
H 0 0 0
H 0 0 0
H 0 0 0
H 0 0 0
0 0 0
You have two primary problems that are plaguing your attempt to successfully read from your data file, one is specific, and the other more general, but both equally fatal to the attempt.
The technical problem with your read can be solved by reviewing Why !.eof() inside a loop condition is always wrong.. The equally important more general problem you are facings is the sprawling list of vectors, strings, and doubles you are attempting to duct-tape and bailing-wire together. When you overly complicate your data handling using superfluous or ill-fitting containers, trying to make them work together is like trying to put toothpaste back into the tube -- it ain't gonna work....
Take a bit of time and sort out what it is you need to store for each chunk of data, and then create a data structure that holds that. It's fine to use a few temporary variables to facilitate reading the data, but the final storage for your data should be straight-forward and as simple as allowable.
In your case you are storing the atomic-symbol as a string and three double values for the position. You base data structure should be able to capture all three as a single object. While there are a couple of options, the plain-old struct for coordinating between the differing types of data will work fine. Then you simply create a vector of struct as your final storage solution to allow access to your data.
In keeping with the simple-as-required, you can use a struct containing a string and three double values. For example:
struct atom {
std::string sym;
double x, y, z;
};
That is all that is needed to capture symbol and positional coordinates. The you simply declare a vector of atom as your final storage solution, e.g.
std::vector<atom> atoms; /* your final storage container */
A running theme throughout your code that will invite Undefined Behavior is failing to validate EVERY input. If you simply read from a stream without validating whether the read succeeded or failed, you are simply playing Russian-Roulette. If your read fails, and you just blindly continue forward using the uninitialized variable you assume was properly filled with data, game-over.
So validate every read. For example, reading the charge and multiplicity, you can do:
if (!(fs >> charge >> multiplicity)) { /* validate EVERY input */
std::cerr << "error: invalid format: charge, multiplicity\n";
return 1;
}
(note: I've shortened your variable names, e.g. inputfile is now fs, typing is not a strong-suit)
For reading each atom from every subsequent line in the file, you can do:
/* read each remaining line until you run out */
while (fs >> atmSymbol >> bposX >> bposY >> bposZ) {
/* add the values read to your temporary struct */
atom tmp = { atmSymbol, bposX, bposY, bposZ };
atoms.push_back(tmp); /* push tmp struct onto storage vector */
}
The key is to validate the success or failure of every read so you know you are processing valid data in your code.
Putting the rest of it together in a short example to read your data file, you could do something like the following:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <array>
#include <string>
#include <vector>
struct atom {
std::string sym;
double x, y, z;
};
int main (int argc, char *argv[]) {
if (argc < 2) { /* validate at least one argument given for filename */
std::cout << "error: insuffient input.\n"
"usage: " << argv[0] << " <fn>\n";
return 1;
}
std::cout << "The input file is: " << argv[1] << '\n';
std::ifstream fs (argv[1]); /* file stream, just use argv[1] */
int charge, multiplicity, natom; /* temporary variables for filling */
double bposX, bposY, bposZ;
std::string atmSymbol;
std::vector<atom> atoms; /* your final storage container */
if (!(fs >> charge >> multiplicity)) { /* validate EVERY input */
std::cerr << "error: invalid format: charge, multiplicity\n";
return 1;
}
/* read each remaining line until you run out */
while (fs >> atmSymbol >> bposX >> bposY >> bposZ) {
/* add the values read to your temporary struct */
atom tmp = { atmSymbol, bposX, bposY, bposZ };
atoms.push_back(tmp); /* push tmp struct onto storage vector */
}
fs.close(); /* close stream -- you are done reading */
natom = atoms.size(); /* get an output size */
std::cout << "\nThere are " << natom << " atoms.\n\n";
for (auto& a : atoms) { /* loop over each atom in vector */
std::cout << a.sym /* output atomic symbol */
<< " " << std::setw(8) << a.x /* each coordinate, and */
<< " " << std::setw(8) << a.y
<< " " << std::setw(8) << a.z << '\n';/* tidy up with \n */
}
}
Example Use/Output
$ ./bin/atoms_read dat/atoms.txt
The input file is: dat/atoms.txt
There are 9 atoms.
C 1.11988 -0.11356 -0.04893
C -0.22149 0.53742 0.1539
N -1.36703 -0.23693 -0.0457
O -0.39583 1.70537 0.48392
H 1.93813 0.59458 0.13709
H 1.23188 -0.48457 -1.07645
H 1.25795 -0.96373 0.63239
H -2.27205 0.14808 0.07622
H -1.29145 -1.18667 -0.31244
Look things over and let me know if you have further questions.
Update Based on Request To Handle Empty-Lines in File
If you have additional blank-line separated blocks of atoms to read in your datafile, all you need to do is rearrange your read slightly to use getline to read a line at a time from the file. You then create a stringstream from the line and read from the stringstream just as we originally read from the file. If you can validly read into your atomic-symbol and positional coordinates from the stringstream, you have a valid line.
A quick edit to read with getline and removing the temporary variables that are no longer needed (we can read directly into the temporary struct now), you could do:
std::ifstream fs (argv[1]); /* file stream, just use argv[1] */
int charge, multiplicity, natom; /* temporary variables for filling */
std::string line;
std::vector<atom> atoms; /* your final storage container */
if (!(fs >> charge >> multiplicity)) { /* validate EVERY input */
std::cerr << "error: invalid format: charge, multiplicity\n";
return 1;
}
/* read each remaining line until you run out with getline */
while (getline (fs, line)) {
std::stringstream ss (line); /* create stringstream from line */
atom tmp; /* declare temporary struct */
/* read from stringstream into temporary struct */
if (ss >> tmp.sym >> tmp.x >> tmp.y >> tmp.z)
atoms.push_back(tmp); /* push_back atmp struct on success */
}
fs.close(); /* close stream -- you are done reading */
Now, beginning with the second line, the code will read all atom data that matches your line format into your atoms vector regardless of blank or other non-conforming lines in your file.

Using fscanf to read from tabbed file with ints and floats in C++

I have looked for a day or so on StackOverflow and other sites, and I can't find a solution to my problem. There are some that are similar, but I can't seem to make them work.
I have a tab-delimited .txt file. One line contains a heading, and 500 lines after that each contain an integer, an integer, a float, an integer, and an integer, in that order. I have a function that is supposed to read the first and third values (the first integer and the float) from each line. It skips the first line. This is in a do-while loop, because I need to be able to process files of different lengths. However, it's getting stuck in the loop. I have it set to output the mean, but it just outputs zeros forever.
void HISTS::readMeans(int rnum) {
int r;
char skip[500];
int index = 0; int area = 0; double mean = 0; int min = 0; int max = 0;
FILE *datafile = fopen(fileName,"r");
if(datafile == NULL) cout << "No such file!\n";
else {
//ignore the first line of the file
r = fscanf(datafile,"%s\n",skip);
cout << skip << endl; //just to check
//this is the problematic code
do {
r = fscanf(datafile,"%d\t%d\t%f\t%d\t%d\n",&index,&area,&mean,&min,&max);
cout << mean << " ";
} while(feof(datafile) != 1)
}
fclose(datafile);
}
Here is a sample data file of the format I'm trying to read:
Area Mean Min Max
1 262144 202.448 160 687
2 262144 201.586 155 646
3 262144 201.803 156 771
Thanks!
Edit: I said I need to read the first and third value, and I know I'm reading all of them. Eventually I need to store the first and third value, but I cut that part for the sake of brevity. Not that this comment is brief.
You should do it C++ style,
#include <iostream>
#include <fstream>
int main() {
std::ifstream inf("file.txt");
if (!inf) { exit(1); }
int idx, area, min, max;
double mean;
while (inf >> idx >> area >> mean >> min >> max) {
if (inf.eof()) break;
std::cout << idx << " " << area << " " << mean << " " << min << " " << max << std::endl;
}
return 0;
}
It is :
1) Easy to read.
2) Less code, so less chance of error.
3) Correct handling of EOF.
Although I have left handling of first line, that is upto you.
fscanf returns the number of arguments read. Thus, if it returns less than 5 you should exit the loop.
OP ended up using operator>>, which is the correct way to do this in C++. However, for the interested C reader, there were a couple of issues in the code posted:
mean was declared as double but read using the wrong format specifier %f instead of %lf.
The first line wasn't completely read, but only the first token, Area.
A possible way to implement the desired task is as follows:
r = fscanf(datafile,"%[^\n]\n",skip);
// ^^^^^ read till newline
while ( (r = fscanf(datafile,"%d%d%lf%d%d",&index,&area,&mean,&min,&max)) == 5 ) {
// ^^ correct format specifier for double
// ...
}

Converting std::vector to char*

I am a c++ student working on a project to receive and encode a message with a simple cipher. To do so, the program accepts each word as a string, converts the string into a vector of characters, and then modifies each character before storing it in a file. In the line that returns the encoded message to the main function, I get this error message: < cannot convert std::vector<char, std::allocator<char> >' tochar*' for argument 1' tochar* cipher(char*, int)' . Despite this error, the program will run, however, it will simply stop after a word has been entered and end the program. This is the code I am using:
// Cipher.cpp
// This program accepts a message and encodes it
// Written by Will Grindle
#include<iostream.h>
#include<string.h>
#include<iomanip.h>
#include<math.h>
#include<fstream.h>
#include<vector.h>
using namespace std;
string get_message(); // function prototypes
char* cipher(char broken[], int length);
int main()
{
int index, length; // declare index for vectors
string message; // declare string
ofstream outfile; // declare filestream
outfile.open("MESSAGE.DAT", ios::in); // create file and open it
message = get_message(); // use function to retrieve data
length = message.length(); // find length of message
// declare vector for breaking the strings down
vector <char> broken(message.begin(), message.end());
vector <char> encoded(50); // declare vector for encoded message
encoded[index] = cipher(broken, length); // initialize encoded to new message
for(index = 0; index <= length - 1; index++)// loop for displaying values
{
cout << encoded[index] << endl;
}
if(outfile) // if there is no error with the file
{
for(index = 0; index <= length - 1; index++) // loop for writing encoded
{ // message to file
outfile << encoded[index] << endl;
cout << "Data written to file." << endl;
}
}
else
{
cout << "Error opening file." << endl;
}
outfile.close(); // close file
system("pause");
return 0;
}
string get_message()
{
string entry; // declare string
cout << "Please enter the word to be entered." << endl; // request entry
cin >> entry; // user enters word
system ("pause");
return entry;
}
char* cipher(char broken[], int length)
{
char index; // declare index
if( broken[index] < 123 && broken[index] > 96 ) // if characters are lowercase,
{ // make them uppercase
broken = broken - 32;
}
for(index = 0; index <= length - 1; index ++)
{
broken[index] = broken[index] * (2/3);
broken[index] = broken[index] - 12;
broken[index] = broken[index] ^ 2;
}
cout << "Message encoded." << endl;
system ("pause");
return(broken);
}
I am open to any suggestions on how to make this work. Thank you!
In C++, a vector isn't actually an array of chars, so you can't pass it into a function expecting a char array. You have a few options here.
First, I'd recommend updating your code so that the cipher function takes in as a parameter either a vector or a std::string. These objects are safer than raw arrays because they know their sizes and can do reallocations if you need them to grow.
Second, you could change the call to cipher to pass in a pointer to the vector's underlying array, like this:
cipher(broken.data(), broken.size());
I think this is a less elegant and more error-prone solution, but you're welcome to use it if you'd like.
Your code looks more like C...
Maybe start learning the basic data types and data structures first.
Then make sure you understand the usage of the vector template
This seems good as well: Differences between C and C++
It also points out some things about style and what you could do, but shouldn't do.
And of course this Programmers.StackExChange Post: What are the fundamental differences between C and C++?
Try to use a modern C++ IDE such as CodeBlocks, Eclipse, Netbeans, ...;
This will help you to prevent some faults at the beginning.

Crash in a C++ tutorial program (reading matrix)

I am new to using C++ and Microsoft Visual Studio and I trying to convert a data file (2 columns by 500 rows consisting of floats) into an array, and then I am trying to output the array on screen. Whenever I try to run it, it comes up with "Unhandled exception at 0x001BC864 in file.exe: 0xC0000005: Access violation writing location 0x00320A20."
I found this video and tried to adapt that code https://www.youtube.com/watch?v=4nz6rPzVm70
Any help would be appreciated.
#include<stdio.h>
#include<string>
#include<iostream> //
#include<fstream> //
#include<array> //
#include<iomanip> //
#include<sstream>//
#include<conio.h>
using namespace std;
int rowA = 0; //
int colA = 0;
int main()
{
string lineA;
int x;
float arrayA[2][500] = { { 0 } };
ifstream myfile("C:/results.dat");
if (myfile.fail()) {
cerr << "File you are trying to access cannot be found or opened";
exit(1);
}
while (myfile.good()) {
while (getline(myfile, lineA)) {
istringstream streamA(lineA);
colA = 0;
while (streamA >> x) {
arrayA[rowA][colA] = x;
colA++; }
rowA++; }
}
cout << "# of Rows --->" << rowA << endl;
cout << "# of Columns --->" << colA << endl;
cout << " " << endl;
for (int i = 0; i < rowA; i++) {
for (int j = 0; j < colA; j++) {
cout << left << setw(6) << arrayA[i][j] << " ";
}
cout << endl;
}
return 0;
_getch();
}
Obviously your access to the array is out of bounds as you have your array indices go beyond the size of the array dimensions.
Given that this will not be the last time you run into this kind of problems, my answer will give you tips on how to detect if stuff like that goes wrong.
Your first tool on the rack is to add assertions to your code which make the problem evident in a debug build of your code.
#include <cassert>
// ...
while (myfile.good()) {
while (getline(myfile, lineA)) {
istringstream streamA(lineA);
colA = 0;
while (streamA >> x) {
// probably you would have noticed the error below while writing
// those assertions as obviously you would notice that you want 500
// rows and not 2 and you want 2 columns, not 500...
assert( rowA < 2 ); // <<--- This will assert!
assert( colA < 500 ); // <<--- This will assert if there are more than 500 lines in the file.
arrayA[rowA][colA] = x;
colA++; }
rowA++; }
With only those 2 extra lines (and the include), you would have been able to see where your code messes up.
In this case, fixing your code is quite easy and I leave it to you as an exercise ;)
In order to avoid mixing up the indices for your multi-dimensional array, you could write your code in a more suggestive manner (more readable).
int main()
{
string lineA;
int x;
const int NUM_COLUMNS = 2;
const int NUM_ROWS = 500;
float arrayA[NUM_COLUMNS][NUM_ROWS] = { { 0 } };
// ...
This tiny bit of extra expressiveness increases your odds to notice that your array access further below uses the wrong index variables per array dimension.
Last not least, you should add extra checks given that your program only works correctly (after fixing it) if the input file does not violate your assumptions (2 columns, less than 501 rows). This falls under the chapter of "defensive programming" - i.e. your code protects itself from violations of assumptions outside its scope of control.
You repeat your error in the print-loop at the bottom, btw. There, too you could add assertions.