How to edit a text file with C++ - c++

How do I edit a specific line of a text file in C++? Let's say I want to open a file and change the focus or pointer or whatever its called to line 17 column 20. That way I can edit text after line 17, column 20.
I tried this, but it didnt work.
ofstream txtFile("textFile.txt");
fseek(txtFile, 17, 20);
txtFile << "New stuff to enter at this point (overwrites old not insert)";
How do I do this?

fseek is not seeking counting lines, but rather bytes. What you instruct the program is not to position the pointer at column 20 of 17th line, but rather at the 17 + 20 = 37th byte of the file.
The first parameter of the function is the origin, i.e. the count of bytes from the origin from which you count, and the second - how many more you offset.
See the reference of fseek.
I am not aware of any library that can do byte positioning in respect of lines and columns in C++. You will probably need to use a higher level function and parse lines one by one (e.g. using getline if you are after C++ solution).

Use fputs, like it's done here. In order to recognize new lines, you have to loop through the content and count the newline characters (i.e.'\n' on linux). You can get the character currently pointed to with getc.
One way to implement this is to write a function that returns the byte-position of where a given row starts, like so:
#include <stdio.h>
int getRowPos(int row,FILE* fp){
int pos=1,lines=1,currChr;
do{
currChr=getc(fp);
if(currChr=='\n')
lines++;
}while(lines<row && currChr!=EOF && ++pos);
return pos;
}
Using fseek one could then go to the position returned by getRowPos plus the column number (i.e. byte number, on the line), and then write the desired content using fputs, like so:
int row=wanted row
int col=wanted column
FILE * pFile;
//Open file for read and write
pFile = fopen ( "myfile.txt" , "rb+" );
int rowPos=getRowPos(row,pFile);
fseek ( pFile , rowPos+colPos , SEEK_SET );
fputs ( "my new content" , pFile );
fclose ( pFile );
If you know how wide your lines are, say n wide, you can add an lseek skipping n bytes ahead before the call to getc.

Related

deleting a line in a .txt file without using another file or array in C++

the professor gave told us to delete a line in a txt file without the help of another file or an array,
i tried to replace the line with backspace but it print the BS character instead
void rem()
{
fstream f("test.txt");
f.seekp(3, ios_base::beg);
f.write("\b",sizeof(char));
f.close();
}
1
2
3
4
5
i want to remove 2
1
3
4
5
after searching for few hours i found that everyone use another file or a vector or the try to replay the line with BS like me.
EDIT:
this is the correct code:
void skip_line(std::fstream& f)
{
char c;
f.get(c);
f.ignore(50, '\n');
}
void getpos(int& readpos, int& writepos)
{
std::fstream f("test.txt", std::ios::in | std::ios::binary);
skip_line(f);
writepos = f.tellg();
skip_line(f);
readpos = f.tellg();
f.close();
}
void rem()
{
int writepos, readpos, length, newSize;
std::fstream f;
getpos(readpos, writepos);
f.open("test.txt");
length = readpos - writepos;
f.seekg(readpos);
for (char c; f.get(c);)
{
f.seekg(writepos);
if (c != '\n') f.put(c);
readpos++;
writepos++;
f.seekg(readpos);
}
f.close();
//fs::path p = "test.txt"
newSize = fs::file_size("test.txt") - length;
fs::resize_file("test.txt", newSize);
}
the rusult:
befor
111111
222222
333333
444444
555555
after
111111
333333
444444
555555
Backspace will not do what you hoped for. A backspace character takes up one char just like any other character. When printed on devices capable of moving the cursor backwards, that's what'll happen. It's just a visual thing and it does not work with files.
Since you are not allowed to use another file or arrays, I'm going to assume that std::vectors and std::strings are also forbidden so I suggest shifting everything down in the file, one char at a time, to overwrite the line to be removed.
You will need a function like std::getline which is capable of reading a line from a stream into a std::string - but you do not need to store any data so we can call it skip_line. It could look like this:
std::istream& skip_line(std::istream& is) {
// read until reading fails or a newline is read:
for(char ch; is.get(ch) && ch != '\n';);
return is;
}
When you've opened the file, call skip_line until you've reached the line you want to remove. If you want to remove line 2, call skip_line 1 time. If you instead want to remove line 3, call skip_line 2 times.
The get (f.tellg()) position in the stream is now where you should start writing when you move everyting in the file back to overwrite the line to be removed. Store this position in a variable called writepos.
Call skip_line one time. The get position is now where you should start reading when moving the contents of the file. Store this position in a variable called readpos.
Calculate and store the length of the line to be removed: lenght_of_line_to_be_removed = readpos - writepos.
Now, you need to read one char at a time from the readpos position and write that char to the writepos position. It could look like this:
f.seekg(readpos); // set the _get_ position where we should read from
for(char ch; f.get(ch);) { // loop for as long as you can read a char
f.seekp(writepos); // set the _put_ position where you should write to
f.put(ch); // ...and write the char
writepos += 1; // step both positions forward
readpos += 1; // -"-
f.seekg(readpos); // set the new _get_ position
}
When the above is done, everything is "shifted down" in the file - but the size of the file will still be the same as it was before:
original: 1 2 3 4 5
after : 1 3 4 5 5
If you use C++17 or newer, you can use the standard functions std::filesystem::file_size and std::filesystem::resize_file to fix this. Remember that you stored lenght_of_line_to_be_removed above. If you use a version of C++ that does not have std::filesystem, you need to use some platform specific function. Posix systems have the truncate function that can be used for this.

