Why does my program infinitely loop? - c++

The program that I'm working on reads an input file's contents (.csv), creates an output file (.txt), and outputs the input file's content in the output file in a formatted fashion. Here's how it looks:
#include "stdafx.h"
#include <iostream> // standard input/output library
#include <string> // string data type and its associated functions
#include <fstream> // file input/output
using namespace std; // use standard namespaces
const int iRows = 1119; // input file contains 1,119 rows
const int iColumns = 11; // input file contains 11 columns
string strData[iRows][iColumns]; // 2-dimensional array that holds input file contents
// pads strings to make them the same wide, for fixed width output
string Align(string strIn, int iWidth)
{
string strOut; // padding
// add padding
for (int i = 0; i < iWidth - strIn.length(); i++)
strOut += " ";
return strOut; // return padding
}
// main program entry point
int main()
{
ifstream inFile; // handle for input file
string strSourcePath = // input file path
"C:\\Users\\Logan\\Documents\\CIS022_S2017_Lab8b.csv";
ofstream outFile; // handle for output file
string strDestPath = // output file path
"C:\\Users\\Logan\\Documents\\out.txt";
inFile.open(strSourcePath); // open input file for read (ifstream)
for (int i = 0; i < iRows; i++) // loop for rows
for (int j = 0; j < iColumns; j++) // embedded loop for column
{
if (j == iColumns - 1) // the last element in the row is newline delimited
getline(inFile, strData[i][j], '\n');
else // all other elements are comma delimited
getline(inFile, strData[i][j], ',');
/*cout << "i = " << i << " j = " << j << " " << strData[i][j] << endl;*/ // console dump for error checking
}
inFile.close(); // done with input file, close it
outFile.open(strDestPath); // open output file for write (ofstream)
for (int i = 0; i < iRows; i++) // loop through each input row
{
outFile <<
strData[i][0] << Align(strData[i][0], 7) << // CRN
strData[i][1] << Align(strData[i][1], 6) << // Subject
strData[i][2] << Align(strData[i][2], 6) << // Number
strData[i][3] << Align(strData[i][3], 20) << // Title
strData[i][4] << Align(strData[i][4], 7) << // Days
strData[i][5] << Align(strData[i][5], 13) << // Meetdates
strData[i][6] << Align(strData[i][6], 17) << // Times
strData[i][7] << Align(strData[i][7], 6) << // Credits
strData[i][8] << Align(strData[i][8], 13) << // Instructor
strData[i][9] << Align(strData[i][9], 6) << // Room
strData[i][10] << endl; // Max Enroll
}
outFile.close(); // close output file
system("Pause"); // wait for user input
return 0; // exit program
}
However, whenever I run it, it loops infinitely here:
for (int i = 0; i < iRows; i++) // loop through each input row
{
outFile <<
strData[i][0] << Align(strData[i][0], 7) << // CRN
strData[i][1] << Align(strData[i][1], 6) << // Subject
strData[i][2] << Align(strData[i][2], 6) << // Number
strData[i][3] << Align(strData[i][3], 20) << // Title
strData[i][4] << Align(strData[i][4], 7) << // Days
strData[i][5] << Align(strData[i][5], 13) << // Meetdates
strData[i][6] << Align(strData[i][6], 17) << // Times
strData[i][7] << Align(strData[i][7], 6) << // Credits
strData[i][8] << Align(strData[i][8], 13) << // Instructor
strData[i][9] << Align(strData[i][9], 6) << // Room
strData[i][10] << endl; // Max Enroll
}
The input file contains 1119 rows of information, so I'll give you the first row:
CRN,Subj,Num,Title,Days,Meetdates,Times,Credits,Instructor,Room,Max Enroll
I let my program sit for a minute and nothing happened. Even adding this code at the beginning of the for loop only outputs the first row of information:
cout <<
strData[i][0] << " " <<
strData[i][1] << " " <<
strData[i][2] << " " <<
strData[i][3] << " " <<
strData[i][4] << " " <<
strData[i][5] << " " <<
strData[i][6] << " " <<
strData[i][7] << " " <<
strData[i][8] << " " <<
strData[i][9] << " " <<
strData[i][10] << endl;
Why does my program infinitely loop?

