i'm struggling to find a way to this and can't seem to find any solution anywhere, but I imagine it has to be possible.
I would like to write to a file, column by column instead of row by row.I have an arrays of strings which get updated as my program is running. The strings look like this "--4---" , "1-----", "--15---", "----6-" etc.
These need to be written to a file aligned column by column, from left to right. eg. :
--1-------
----------
4----15---
----------
-------6--
----------
I am using ofstream. I could wait until a certain number of strings are updated, then print them row by row to the file, but it gets messy when I have numbers with two digits , as have to correct offsets in advance (each array index must be aligned) etc.
Thanks for any help
A better method is to model the file in memory, then write the memory to the file.
If your text representation has 5 columns, I recommend using a matrix with 6 columns and let the 6th column be a newline.
#define MAX_ROWS 4
#define MAX_COLUMNS (5+1)
char board[MAX_ROWS][MAX_COLUMNS];
// Initialize the board
for (size_t row = 0; row < MAX_ROWS; ++row)
{
for (size_t column = 0; column < MAX_COLUMNS - 1; ++column)
{
board[row][column] = '-';
}
board[row][column] = '\n';
}
board[MAX_ROWS-1][MAX_COLUMNS-1] = '\0'; // Add terminating NULL.
You can then print the board by:
cout << (char *)(&board[0][0]) << endl;
Usually, writing to memory is a lot faster than writing to a file. Also, you can output the board in any format, such as CSV or XML.
Related
I'm trying to save numbers from first txt file to second one in reversed order.
To be clear, inside 1st txt I have typed numbers from 1 to 10 (decimal notation). When I try to count them, I get 5 or 7, depending on what's between them (space or enter).
Then, another error is that inside 2nd txt program saves as much "0s" as dl's variable value is equal to instead of loaded numbers in reversed order.
I paste the whole code, because I don't know file operation rules good enough to determine which exact part could be the source of problem. Thank You in advance.
#include <fstream>
#include <iostream>
using namespace std;
int main() {
fstream plik1;
plik1.open("L8_F3_Z2a.txt", ios::in | ios::binary);
fstream plik2;
plik2.open("L8_F3_Z2b.txt", ios::out);
if(!plik1.good() || !plik2.good()) {
cout << "file(s) invalid" << endl;
return 1;
}
plik1.seekg(0, ios::end);
int dl = plik1.tellg() / sizeof(int);
cout << "length = " << dl << endl;
int a;
for(int i = 0; i < dl; i++) {
plik1.seekg((i + 1) * sizeof(int), ios::end);
plik1 >> a;
plik2 << a;
cout << i + 1 << ". a = " << a << endl;
}
plik1.close();
plik2.close();
return 0;
}
edit the output is:
length = 7
1. a = 0
2. a = 0
3. a = 0
4. a = 0
5. a = 0
6. a = 0
7. a = 0
--------------------------------
Process exited after 0.03841 seconds with return value 0
Press any key to continue . . .
Problem
When a file is encoded as text the binary size of the data is irrelevant.
int dl = plik1.tellg() / sizeof(int);
will get you the side of the file in integers, but the file isn't storing integers. It is storing a stream of characters. Say for example the file holds one number:
12345
which is five characters long. Assuming the file is using good ol ASCII, that's 5 bytes. When 12345 is converted to an int it will probably be 4 or 8 bytes and almost certainly not 5 bytes. Assuming the common 32 bit (4 byte) int
int dl = plik1.tellg() / sizeof(int);
int dl = 5 / 4;
int dl = 1;
Yay! It worked! But only by the grace of whatever deity or cosmic entity you worship. Or don't worship. I'm not going to judge. To show why you can't count on this, lets look at
123
this is three characters and 3 bytes, so
int dl = plik1.tellg() / sizeof(int);
int dl = 3 / 4;
int dl = 0;
Whoops.
Similarly
1 2 3 4 5
is five numbers. The file length will probably be the sum of one byte per digit and one byte per space, 9 bytes.
Where this gets weird is some systems, looking at you Windows, use a two character end of line marker, carriage return and a line feed. This means
1
2
3
4
5
will sum up to 13 bytes.
This is why you see a different size depending on whether the numbers are separated with spaces or newlines.
Solution
The only way to find out how many numbers are in the file is to read the file, convert the contents to numbers, and count the numbers as you find them.
How to do that:
int num;
int count = 0;
while (plik1 >> num) // read numbers until we can't read any more
{
count++;
}
From this you can determine the size of the array you need. Then you rewind the file, seek back to the beginning, allocate the array and read the file AGAIN into the array. This is dumb. File IO is painfully slow. You don't want to do it twice. You want to read the file once and store as you go without caring how many numbers are in the file.
Fortunately there are a number of tools built into C++ that do exactly that. I like std::vector
std::vector<int> nums;
int num;
while (plik1 >> num)
{
nums.push_back(num);
}
vector even keeps count for you.
Next you could
std::reverse(nums.begin(), nums.end());
and write the result back out.
for (int num: nums)
{
plik2 << num << ' ';
}
Documentation for std::reverse
If your instructor has a no vector policy, and unfortunately many do, your best bet is to write your own simple version of vector. There are many examples of how to do this already on Stack Overflow.
Addendum
In binary 5 integers will likely be 20 or 40 bytes no matter how many digits are used and no separators are required.
It sounds like storing data as binary is the bees knees, right? Like it's going to be much easier.
But it's not. Different computers and different compilers use different sizes for integers. All you are guaranteed is an int is at least 2 bytes and no larger than a long. All of the integer types could be exactly the same size at 64 bits. Blah. Worse, not all computers store integers in the same order. Because it's easier to do some operations if the number is stored backwards, guess what? Often the number is stored backwards. You have to be very, very careful with binary data and establish a data protocol (search term for more on this topic: Serialization) that defines the how the data is to be interpreted by everyone.
I am trying to open a text file and pass the lines of the text file to a vector. The first digit in each line is the size of the vector and since I do not know the end point of the text file I am using a while loop to find the end. The idea is that I can take a text file and run a merge sort on it. So, for example:
3 5 4 9
5 0 2 6 8 1
sorted it would be become:
4 5 9
0 1 2 6 8
The problem I am having is that when I sort a vector that is larger than the prior vector (as in the example) I do not get output. It is probably something simple that I just have over looked. I am pretty sure the issue is in the code below. Thanks for any pointers.
while (!file.eof())
{
int size;
file >> size;
vector<int> myVector(size);
int n = 0;
while (n < size && file >> myVector[n])
{
++n;
}
sort(myVector);
for (int j = 0; j < size; ++j)
{
if (file.eof()) break;
cout << myVector[j] << ' ';
}
cout << '\n';
}
The problem is this line:
if (file.eof()) break;
Once you've read the last line of the file, file.eof() will be true. So the first time through the loop that's supposed to print the sorted vector, you break out of the loop and don't print anything. It has nothing do with whether the vector is larger than the previous vector, it's just a problem with the last line of the file. The fix is to get rid of that unnecessary line.
You also need to change the main loop. while (!file.eof()) is the wrong way to loop over a file's contents (see the linked questions for full explanations). Use:
int size;
while (file >> size) {
...
}
because of the line :
if(file.eof()) break;
if you get to eof your program wont print anything since you break the printing loop on its first iteration
for instance - if there are no chars after 8 in your example - you wont get output ,but even a single space can change that
besides that - is there any chance or cases that your sorting function clears a vector ? or changes it ?
I am creating a command-line minesweeper game which has a save and continue capability. My code generates a file called "save.txt" which stores the position of the mines and the cells that the player has opened. It is separated into two columns delimited by a space where the left column represents the row of the cell and the right column represents the column of the cell in the matrix generated by my code. The following is the contents of save.txt after a sample run:
3 7
3 9
5 7
6 7
8 4
Mine end
2 9
1 10
3 5
1 1
Cell open end
You may have noticed Mine end and Cell open end. These two basically separate the numbers into two groups where the first one is for the position of the mines and the latter is for the position of the cells opened by the player. I have created a code which generates an array for each column provided that the text file contains integers:
int arrayRow[9];
int arrayCol[9];
ifstream infile("save.txt");
int a, b;
while(infile >> a >> b){
for(int i = 0; i < 9; i++){
arrayRow[i] = a;
arrayCol[i] = b;
}
}
As you can see, this won't quite work with my text file since it contains non-integer text. Basically, I want to create four arrays: mineRow, mineCol, openedRow, and openedCol as per described by the first paragraph.
Aside from parsing the string yourself and doing string operations, you can probably redefine the file format to have a header. Then you can parse the once and keep everything in numbers. Namely:
Let the Header be the first two rows
Row 1 = mineRowLen mineColLen
Row 2 = openedRowLen openedColLen
Row 3...N = data
save.txt:
40 30
20 10
// rest of the entries
Then you just read 40 for the mineRow, 30 for mineCol, 20 for openedRow, 10 for openedCol since you know their lengths. This will be potentially harder to debug, but would allow you to hide the save state better to disallow easy modification of it.
You can read the file line by line.
If the line matches "Mine end" or "Cell open end", continue;
Else, split the line by space (" "), and fill the array.
I am trying to write data from a 2D array into a binary file. I am only writing the data that has a value greater than 0. Therefore, if the data is is 0, it will not be written to the file. The data is as follows:
Level 0 1 2 3 4 5
Row 0 4 3 1 0 2 4
Row 1 0 2 4 5 0 0
Row 2 3 2 1 5 2 0
Row 3 1 3 0 1 2 0
void {
// This is what i have for writing to file.
ofstream outBinFile;
ifstream inBinFile;
int row;
int column;
outBinFile.open("BINFILE.BIN", ios::out | ios::binary);
for (row = 0; row < MAX_ROW; row++){
for (column = 0; column < MAX_LEVEL; column++){
if (Array[row][column] != 0){
outBinFile.write (reinterpret_cast<char*> (&Array[row][column]), sizeof(int)
}
}
}
outBinFile.close();
// Reading to file.
inBinFile.open("BINFILE.BIN", ios::in | ios::binary);
for (row = 0; row < MAX_ROW; row++){
for (column = 0; column < MAX_LEVEL; column++){
if (Array[row][column] != 0){
inBinFile.read (reinterpret_cast<char*> (&Array[row][column]), sizeof(int)
}
}
}
inBinFile.close();
}
All the data being read is being inserted into the first row, how can i get the data to load as i exited the program?
You are reading only when data is not equal zero, means that it gets locked with first zero. Once it reaches zero it stops reading.
Before "if command" read file to some other varaible then in if (variable != 0) Array[row][column] = variable.
If your Array is initialized with data, maybe take a look into setting position of your reading. So to set ok I have zero, I should read from another position next.
Binary files take a simple memory dump. I'm on a mac so I had to find a way to calculate the size of the array since sizeof(array name) doesn't return the memory size of the array for some reason (macintosh, netbeans IDE, xCode compiler). The workaround I had to use was:
to write the file:
fstream fil;
fil.open("filename.xxx", ios::out | ios::binary);
fil.write(reinterpret_cast<char *>(&name), (rows*COLS)*sizeof(int));
fil.close();
//note: since using a 2D array &name can be replaced with just the array name
//this will write the entire array to the file at once
Reading was the same. Since the examples from the Gaddis book I was using did not work correctly on Macintosh I had to find a different way to accomplish this. Had to use the following code snippet
fstream fil;
fil.open("filename.xxx", ios::in | ios::binary);
fil.read(reinterpret_cast<char *>(&name), (rows*COLS)*sizeof(int));
fil.close();
//note: since using a 2D array &name can be replaced with just the array name
//this will write the entire array to the file at once
Instead of just getting the size of the entire array you need to calculate the size of the entire array by multiplying the rows*columns for a 2d array then multiplying it by the size of the data type (since I used integer array it was int in this case).
I'm learning c++ and got the project to send a pascal's triangle to output (after n-rows of calculation)., getting output like this, stored in a stringstream "buffer"
1
1 1
1 2 1
1 3 3 1
But what I want is rather
1
1 1
1 2 1
1 3 3 1
My idea was: calculate the difference of the last line and current line length (I know that the last one is the longest). Then pad each row using spaces (half of the line-length-difference).
My Problem now is:
I didn't get how getLine works, neither how I might extract a specific (-> last) line
I don't know and could not find how to edit one specific line in a stringstream
Somehow I got the feeling that I'm not on the best way using stringstream.
So this is rather a common question: How'd you solve this problem and if possible with stringstreams - how?
To know the indentation of the first line, you would need to know the number of lines in the input. Therefore you must first read in all of the input. I chose to use a vector to store the values for the convenience of the .size() member function which will give the total number of lines after reading in all input.
#include<iostream>
#include<sstream>
#include<vector>
#include<iomanip> // For setw
using namespace std;
int main()
{
stringstream ss;
vector<string> lines;
string s;
//Read all of the lines into a vector
while(getline(cin,s))
lines.push_back(s);
// setw() - sets the width of the line being output
// right - specifies that the output should be right justified
for(int i=0,sz=lines.size();i<sz;++i)
ss << setw((sz - i) + lines[i].length()) << right << lines[i] << endl;
cout << ss.str();
return 0;
}
In this example, I am using setw to set the width of the line to be right justified. The padding on the left side of the string is given by (sz - i) where sz is the total number of lines and i is the current line. Therefore every subsequent line has 1 less space on the left hand side.
Next I need to add in the original size of the line (lines[i].length()), otherwise the line will not contain a large enough space for the resulting string to have the correct padding on the left hand side.
setw((sz - i) + lines[i].length())
Hope this helps!
If you have access to the code that writes the initial output, and if you know the number of lines N you are writing, you could simply do:
for(int i = 0; i < N; ++i) {
for(int j = 0; j < N - 1 - i; ++j)
sstr << " "; // write N - 1 - i spaces, no spaces for i == N.
// now write your numbers the way you currently do
}