4

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 ->
NateTheGreatt
  • 309
  • 1
  • 5
  • 17
  • Yes, but I'd suggest a higher level language. Perhaps python, which would make this ever so easy. How many files need to have this done? – JoshD Oct 12 '10 at 01:56
  • 2
    Are you sure that line actually exists and is not an artifact of your editor. If the last line with content is terminated with '\n' then an editor may display a blank last line even if that line contains zero data. – Martin York Oct 12 '10 at 02:27
  • I didn't even think of using '\n'. I was using endl. – NateTheGreatt Oct 12 '10 at 02:34

6 Answers6

3

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:

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.

Community
  • 1
  • 1
karlphillip
  • 92,053
  • 36
  • 243
  • 426
2
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;
}
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
1

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
}
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
1

C++11 or higher - removes trailing whitespaces (newlines, tabs, spaces, ...):

std::string readAndTrimTrailingSpaces(std::string const & filePath)
{
    std::ifstream file(filePath);
    std::string   buffer(std::istreambuf_iterator<char>{file}, {});

    while (!buffer.empty() && std::isspace(buffer.back()))
        buffer.pop_back();

    return buffer;
}
Chnossos
  • 9,971
  • 4
  • 28
  • 40
  • 1
    Apparently that second argument to `std::string` [can be shortened to `{}`](https://stackoverflow.com/a/116177/6243352). – ggorlen Mar 09 '23 at 20:31
0

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.

Donotalo
  • 12,748
  • 25
  • 83
  • 121
0

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.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252