Go to a specific line in file and read it

Problem description
I have a file containing a set of lines. A
File 1:
"Hello How are you"
"The cat ate the mouse"
Based on the the beginning and ending of the lines given by the user as input. I want to go to each line in the file and Extract it.
For examples if user types 1 , 17 then I have to go to line 1 that has a size of 17 characters. He can give any line number in the file.
I read the following Answer Read from a specific spot in a file C++. But I didn't really understand. Why do the lines have to be the same size? If i have the information concerning the beginning and ending of every line in the file. Why can't I access it directly?
Source code
I tried to use the following code which was inspired by Read Data From Specified Position in File Using Seekg But I couldn't extract the lines why?
#include <fstream>
#include <iostream>
using namespace std::
void getline(int, int, const ifstream & );
int main()
{
//open file1 containing the sentences
ifstream file1("file1.txt");
int beg = 1;
int end = 17;
getline(beg,end, file1);
beg = 2;
end = 20;
getline(beg,end, file1);
return 0;
}
void getline(int beg, int end, const ifstream & file)
{
file.seekg(beg, ios::beg);
int length = end;
char * buffer = new char [length];
file.read (buffer,length);
buffer [length - 1] = '\0';
cout.write (buffer,length);
delete[] buffer;
}
This code appears to be using line numbers as byte offsets. If you seek to offset "1" the file seeks forward 1 byte, not 1 line. If you seek to offset 2, the file seeks forward 2 bytes, not 2 lines.
To seek to a specific line you need to read the file and count the number of line breaks until you get to the line you want. There is code that already does this, for example std::getline(). If you don't already know the exact byte offset of the line you want, you can call std::getline() the number of times equal to the line number you want.
Also remember that the first byte of a file is at offset 0 not offset 1, and that different platforms use different bytes to indicate the end of a line (E.g. on Windows it's "\r\n", on Unix it's "\n"). If you're using a library function to read lines, the line ending should be taken care of for you.

Trying to read the whole text file

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.

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?

How to eliminate the '\n' at the end of a txt file

I'd like to eliminate the extra '\n' at the end of a txt file. Which function can be used to do this job in c / c++.
Thanks advanced
One approach would be to iterate of the file line-by-line using getline, saving off that data for later. After each line is read, write the previous line (with \n). When no more data is available write the final line without the \n anymore.
Alternately seek to the end to get the size, read the data in blocks of some chunk size, rewriting them until the second-to-last character. If the last character is a \n don't write it, otherwise do write it.
The posix way to do this is the truncate function.
If you get the length of the file using seek or another OS call you can use truncate to
trim the last char of the length of the file without rewriting the whole thing.
//This RESIZES the file without the need to REWRITE the whole thing:
struct stat fileStat;
stat("Filename", &fileStat);
status = truncate("Filename",fileStat.st_size-1);
The windows equivalent function is chsize however it requires opening a file first:
// Open a file
if( _sopen_s( &fh, "data", _O_RDWR | _O_CREAT, _SH_DENYNO,
_S_IREAD | _S_IWRITE ) == 0 )
{
if( ( result = _chsize( fh, _filelength( fh ) -1 ) ) == 0 )
printf( "Size successfully changed\n" );
else
printf( "Problem in changing the size\n" );
_close( fh );
}