What happens if, in this code,
string Align(string strIn, int iWidth)
{
string strOut; // padding
// add padding
for (int i = 0; i < iWidth - strIn.length(); i++)
strOut += " ";
return strOut; // return padding
}
strIn is longer than iWidth ?
You will attempt to increment i until it reaches a negative number.
Here is probably your issue.

Related

creating a c++ program that displays hexadecimal-formatted data from a bmp file

I'm trying to create a program that displays output of a bmp file in the form of hexadecimal. So far I get the output, but I need it to be organized a certain way.
The way it needs to be organized is with the address of the bmp file to be on the left column and then 16 bytes of data in hex across each row in the order they appear in the file. While leaving an extra space between every 8 bytes. So far, I got the hexadecimal to show up, I just need help with organizing it.
What I have:
What I'm trying to make it look like:
Here is my code:
#include <iostream> // cout
#include <fstream> // ifstream
#include <iomanip> // setfill, setw
#include <stdlib.h>
using namespace std; // Use this to avoid repeated "std::cout", etc.
int main(int argc, char *argv[]) // argv[1] is the first command-line argument
[enter image description here][1]{
// Open the provided file for reading of binary data
ifstream is("C:\\Users\\Test\\Documents\\SmallTest.bmp", ifstream::binary);
if (is) // if file was opened correctly . . .
{
is.seekg(0, is.end); // Move to the end of the file
int length = is.tellg(); // Find the current position, which is file length
is.seekg(0, is.beg); // Move to the beginning of the file
char * buffer = new char[length]; // Explicit allocation of memory.
cout << "Reading " << length << " characters... ";
is.read(buffer, length); // read data as a block or group (not individually)
if (is)
cout << "all characters read successfully.\n";
else
cout << "error: only " << is.gcount() << " could be read.\n";
is.close();
// Now buffer contains the entire file. The buffer can be printed as if it
// is a _string_, but by definition that kind of print will stop at the first
// occurrence of a zero character, which is the string-ending mark.
cout << "buffer is:\n" << buffer << "\n"; // Print buffer
for (int i = 0; i < 100; i++) // upper range limit is typically length
{
cout << setfill('0') << setw(4) << hex << i << " ";
cout << setfill('0') << setw(2) << hex << (0xff & (int)buffer[i]) << " ";
}
delete[] buffer; // Explicit freeing or de-allocation of memory.
}
else // There was some error opening file. Show message.
{
cout << "\n\n\tUnable to open file " << argv[1] << "\n";
}
return 0;
}
You could do it something like this:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <cctype>
std::ostream& fullLine(std::ostream& out, const std::vector<uint8_t>& v, size_t offset)
{
//save stream state so we can restore it after all the hex/setw/setfill nonsense.
std::ios oldState(0);
oldState.copyfmt(out);
out << std::hex << std::setfill('0') << std::setw(8) << offset << " ";
for (size_t i = 0; i < 16; ++i)
{
if (i == 8) out << " ";
out << std::hex << std::setfill('0') << std::setw(2) << static_cast<uint32_t>(v[i + offset]) << " ";
}
out << " ";
//restore stream state to print normal text
out.copyfmt(oldState);
for (size_t i = 0; i < 16; ++i)
{
out << (std::isprint(v[i + offset]) ? static_cast<char>(v[i + offset]) : '.');
}
out << "\n";
return out;
}
int main()
{
std::vector<uint8_t> data;
std::ifstream f("test.txt", std::ios::binary);
if (f)
{
f.seekg(0, f.end);
data.resize(static_cast<size_t>(f.tellg()));
f.seekg(0, f.beg);
f.read((char*)data.data(), data.size());
const size_t numFullLines = data.size() / 16;
const size_t lastLineLength = data.size() % 16;
for (size_t i = 0; i < numFullLines; ++i)
{
if (!fullLine(std::cout, data, i * 16))
{
std::cerr << "Error during output!\n";
return -1;
}
}
}
return 0;
}
There's probably a fancy way to do it, but I usually go for brute force when I'm looking for particular output using iostreams.
How to handle the partial last line is up to you. :)
Use the % operator to break the line after every 16th count:
cout << hex;
for(int i = 0; i < 100; i++)
{
if(i && (i % 16) == 0)
cout << "\n";
cout << setfill('0') << setw(2) << (buffer[i] & 0xFF) << " ";
}
I need it to be organized a certain way.
In another answer, I submitted this form of dumpByteHex()... perhaps it can help you achieve what you want. (see also https://stackoverflow.com/a/46083427/2785528)
// C++ support function
std::string dumpByteHex (char* startAddr, // reinterpret_cast explicitly
size_t len, // allows to char* from T*
std::string label = "",
int indent = 0)
{
std::stringstream ss;
if(len == 0) {
std::cerr << "\n dumpByteHex() err: data length is 0? " << std::endl << std::dec;
assert(len != 0);
}
// Output description
ss << label << std::flush;
unsigned char* kar = reinterpret_cast<unsigned char*>(startAddr); // signed to unsigned
std::string echo; // holds input chars until eoln
size_t indx;
size_t wSpaceAdded = false;
for (indx = 0; indx < len; indx++)
{
if((indx % 16) == 0)
{
if(indx != 0) // echo is empty the first time through for loop
{
ss << " " << echo << std::endl;
echo.erase();
}
// fields are typically < 8 bytes, so skip when small
if(len > 7) {
if (indent) { ss << std::setw(indent) << " "; }
ss << std::setfill('0') << std::setw(4) << std::hex
<< indx << " " << std::flush;
} // normally show index
}
// hex code
ss << " " << std::setfill('0') << std::setw(2) << std::hex
<< static_cast<int>(kar[indx]) << std::flush;
if((indx % 16) == 7) { ss << " "; wSpaceAdded = true; } // white space for readability
// defer the echo-of-input, capture to echo
if (std::isprint(kar[indx])) { echo += kar[indx]; }
else { echo += '.'; }
}
// finish last line when < 17 characters
if (((indx % 16) != 0) && wSpaceAdded) { ss << " "; indx++; } // when white space added
while ((indx % 16) != 0) { ss << " "; indx++; } // finish line
// the last echo
ss << " " << echo << '\n';
return ss.str();
} // void dumpByteHex()
Output format:
0000 11 22 33 44 55 66 00 00 00 00 77 88 99 aa ."3DUf....w...

Getting all chars in text file by fscanf() returns invalid count of char

I was working on getting strings from text files by c File. I was intended to get lines with using text size. I created a simple text file;
a s
da
Its 7 byte. I was thinking file size should be 6 byte because it has 5 character and a new line. I've worked on it and figured it has 2
'\n'
char value. My first problem is why text puts 2 new line for every line.
Then I used fscanf function to get strings but this time it gave me char array size of 6 not 7.It ignores second new line byte. But when I call fscanf() to get one byte one I can reach all bytes Why that array has 6 char even I call fscanf() function to get 7 byte ?
Here is my code:
#include <iostream>
#include <windows.h>
#include <stdio.h>
using namespace std;
int main()
{
//Declaring char arrays for every bits//
char a[1], b[1] , c[1] , d[1] , e[1] , f[1] , g[1] , h[7];
//Streaming the text file//
FILE *file;
file=fopen("conversations.txt","r");
fseek (file, 0, SEEK_END);
int size=ftell (file);
printf ("Size of myfile.txt: %ld bytes.%c",size , 10);
//Appending char value of first pointed bit//
fseek ( file , 0 , SEEK_SET );
fscanf(file, "%1c",a);
cout << a[0] << "<-a||";
fseek ( file , 1 , SEEK_SET );
fscanf(file, "%1c",b);
cout << b[0]<< "<-b||";
fseek ( file , 2 , SEEK_SET );
fscanf(file, "%1c",c);
cout << c[0]<< "<-c||";
fseek ( file , 3 , SEEK_SET );
fscanf(file, "%1c",d);
cout << d[0]<< "<-d||";
fseek ( file , 4, SEEK_SET );
fscanf(file, "%1c",e);
cout << e[0]<< "<-e||";
fseek ( file , 5 , SEEK_SET );
fscanf(file, "%1c",f);
cout <<f[0]<< "<-f||";
fseek ( file , 6 , SEEK_SET );
fscanf(file, "%1c",g);
cout << g[0]<< "<-g||";
//Getting bits of chars
cout << endl<<endl;
for (int i = 0; i < 8; ++i) {
bool is_set = a[0] & (1 << i);
cout << "Bit " << i << ": " << is_set << '\n';
}
cout<<endl<<endl;
for (int i = 0; i < 8; ++i) {
bool is_set = b[0] & (1 << i);
cout << "Bit " << i << ": " << is_set << '\n';
}
cout<<endl<<endl;
for (int i = 0; i < 8; ++i) {
bool is_set = c[0] & (1 << i);
cout << "Bit " << i << ": " << is_set << '\n';
}
cout<<endl<<endl;
for (int i = 0; i < 8; ++i) {
bool is_set = d[0] & (1 << i);
cout << "Bit " << i << ": " << is_set << '\n';
}
cout<<endl<<endl;
for (int i = 0; i < 8; ++i) {
bool is_set = e[0] & (1 << i);
cout << "Bit " << i << ": " << is_set << '\n';
}
cout<<endl<<endl;
for (int i = 0; i < 8; ++i) {
bool is_set = f[0] & (1 << i);
cout << "Bit " << i << ": " << is_set << '\n';
}
cout<<endl<<endl;
for (int i = 0; i < 8; ++i) {
bool is_set = g[0] & (1 << i);
cout << "Bit " << i << ": " << is_set << '\n';
}
//Appending all bytes to one char array//
fseek ( file , 0 , SEEK_SET );
fscanf( file, "%7c",h);
for(int i = 0 ; i < 7 ; i++ )
{
cout << h[i];
}
cout << endl << endl;
//Getting bits of chars
for (int i = 0; i < 8; ++i) {
bool is_set = h[0] & (1 << i);
std::cout << "Bit " << i << ": " << is_set << '\n';
}
cout << endl << endl;
for (int i = 0; i < 8; ++i) {
bool is_set = h[1] & (1 << i);
std::cout << "Bit " << i << ": " << is_set << '\n';
}
cout << endl << endl;
for (int i = 0; i < 8; ++i) {
bool is_set = h[2] & (1 << i);
std::cout << "Bit " << i << ": " << is_set << '\n';
}
cout << endl << endl;
for (int i = 0; i < 8; ++i) {
bool is_set = h[3] & (1 << i);
std::cout << "Bit " << i << ": " << is_set << '\n';
}
cout << endl << endl;
for (int i = 0; i < 8; ++i) {
bool is_set = h[4] & (1 << i);
std::cout << "Bit " << i << ": " << is_set << '\n';
}
cout << endl << endl;
for (int i = 0; i < 8; ++i) {
bool is_set = h[5] & (1 << i);
std::cout << "Bit " << i << ": " << is_set << '\n';
}
cout << endl << endl;
for (int i = 0; i < 8; ++i) {
bool is_set = h[6] & (1 << i);
std::cout << "Bit " << i << ": " << is_set << '\n';
}
return 0;
}
You can't use ftell() to determine how big a text file is.
Per the C Standard, 7.21.9.4 The ftell function:
The ftell function obtains the current value of the file position
indicator for the stream pointed to by stream. For a binary
stream, the value is the number of characters from the
beginning of the file. For a text stream, its file position
indicator contains unspecified information, usable by the fseek
function for returning the file position indicator for the stream to
its position at the time of the ftell call; the difference
between two such return values is not necessarily a meaningful
measure of the number of characters written or read.
OP is attempting to look at a file, byte by byte, yet opens the file in "text" mode
file=fopen("conversations.txt","r");
In text mode various translations may occur concerning lines and file endings. To read a file byte-by-byte and print it true values, open the file in binary mode.
file=fopen("conversations.txt","rb");
Using binary mode and fscanf() is dodgy, better to use fread().
In Windows, a "\r\n" sequence is typically changed to '\n' when work with a file in text mode. A lone "\n" sequence remains '\n'. This is why OP sees 2 '\n' and code attempts to read the file in text mode at select offsets.
Windows (and MS-DOS) use a two byte sequence, 0x0D 0x0A, to represent a new line in text files. So every new line in your text produces two bytes in your text file. fscanf and other C and C++ text input functions (as well as output functions) understand the line conventions for the system that they are designed for: on output the character '\n' generates the appropriate sequence to start a new line, with 0x0D 0x0A on Windows, and 0x0A on typical Unix systems, and 0x0D on older Macs. On input the functions simply reverse that process. So on Windows the two bytes that represent the end of a line are read as a single character, '\n', that represents a new line.
The two bytes in Windows files are sometimes lazily referred to as \r\n, but since '\n' represents a newline character, writing "\r\n" to a file would produce 0x0D 0x0D 0x0A. Better to think of those bytes in the file as what they are: ASCII codes for CR and LF. It's the combination of the two that moves the output position to the beginning of the line (CR for Carriage Return), then moves it down to the next line (LF for Line Feed).

C++ ofstream write does not work under Windows. Works fine under Linux

The code below does not work under Windows and GNU C++, VS10,VS12, Intel C++ 14.0. The code below does work under Linux and GNU C++ 4.7, 4.8, Intel C++ 14, Open64 5.0.Replacing DIMEN with DIMEN-256 in the inner test for-loop ... works!? Any idea?
//============================//
// Read and Write binary file //
// using buffers //
//============================//
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
using namespace std;
int main()
{
// 1. variables and parameters
const long int DIMEN = static_cast<long int>(pow(10.0,8.0));
const long int I_DO_MAX = 100;
const string fileName = "my_file.bin";
ofstream fileOUT;
ifstream fileIN;
double* myArrayAlpha = new double [DIMEN];
double* myArrayBeta = new double [DIMEN];
long int i;
long int j;
// 2. build the array with some data
cout << " 1 --> Build the array with some data" << endl;
for (i = 0; i < DIMEN; i++)
{ myArrayAlpha[i] = static_cast<double>(i); }
for (i = 0; i < I_DO_MAX; i++)
{
// 3. open the file stream
cout << "-------------->>> " << i << endl;
cout << " 2 --> Open the file stream" << endl;
fileOUT.open(fileName.c_str(), ios::out | ios::binary | ios::trunc);
fileIN.open(fileName.c_str(), ios::in | ios::binary);
// 4. test if the file stream is opened
cout << " 3 --> Test if the file stream is opened with success" << endl;
if (!fileOUT.is_open())
{ cout << "Error! The output file stream is not opened. Exit."
<< endl; return -1; }
if (!fileIN.is_open())
{ cout << "Error! The input file stream is not opened. Exit."
<< endl; return -1; }
// 5. write the contents of myArrayAlpha[] to a file
cout << " 4 --> Write and then Read to the file" << endl;
fileIN.seekg(0, fileIN.beg);
fileOUT.seekp(0);
fileOUT.write(reinterpret_cast<char*>(&myArrayAlpha[0]),
DIMEN * sizeof(double));
fileIN.read(reinterpret_cast<char*>(&myArrayBeta[0]),
DIMEN * sizeof(double));
// 6. test that I am writting and reading correctly
for (j = 0; j < DIMEN; j++) // replace DIMEN
{ // with DIMEN-256 to work under Windows
if (myArrayAlpha[j] != myArrayBeta[j])
{ cout << myArrayAlpha[j] << endl;
cout << myArrayBeta[j] << endl;
cout << "j = " << j << endl;
cout << "Error!"; return -1; }
}
cout << " 5 --> Read and Write with success" << endl;
cout << " 6 --> Close the I/O streams" << endl;
// 7. close the file stream
fileIN.close();
fileOUT.close();
}
// 8. free up the RAM
delete [] myArrayAlpha;
delete [] myArrayBeta;
return 0;
}
The problem is that your data is not being flushed to the external sequence after the write call, so it is still positioned in the internal buffer. Add this line after write():
fileOUT << std::flush;

C++ how to load a 16bit TIFF file in a container to perform math operations on its data?

I have writtent a small C++ console application with code::blocks that loads
an array of values from a CSV file, performs a special "inverted" random dithering on the values, and exports the result as a PBM file (a bitmap).
The density of black pixels on the final PBM picture depends on 3 independent variables: "Reflectance of the white", "Reflectance of the black", and the values of the CSV.
The reason I use a CSV file is because I don't know how I can directly load a TIFF file into my script. The values of my file "wall.csv" are produced by a python script that transforms any tiff file in a csv...
Could you please check my code and advise for a solution to load a TIFF and detect automatically the size of the image in pixels?
The variables colo and lines define the size of the image contained as ASCII data in the CSV...
And the image values are loaded in the vector <float> CSV
What library would you use to load the tiff?
Thanks!
code:
#include <deque>
#include <cmath>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <random>
#include <cstdlib>
using namespace std;
deque <float> CSV; // CSV input values, "PHOTOMETRY"
deque <float> RND; // will contain random values from 0.0 to 1.0
int colo = 0; // variables inputed
int lines = 0; // lines
float YBK = 0; // Reflectance White
float YW = 0; // Reflectance Black
float Lmax = 0; // variables to be computed
float Lmin = 10000000; // arbitrarily high value
float NBK = 0; // will contain a normalized Black value
float NW = 1; // normalized white value
float CRATIO = 0; // Black to White dynamic ratio
float LRATIO = 0; // Lowest to Highest pixel value dynamic ratio
float Z = 0; // processing variables
float X = 0;
float aBK = 0; // computed density of black at each pixel
float vRND = 0; // random value container
float IO = 0;
int main(){
cout << "please put a file named wall.csv" << endl << "in the same forler as this executable" << endl << endl;
cout << "how many:" << endl << "columns does the CSV has?" << endl;
cin >> colo;
cout << "lines does the CSV has?" << endl;
cin >> lines;
cout << "reflectance of the WHITE (CIE Y)?" << endl;
cin >> YW;
cout << "reflectance of the BLACK (CIE Y)?" << endl;
cin >> YBK;
NBK = YBK / YW; // normalized BK
CRATIO = NW / NBK; // correction Ratio
int C = lines * colo; // cells
cout << endl << " there are: " << colo << " columns";
cout << endl << " and : " << lines << " lines " ;
cout << endl << " that makes " << C << " cells " << endl;
cout << endl << " correction ratio is: " << CRATIO << endl << endl;
///_____ IMPORT THE PHOTOMETRIC DATA
cout << "...importing the photometric data" << endl;
float x = 0; // a variable that will contain a value from the file
ifstream ifs ("wall.csv");
char dummy;
for (int i = 0; i < lines; ++i){
for (int i = 0; i < colo; ++i){
ifs >> x;
if (x > Lmax) {
Lmax = x; // determines the highest pixel value
}
if (x < Lmin) {
Lmin = x; // determines the lowest pixel value
}
CSV.push_back(x);
// So the dummy won't eat digits
if (i < (colo - 1))
ifs >> dummy;
}}
ifstream ifs_close();
LRATIO = Lmax / Lmin;
cout << "...photometric data imported" << endl;
cout << endl << " maximum Luminance is: " << Lmax;
cout << endl << " minimum Luminance is: " << Lmin << endl;
cout << endl << "...luminance ratio is: " << LRATIO;
if (LRATIO > CRATIO) {
cout << endl << "...luminance ratio is: " << LRATIO;
cout << endl << "...this is too high, ending..." << '\a';
return(0);
}
cout << endl << "...luminance can be corrected :)" << endl;
///______ CREATE RANDOM VALUES BETWEEN 0 & 1
std::default_random_engine generator;
std::uniform_real_distribution <double> distribution(0.0,1.0);
for (int i=0; i<C; ++i) {
double number = distribution(generator);
RND.push_back(number);
}
cout << endl << "...random values created" << endl;
///_______ process & export to PBM
ofstream output_file("./wall.pbm");
output_file << "P1" << "\n" << colo << " " << lines << "\n"; /// PBM HEADER
cout << endl << "...file header written" << endl;
cout << endl << "...computing";
int CELLS = C; // copy the amount of cells
int LINEW = colo;
int PERCENT = 100;
while (CELLS > 0) {
while (LINEW > 0) {
Z = Lmin/CSV.front(); /// processing calculus
X = (NBK - Z)/(NBK - NW);
aBK = (1 - X);
vRND = RND.front();
if (aBK > (vRND)) {
IO = 1;
}
else {
IO = 0;
}
LINEW = LINEW - 1;
CELLS = CELLS - 1;
PERCENT = PERCENT - CELLS / C;
output_file << IO << "\n";
//cout << ERR << " "; /// fancy...
CSV.erase(CSV.begin());
RND.erase(RND.begin());
}
LINEW = colo;
}
cout << endl << "...computing done" << endl;
cout << "...file written";
output_file.close();
return(0);
}
Check out lib tiff. OpenCV just uses lib tiff as well.
http://www.libtiff.org/

Overwrite a line with ofstream C++

I am doing a little game and I am saving the player details in a txt file.
Example of that txt file:
Eric 13 8 10 10 30 10 10 50 0 0 0 0
William 1 0 10 30 30 10 10 50 0 0 0 0
John 1 0 10 30 30 10 10 50 0 0 0 0
This is what I had in mind: when the player chooses to save the game while playing, the save_game function should check if there is already any saved data. If there is, instead of appending the data to the end of the txt, it should overwrite that specific line.
Here is my current function:
// SAVE GAME
void save_game(Player player)
{
ofstream coutfile (SaveDestiny, ios::app);
if (coutfile.is_open()) // if it opens correctly
{
// Now checking if the name already exists
string imported_name;
ifstream cinfile (SaveDestiny); // opens file that contains the saved games
cinfile >> imported_name; // getting first element of file
bool j = 0; // j = 0 while the strings don't match. j = 1 when the string was found
while (cinfile >> imported_name) // while the end of file is not reached
{
if (player.name.compare(imported_name) == 0) // if the strings are the same, overwrite data
{
j = 1;
coutfile << " \r" << endl;
break;
}
else // if the strings are different, keep reading
{
cinfile >> imported_name;
}
}
// Continuing...
coutfile << player.name << " " << player.level << " " << player.exp << " " << player.max_exp << " "
<< player.hp << " " << player.max_hp << " " << player.mp << " " << player.max_mp << " "
<< player.gold << " " << player.weapon << " " << player.shield << " " << player.heal_spell << " "
<< player.attack_spell << endl;
}
else
{
ofstream coutfile (SaveDestiny, ios::app);
coutfile << "test";
cout << "Unable to open file";
cin.get();
}
draw_rectangle(37,8,72,14,15); // white limits
draw_rectangle(39,9,70,13,9); // blue background
cor(9,15);
gotoxy(50,10);
cout << "GAME SAVED!";
gotoxy(41,12);
cor(9,14);
cout << "Press <Enter> to continue... ";
cin.get();
}
On most modern filesystems files are not "line-based" (or "record-based") they are character-based so you can't "overwrite a line". The old line might be 20 characters long and the new one would be 24 characters, in which case it would overwrite the old line and the first 4 characters of the next line. To make this work you would have to "push" everything after the line later in the file, which isn't possible with C++ (or C) IO facilities.
One option would be to write all lines with a fixed length, say 50 characters, so that overwriting the 3rd line involves replacing characters 100 to 149, even if the line only actually needs 24 characters.
Another option would be to keep the file in memory in a record-based form and write out the entire file every time you change it (or at least write out the new line and all lines that come after it)
Ok I've managed to get around the problem and now it's working brilliantly! :D
First, the function checks if the player name already is on the txt. I created a enable variable j. When j=1, the name exists and the data needs to be overwritten! When j=0, the function will append the data to the txt right away.
Ok, let's say j=1. The function determines the number of lines in txt. It then creates a vector with two vectors inside: the name, and the game variables.
After that, the function deletes the previouscontent of txt file. And writes the content of the vector to the txt, except the data that needs to be overwritten (it will skip writing that part to the txt), because at the end of the function, that new data will be written. :D I hope I made myself clear enough. Sorry if someone doesn't understand what I wrote...
Here is my new save_game function:
// SAVE GAME
void save_game(Player player)
{
ofstream coutfile (SaveDestiny, ios::app);
if (coutfile.is_open()) // if it opens correctly
{
string imported_name;
ifstream cinfile (SaveDestiny); // opens file that contains the saved games
bool j = 0;
// Now checking if the name already exists
while (cinfile >> imported_name) // while the end of file is not reached
{
if (player.name.compare(imported_name) == 0) // if the strings are the same, overwrite data
{
j = 1; // enable overwrite
break;
}
// if the strings are different, keep reading
}
// at this point: j = 0 to append to end. j = 1 to overwrite.
// Overwriting data
if (j == 1)
{
ifstream cinfile (SaveDestiny);
// now determining the size of the vector (number of lines in txt)
int line_numbers = 0;
string line;
while (getline(cinfile, line))
{
line_numbers++;
}
cinfile.close(); // closing
ifstream cinfile2 (SaveDestiny); // reopening to read from the beginning
// now creating the vector with the saves
vector<vector<string>> temp_saves(line_numbers, vector<string>(2));
string name2;
string values;
for (unsigned int x = 0; x < temp_saves.size(); x++)
{
cinfile2 >> name2;
getline(cinfile2, values);
temp_saves[x][0] = name2;
temp_saves[x][1] = values;
}
coutfile.close(); // closing output file
ofstream coutfile2 (SaveDestiny); // reopening in overwrite mode
// delete all saves.txt, copying vector content to txt (except the one we want to overwrite)
for (unsigned int x = 0; x < temp_saves.size(); x++)
{
if ( temp_saves[x][0].compare(player.name) != 0)
{
coutfile2 << temp_saves[x][0] << temp_saves[x][1] << endl;
}
}
coutfile2.close(); // closing output file
}
// Appending new data...
ofstream coutfile3 (SaveDestiny, ios::app); // reopening in append mode
coutfile3 << player.name << " " << player.level << " " << player.exp << " " << player.max_exp << " "
<< player.hp << " " << player.max_hp << " " << player.mp << " " << player.max_mp << " "
<< player.gold << " " << player.weapon << " " << player.shield << " " << player.heal_spell << " "
<< player.attack_spell << endl;
}
else
{
ofstream coutfile (SaveDestiny, ios::app);
cout << "Unable to open file";
cin.get();
}
draw_rectangle(37,8,72,14,15); // white limits
draw_rectangle(39,9,70,13,9); // blue background
cor(9,15);
gotoxy(50,10);
cout << "GAME SAVED!";
gotoxy(41,12);
cor(9,14);
cout << "Press <Enter> to continue... ";
cin.get();
}