There are a number of ways to control the read loop -- however, the control must be based upon a successful read from the file. You can either use your nested loops and read, e.g.
for (int i = 0; i < *sizeOfBoardGame; i++) {
for (int j = 0; j < *sizeOfBoardGame; j++) {
if (!(inFS.get(c)))
goto readdone;
boardOfGame[i][j] = c;
}
}
readdone:;
(note: using fixed for
loops -- you cannot account for additional characters like spaces or the terminating '\n'
. for
loops will only execute that specific number of times regardless what the character read is. Better to use while
loops and counter-variables to ensure you fill each row and column with a valid character)
Or, preferably, just control the read itself, e.g.
while (row < BRDSZ && inFS.get(c)) {
if (!isspace (c)) {
boardOfGame[row][col++] = c;
if (col == BRDSZ) {
col = 0;
row++;
}
}
}
inFS.close();
Putting it into a short example you could do:
#include <iostream>
#include <fstream>
#include <cctype>
using namespace std;
#define BRDSZ 7 /* if you need constants, #define them */
#define MAXFN 1024
void initializeBoardGame (char (*boardOfGame)[BRDSZ]);
int main (void) {
char boardOfGame[BRDSZ][BRDSZ] = {{0}};
initializeBoardGame (boardOfGame);
for (int i = 0; i < BRDSZ; i++) {
for (int j = 0; j < BRDSZ; j++)
cout << " " << boardOfGame[i][j];
cout << '\n';
}
}
void initializeBoardGame (char (*boardOfGame)[BRDSZ])
{
char nameOfFile[MAXFN],
c;
int row = 0,
col = 0;
ifstream inFS;
cout << "Please enter the name of the input file: ";
if (!(cin >> nameOfFile)) {
cerr << "error: invalid filename entry\n";
exit (EXIT_FAILURE);
}
inFS.open (nameOfFile);
if (!(inFS.is_open())) {
cerr << "error: file open failed '" << nameOfFile << "'.\n";
exit (EXIT_FAILURE);
}
while (row < BRDSZ && inFS.get(c)) {
if (!isspace (c)) {
boardOfGame[row][col++] = c;
if (col == BRDSZ) {
col = 0;
row++;
}
}
}
inFS.close();
if (row != BRDSZ) {
cerr << "error: insufficient values read from file.\n";
exit (EXIT_FAILURE);
}
}
Example Input File
$ cat dat/board_7x7.txt
1 6 7 0 4 2 1
2 1 1 2 4 4 9
5 7 9 4 1 5 1
1 6 8 1 1 4 4
2 7 0 7 0 2 9
6 5 8 3 0 0 1
2 5 6 7 4 1 3
Example Use/Output
$ ./bin/readboard
Please enter the name of the input file: dat/board_7x7.txt
1 6 7 0 4 2 1
2 1 1 2 4 4 9
5 7 9 4 1 5 1
1 6 8 1 1 4 4
2 7 0 7 0 2 9
6 5 8 3 0 0 1
2 5 6 7 4 1 3
Look things over and let me know if you need further help.
Update To Read X,X,X,X
Now that I understand you want to read 4 values and replace the ','
in the input file with an actual ' '
(space
character) in your array, you can tweak the read loop to read each character, if the character is !isspace(c)
then store the character, if c == ','
then store a space, e.g.
while (row < BRDSZ && inFS.get(c)) {
if (c == ',')
boardOfGame[row][col++] = ' ';
else if (!isspace(c))
boardOfGame[row][col++] = c;
if (col == BRDSZ) {
col = 0;
row++;
}
}
Since you are storing a space between each X
(or character value) in your array, when you want to insert a character and move the existing column values to the right, you must move the column values by 2 instead of just shifting by 1
to account for the space
that is stored before the value, e.g. (to insert the letter 'c'
as the new first value in the array at boardOfGame[0][0]
, you could do:
memmove (&boardOfGame[0][2], &boardOfGame[0][0], BRDSZ - 2);
boardOfGame[0][0] = 'c';
boardOfGame[0][1] = ' ';
Putting that altogether in an example (and printing with an additional space between columns), you could do:
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstring>
using namespace std;
#define BRDSZ 7 /* if you need constants, #define them */
#define MAXFN 1024
void initializeBoardGame (char (*boardOfGame)[BRDSZ]);
int main (void) {
char boardOfGame[BRDSZ][BRDSZ] = {{0}};
initializeBoardGame (boardOfGame);
for (int i = 0; i < BRDSZ; i++) { /* output original values */
for (int j = 0; j < BRDSZ; j++)
cout << " " << boardOfGame[i][j];
cout << '\n';
}
cout << '\n';
/* insert 'c' at boardOfGame[0][0] - shif existing right */
memmove (&boardOfGame[0][2], &boardOfGame[0][0], BRDSZ - 2);
boardOfGame[0][0] = 'c';
boardOfGame[0][1] = ' ';
for (int i = 0; i < BRDSZ; i++) { /* output with new value */
for (int j = 0; j < BRDSZ; j++)
cout << " " << boardOfGame[i][j];
cout << '\n';
}
}
void initializeBoardGame (char (*boardOfGame)[BRDSZ])
{
char nameOfFile[MAXFN],
c;
int row = 0,
col = 0;
ifstream inFS;
cout << "Please enter the name of the input file: ";
if (!(cin >> nameOfFile)) {
cerr << "error: invalid filename entry\n";
exit (EXIT_FAILURE);
}
inFS.open (nameOfFile);
if (!(inFS.is_open())) {
cerr << "error: file open failed '" << nameOfFile << "'.\n";
exit (EXIT_FAILURE);
}
while (row < BRDSZ && inFS.get(c)) {
if (c == ',')
boardOfGame[row][col++] = ' ';
else if (!isspace(c))
boardOfGame[row][col++] = c;
if (col == BRDSZ) {
col = 0;
row++;
}
}
inFS.close();
if (row != BRDSZ) {
cerr << "error: insufficient values read from file.\n";
exit (EXIT_FAILURE);
}
}
Example Input File
$ cat dat/board_7x7-2.txt
5,9,3,7
2,6,2,1
1,9,0,9
2,1,7,3
6,1,6,3
1,7,3,8
3,0,6,4
Example Use/Output
$ ./bin/readboard2
Please enter the name of the input file: dat/board_7x7-2.txt
5 9 3 7
2 6 2 1
1 9 0 9
2 1 7 3
6 1 6 3
1 7 3 8
3 0 6 4
c 5 9 3
2 6 2 1
1 9 0 9
2 1 7 3
6 1 6 3
1 7 3 8
3 0 6 4
Reading from File with Unequal Rows
If I understand now and your data file looks similar to:
Example Input File
$ cat dat/board_7x7-3.txt
X, ,X, ,X, ,X
, , , , , , ,
X, ,X, ,X, ,X
, , , , , , ,
X, ,X, ,X, ,X
, , , , , , ,
X, ,X, ,X, ,X
And, your job is to read the X
values storing them in a 2D array with spaces stored between the X
values, then you will need to pass a pointer to either int
or size_t
as a parameter to your function so that it can be updated with the number of rows actually stored (since the rows containing all commas contain no values). The approach to reading is essentially the same, accept this time you will determine when valid data is read and if it is not the 1st column, you will add a space before it in your array, e.g.
while (row < BRDSZ && inFS.get(c)) {
if ((!isspace(c) && c != ',')) { /* read store only values */
if (col) /* if not 1st, store ' ' */
boardOfGame[row][col++] = ' ';
boardOfGame[row][col++] = c;
}
if (col == BRDSZ) {
col = 0;
row++;
}
}
The complete example with the additional pointer parameter added and the number of rows filled updated within the function is:
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstring>
using namespace std;
#define BRDSZ 7 /* if you need constants, #define them */
#define MAXFN 1024
void initializeBoardGame (char (*boardOfGame)[BRDSZ], int *nrows);
int main (void) {
char boardOfGame[BRDSZ][BRDSZ] = {{0}};
int nrows = BRDSZ;
initializeBoardGame (boardOfGame, &nrows);
for (int i = 0; i < nrows; i++) { /* output original values */
for (int j = 0; j < BRDSZ; j++)
cout << boardOfGame[i][j];
cout << '\n';
}
cout << '\n';
/* insert 'c' at boardOfGame[0][0] - shif existing right */
memmove (&boardOfGame[0][2], &boardOfGame[0][0], BRDSZ - 2);
boardOfGame[0][0] = 'c';
boardOfGame[0][1] = ' ';
for (int i = 0; i < nrows; i++) { /* output with new value */
for (int j = 0; j < BRDSZ; j++)
cout << boardOfGame[i][j];
cout << '\n';
}
}
void initializeBoardGame (char (*boardOfGame)[BRDSZ], int *nrows)
{
char nameOfFile[MAXFN],
c;
int row = 0,
col = 0;
ifstream inFS;
cout << "Please enter the name of the input file: ";
if (!(cin >> nameOfFile)) {
cerr << "error: invalid filename entry\n";
exit (EXIT_FAILURE);
}
inFS.open (nameOfFile);
if (!(inFS.is_open())) {
cerr << "error: file open failed '" << nameOfFile << "'.\n";
exit (EXIT_FAILURE);
}
while (row < BRDSZ && inFS.get(c)) {
if ((!isspace(c) && c != ',')) { /* read store only values */
if (col) /* if not 1st, store ' ' */
boardOfGame[row][col++] = ' ';
boardOfGame[row][col++] = c;
}
if (col == BRDSZ) {
col = 0;
row++;
}
}
inFS.close();
if (row != BRDSZ)
*nrows = row;
}
Example Use/Output
$ ./bin/readboard3
Please enter the name of the input file: dat/board_7x7-3.txt
X X X X
X X X X
X X X X
X X X X
c X X X
X X X X
X X X X
X X X X
If this is not what you intend, the post the exact output you expect so I will know what your intent is regarding input lines that are all commas.
Last But Not Least
What this all boils down to is that your separators change between odd and even rows. This is a fairly ridiculous case, but it has educational value in helping you think through reading data with varying states of input.
When your rows are odd, your file is a comma-separated file. When you rows are even your file is a space-separated file. The simplest and most efficient test of whether an integer value (like row
in the code) is odd or even is simply to test if the 1-bit
is set (e.g. row & 1 == 0
is row is even and row & 1 == 1
row is odd)
So you simply swap which separator you are looking for based on whether your row
is odd or even, e.g.
while (row < BRDSZ && inFS.get(c)) { /* protect row bounds, read c */
if (col < BRDSZ) { /* protect col bounds */
if ((row & 1) == 0) { /* even no. row */
if (c != ',' && c != '\n') /* separator is ',' */
boardOfGame[row][col++] = c; /* assign c */
}
else { /* odd no. row */
if (!isspace(c) && c != '\n') { /* seperator is ' ' */
if (c == ',') /* if c ',' save ' ' */
boardOfGame[row][col++] = ' ';
else /* otherwise, use c */
boardOfGame[row][col++] = c;
}
}
}
if (c == '\n') { /* when newline reached */
col = 0; /* reset values, increment row */
row++;
}
}
inFS.close();
When you substitute this read into the last example, you end up with:
Example Use/Output
$ ./bin/readboard4
Please enter the name of the input file: dat/board_7x7-3.txt
X X X X
X X X X
X X X X
X X X X
c X X X
X X X X
X X X X
X X X X
Now having gone though this multiple ways, you should be picking up on the fact that reading data character by character is a simple matter of:
testing the character "What do I have?";
handling the character based on your needs, e.g. "What do I do with it?"; and
never forget to account for your line-ending characters (e.g. '\n'
) also being read as part of your read loop.
If you think of it that way, you should be able to sort it all out.