Is there a way in C++ to remove/trim a trailing new line from a text file?
For example
content content
content content
content content
<- this line in the text file is empty and needs to go ->
Is there a way in C++ to remove/trim a trailing new line from a text file?
For example
content content
content content
content content
<- this line in the text file is empty and needs to go ->
You need to read all the contents from file, and write the contents again in such a way that no empty line exists, or the way you want.
Sure! One way to do it would be to read the file to a std::string
#include <fstream>
#include <string>
// Add this code inside your main() function
std::ifstream ifs("filename.txt");
std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
and then use any of the techniques described here:
http://stackoverflow.com/questions/1488775/c-remove-new-line-from-multiline-string
then you could overwrite the file with the new result. Of course, this approach ain't practical when dealing with very large files (let's say, 2GB) but such thing is not a constraint according to your original question.
This thread also has great material on detecting new lines.
ifstream fin("input.txt");
vector<string> vs;
string s;
while(getline(fin,s))
vs.push_back(s);
fin.close();
ofstream fout("input.txt");
for(vector<string>::iterator it = vs.begin(); it != vs.end(); ++it)
{
if(it != vs.begin())
fout << '\n';
fout << *it;
}
You can create a simple filter, applied as in:
remove_empty_last_line < input.txt > output.txt
Or, you can create your own file input stream ala:
#include <fstream>
std::ifstream myin(filename);
Then, the code would resemble (untested)...
char c, d, e;
if (cin.get(c))
if (cin.get(d))
{
while (cin.get(e))
{
cout << d;
c = d;
d = e;
}
if (c != '\n' || d != '\n')
cout << d;
}
else
cout << c;
(Substitute myin for cin if desired, then myin.close()). No need to use std::strings for something so simple: they just slow everything down. One of the great strengths of C (and hence C++) is being able to efficiently process data a character at a time.
The most efficient method would be to seek to the end of the file and move the end-of-file pointer backwards. Unfortunately this is not portable because there is no standard way of setting the end-of-file pointer in either the C or C++ standard libraries. You need to use a platform-specific function such as SetEndOfFile
on Windows or ftruncate
on POSIX. For example:
void RemoveFinalNewline(const char *filename)
{
#if defined(_WIN32)
HANDLE hFile = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)
; // handle error
LARGE_INTEGER fileSize;
if(GetFileSizeEx(hFile, &fileSize) == 0)
; // handle error
if(fileSize.QuadPart < 2)
; // this case is left as an exercise to the reader
LARGE_INTEGER newFilePtr;
newFilePtr.QuadPart = -2;
if(SetFilePointerEx(hFile, &newFilePtr, NULL, FILE_END) == 0)
; // handle error
char lastTwoBytes[2];
if(ReadFile(hFile, lastTwoBytes, 2, NULL, NULL) == 0)
; // handle error
if(lastTwoBytes[1] == '\n')
{
fileSize.QuadPart--;
if(lastTwoBytes[0] == '\r')
fileSize.QuadPart--;
if(SetFilePointerEx(hFile, &fileSize, NULL, FILE_BEGIN) == 0)
; // handle error
if(SetEndOfFile(hFile) == 0)
; // handle error
// Success!
}
// else the file didn't end in a newline
CloseHandle(hFile); // and we're done
#else // POSIX case; the non-Windows, non-POSIX case is left as an exercise
int fd = open(filename, O_RDWR);
if(fd == -1)
; // handle error
off_t fileSizeMinus1 = lseek(fd, -1, SEEK_END);
if(fileSizeMinus1 == (off_t)-1)
; // handle error
// We're assuming that a newline is a bare LF '\n' here. The CRLF case
// is left as an exercise (hint: see the Windows case above)
char lastChar;
if(read(fd, &lastChar, 1) != 1)
; // handle error
if(lastChar == '\n')
{
if(ftruncate(fd, fileSizeMinus1) == -1)
; // handle error
// else success!
}
// else the file does not end in a newline
close(fd); // and we're done
#endif
}