Trying to read the whole text file - c++

I'm trying to read the whole contain of the txt file, not line by line, but the whole contain
and print it on screen inside a textfield in xcode
i'm using a mix of obj-c and c++ lang:
while(fgets(buff, sizeof(buff), in)!=NULL){
cout << buff; // this print the whole output in the console
NSString * string = [ NSString stringWithUTF8String:buff ] ;
[Data setStringValue:string]; // but this line only print last line inside the textfield instead of printing it all
}
I'm trying to print the whole contain of the file like:
something...
something...
etc...
but instead it just printing the last line to the textfield, please help me

Is there a reason you aren't using Obj-C to read the file? It would be as simple as:
NSData *d = [NSData dataWithContentsOfFile:filename];
NSString *s = [[[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding] autorelease];
[Data setStringValue:s];
Edit: To use the code you have now I would try something like this:
while(fgets(buff, sizeof(buff), in)!=NULL){
NSMutableString *s = [[Data stringValue] mutableCopy];
[s appendString:[NSString stringWithUTF8String:buff]];
[Data setStringValue:s];
}

Read a file, return the content as a C++ string:
// open the file
std::ifstream is;
is.open(fn.c_str(), std::ios::binary);
// put the content in a C++ string
std::string str((std::istreambuf_iterator<char>(is)),
std::istreambuf_iterator<char>());
In your code you are using the C api (FILE* from cstdio). In C, the code is more complex:
char * buffer = 0; // to be filled with the entire content of the file
long length;
FILE * f = fopen (filename, "rb");
if (f) // if the file was correctly opened
{
fseek (f, 0, SEEK_END); // seek to the end
length = ftell (f); // get the length of the file
fseek (f, 0, SEEK_SET); // seek back to the beginning
buffer = malloc (length); // allocate a buffer of the correct size
if (buffer) // if allocation succeed
{
fread (buffer, 1, length, f); // read 'length' octets
}
fclose (f); // close the file
}

To answer the question of why your solution didn't work:
[Data setStringValue:string]; // but this line only print last line inside the textfield instead of printing it all
Assuming that Data refers to a text field, setStringValue: replaces the entire contents of the field with the string you passed in. Your loop reads and sets one line at a time, so at any given time, string is one line from the file.
Views only get told to display when you're not doing anything else on the main thread, so your loop—assuming that you didn't run it on another thread or queue—does not print one line at a time. You read each line and replace the text field's contents with that line, so when your loop finishes, the field is left with the last thing you set its stringValue to—the last line from the file.
Slurping the entire file at once will work, but a couple of problems remain:
Text fields aren't meant for displaying multiple lines. No matter how you read the file, you're still putting its contents in a place that isn't designed for such contents.
If the file is large enough, reading it will take a significant amount of time. If you do this on the main thread, then, during that time, the app will be hung.
A proper solution would be:
Use a text view, not a text field. Text views are built to work with text of any number of lines, and when you create one in a nib, it comes wrapped in a scroll view for free.
Read the file one line or other limited-size chunk at a time, but not in a for or while loop. Use NSFileHandle or dispatch_source, either of which will call a block you provide whenever they read another chunk of the file.
Append each chunk to the text view's storage instead of replacing the entire text with it.
Show a progress indicator when you start reading, then hide it when you finish reading. For extra credit, make it a determinate progress bar, showing the user how far you've gotten through the file.

Related

How to DELETE a line(s) in C++?

I am new to file-handling...
I am writing a program that saves data in text-files in the following format:
3740541120991
Syed Waqas Ali
Rawalpindi
Lahore
12-12-2012
23:24
1
1
(Sorry for the bad alignment, it's NOT a part of the program)
Now I'm writing a delete function for the program that would delete a record.
So far this is my code:
void masterSystem::cancelReservation()
{
string line;
string searchfor = "3740541120991";
int i=0;
ifstream myfile("records.txt");
while (getline(myfile, line))
{
cout << line << endl;
if (line==searchfor)
{
// DELETE THIS + THE NEXT 8 LINES
}
}
}
I've done a bit of research and have found out that there is no easy way to access the line of a text file so we have to create another text file.
But the problem arises that how do I COPY the records/data before the record being deleted into the NEW text file?
Open the input file; read one line at a time from the input file. If you decide to want to keep that line, write it to the output file. On the other hand, if you want to 'delete' that line, don't write it to the output file.
You could have a record per line and make even more easy for example:
3740541120991|Syed Waqas Ali|Rawalpindi|Lahore|12-12-2012|23:24|1|1
and the | character saparating each field. This is a well known technic knows as CSV (Comma separated Values)
This way you don't have to worry about reading consecutive lines for erase a record and add a record access the file only once.
So your code becoms into:
void masterSystem::cancelReservation()
{
string line;
string searchfor = "3740541120991";
ifstream myfile("records.txt");
while (getline(myfile, line))
{
// Here each line is a record
// You only hace to decide if you will copy
// this line to the ouput file or not.
}
}
Don't think only about removing a record, there are others operations you will need to do against this file save a new record, read into memory and search.
Think a moment about search, and having your current desing in mind, try to answer this: How many reservations exists for date 12-12-2012 and past 12:00 AM?
In your code you have to access the file 8 times per record even if the other data is irrelevant to the question. But, if you have each record in a line you only have to access file 1 time per record.
With a few reservations the diference is near 0, but it grows exponentially (n^8).

Avoid \r\n while reading text from a binary file

I have a binary file packing lots of files (something like a .tar), where I can found both binary and text files.
When processing in memory strings, carriage lines are usually '\n', but if I read the text part from this packed file, I get "\r\n". Therefore processing this text gives me errors.
Here is the code for reading the text from a binary file:
FILE* _fileDescriptor; // it's always open to improve performance
fopen_s(&_fileDescriptor, _filePath.string().c_str(), "rb");
char* data = new char[size + 1]; // size is a known and correct value
fseek(_fileDescriptor, begin, SEEK_SET); // begin is another known value, where the file starts inside the packed one
fread(data, sizeof(char), size, _fileDescriptor);
data[it->second.size] = '\0';
This gives me the right text into data, but the following code gives me error when reading an empty line:
istringstream ss(data); // create a stringstream to process it in another function
delete[] data; // free the data buffer
// start processing the file
string line;
getline(infile, line); // read an empty line
if(line.size() > 0) {
/*
enters here, because the "empty" line was "\r\n", and now the value of line is '\r', therefore line.size() == 1
*/
...
So, any advice to avoid the '\r'?
I edited it on Notepad++. Changing its configuration to use '\n' instead of '\r\n' as line carriage works, but I don't want to depend on this because other people can miss that, and it would be very hard to spot the problem if that happens.
Probably easiest to trim the '\r' characters out of your string and then discard blank lines. See this answer for approaches to trimming a std::string (I'm assuming that's what 'line' is):
What's the best way to trim std::string?

C++ Reading file using while loop, start at line "x"

I've been stuck on this issue for a while. What I have here is a loop that will read a text file containing file names. The loop reads these lines one by one, and sets it into memory via the variable sFileName. sFileName is later called upon to load an image, and the program does this one by one as the loop loads it. The user then selects a tag for the image and loads the next one. The tag and the image file name are exported into another text file, named imgresults.txt. Now, the text file with the file names is a few thousand lines. So, in the case that the user has to exit the program and tries to continue later, the loop restarts, instead of leaving off at the point when the program was closed.
I am trying to find a way to have the loop start at that point. So far, I decided to use getline() to count how many lines are currently in imgresults.txt, as that will give the number of images that have already been run through the program. This number is stored in the variable "x". I've been doing a lot of research, but I just cannot find how to set a condition for the while loop to begin at line "x". Do you guys have any suggestions? Also, if you need any clarifications, please ask. I only included the code regarding the loop, as the code for loading the image and such is perfect fine.
int _tmain(int argc, _TCHAR* argv[])
{
int value = 0;
int nCounter = 0;
FILE* fIn = NULL;
char * sLine = new char[MAX_FILENAME_SIZE];
char * sFileName = new char [MAX_FILENAME_SIZE];
char * s = new char [MAX_FILENAME_SIZE];
#define ImgListFileName "path"
#define ImgRepository "path"
if ((fIn = fopen(ImgListFileName,"rt"))==NULL)
{
printf("Failed to open file: %s\n",ImgListFileName);
return nCounter;
}
ifstream imgresults;
imgresults.open ("imgresults.txt");
int x=0;
string line;
while(!imgresults.eof()) {
getline (imgresults, line);
x++;
}
srand (time(NULL));
cout << x;
while(!feof(fIn)){
memset(sLine,0,MAX_FILENAME_SIZE*sizeof(char));
memset(sFileName,0,MAX_FILENAME_SIZE*sizeof(char));
memset(s,0,MAX_FILENAME_SIZE*sizeof(char));
fgets(sLine,MAX_FILENAME_SIZE,fIn);
strncpy(s,sLine,65);
strcat(sLine,"\0");
strcat(sFileName,s);
printf (sFileName);
nCounter++;
}
Thanks in advance!
If you really want to use imgresults.txt as the information on where you should start from the input file, then the best you can do is to have a while loop to read x lines from the input file just before the while loop where you read the input file.
while (x--) {
fgets(sLine, MAX_FILENAME_SIZE, fIn);
}
Better solution would probably be to write state of processing to another file so that you would not have to read input file line by line, but you could immediately seek to a known offset in the file.
Use ifstream::tellg to retrieve and store the current position of the file when the programm was closed.
Use ifstream::seekg to restore that position when the porgramm restarts.
Before you read each line, save the current offsets in your input and output file to a separate file, seeking back to the beginning and overwriting the existing data each time.
When/if you restart, read the offsets from that file, seek to those points, and start working from there.
You can just read lines in parallel from both files and stop when you reach the end of results file. When the loop ends, you have already discarded the file names that were already processed.
ifstream results("results.txt");
ifstream names("names.txt");
if (results && names) {
std::string temp1, temp2;
while (getline(results, temp1) && getline(names, temp2)) ; /* do nothing */
}
if (names) {
// process the rest
}
Not the most efficient solution, but it saves you the hassle of saving offsets. Just make sure that before the very first processing the results file is completely empty (or doesn't exist at all), otherwise this code will skip the first line of names file.

Binary file only overwrites first line C++

So I have a binary file that I create and initialize. If I set my pointer to seekg = 0 or seekp = 0, then I can overwrite the line of text fine. However if I jump ahead 26 bytes (the size of one line of my file and something I have certainly confirmed), it refuses to overwrite. Instead it just adds it before the binary data and pushes the old data further onto the line. I want the data completely overwritten.
char space1[2] = { ',' , ' '};
int main()
{
CarHashFile lead;
lead.createFile(8, cout);
fstream in;
char* tempS;
tempS = new char[25];
in.open("CarHash.dat", ios::binary | ios::in | ios::out);
int x = 2000;
for(int i = 0; i < 6; i++)
tempS[i] = 'a';
int T = 30;
in.seekp(26); //Start of second line
in.write(tempS, 6); //Will not delete anything, will push
in.write(space1, sizeof(space1)); //contents back
in.write((char *)(&T), sizeof(T));
in.write(space1, sizeof(space1));
in.write(tempS,6);
in.write(space1, sizeof(space1));
in.write((char *)&x, sizeof(x));
//Now we will use seekp(0) and write to the first line
//it WILL overwrite the first line perfectly fine
in.seekp(0);
in.write(tempS, 6);
in.write((char*) &x, sizeof(x));
in.write(tempS, 6);
in.write((char *) &T, sizeof(T));
return 0;
}
The CarHashFile is an outside class that creates a binary file full of the following contents when create file is invoked: "Free, " 1900 ", Black, $" 0.00f.
Everything enclosed in quotes was added as a string, 1900 as an int, and 0.00f as a float obviously. I added all of these through write, so I'm pretty sure it's an actual binary file, I just don't know why it only chooses to write over the first line. I know the file size is correct because if I set seekp = 26 it will print at the beginning of the second line and push it down. space was created to easily add the ", " combo to the file, there is also a char dol[1] = '$' array for simplicity and a char nl[1] = '\n' that lets me add a new line to the binary file (just tried removing that binary add and it forced everything onto one row, so afaik, its needed).
EDIT: Ok so, it was erasing the line all along, it just wasn't putting in a new line (kind of embarrassing). But now I can't figure out how to insert a newline into the file. I tried writing it the way I originally did with char nl[1] = { '\n' }. That worked when I first created the file, but won't afterwards. Are there any other ways to add lines? I also tried in << endl and got nothing.
I suggest taking this one step at a time. the code looks OK to me, but lack of error checking will mean any behavior could be happening.
Add error checks and reporting to all operations on in.
If that shows no issues, do a simple seek then write
result = in.pseek(26);
//print result
result = in.write("Hello World",10);
// print result
in.close();
lets know what happens
The end problem wasn't my understand of file streams. It was my lack of understanding of binary files. The newline screwed everything up royally, and while it could be added fine at one point in time, dealing with it later was a huge hassle. Once I removed that, everything else fell into place just fine. And the reason a lot of error checking or lack of closing files is there is because its just driver code. Its as bare bones as possible, I really didn't care what happened to the file at that point in time and I knew it was being opened. Why waste my time? The final version has error checks, when the main program was rewritten. And like I said, what I didn't get was binary files, not file streams. So AJ's response wasn't very useful, at all. And I had to have 25 characters as part of the assignment, no name is 25 characters long, so it gets filled up with junk. Its a byproduct of the project, nothing I can do about it, other than try and fill it with spaces, which just takes more time than skipping ahead and writing from there. So I chose to write what would probably be the average name (8 chars) and then just jump ahead 25 afterwards. The only real solution I could say that was given here was from Emile, who told me to get a Hex Editor. THAT really helped. Thanks for your time.

Need help about monitoring txt file and reading new(last) entry(word) from that txt file

This is my first contact with C++.I have to make program that will monitor one .txt or .doc file and read every new(last) entry(word) from it.Only thing that I was able to do by now is to completely read txt file, but that is not the point, I can't even get only last word from txt file so I would really appreciate your help with this.
Thank you all in advance!!!
Not sure if this is homework, and just in case it is I'm trying to avoid spoiling it by "telling to much", and instead point you to the key ideas you could use.
To avoid reading the whole file, you could use first use the seekg method to position the file a certain number of bytes from the end, then perform the "read to the last word" from there.
To perform the "read to the last word" task proper (net of the optimization of not reading the whole file one word at a time, for which see first paragraph) use the >> operator with the std::ifstream as the left operand and a std::string as the right operand: just put this in a while(!thestream.eof()) { ... } so it will keep reading until it has the last word.
BTW, note that reading the text from a .doc file will be orders of magnitude harder than reading it from a text file, unless you can use a suitable ".doc-reading library" (the standard C++ library has no such functionality, per se).
Reading from MS Word from C++ is a tedious task; you'll need to get through the jumble of COM interfaces. Since you are saying it's your first contact with C++, my advice is to concentrate on plain text instead, namely on getting the last line of a plain text file.
I would do something like this. Provide your implementations of ReadFromEnd and FindRightmostLineSeparator, they should be trivial, and initialize the fileSize variable.
int const INITIAL_BUFFER_SIZE = 64;
int bufferSize = INITIAL_BUFFER_SIZE;
char* lastLine = NULL;
std::auto_ptr<char> buffer (new char[buffer_size]);
while(true) {
ReadFromEnd(buffer, buffer_size);
lastLine = FindRightmostLineSeparator(buffer);
if (lastLine == NULL && bufferSize == fileSize)
lastLine = buffer;
if (lastLine)
break;
buffer_size *= 2;
if (buffer_size > fileSize)
bufferSize = fileSize;
buffer.reset(new char[buffer_size]);
}
// lastLine contains the pointer to your last line