开发者

C++ remove trailing new line from text file

Is there a way in C++ to remove/trim a trailing new line from a text file?

For example

content content
content content
content cont开发者_如何学JAVAent
<- this line in the text file is empty and needs to go ->


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.


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;
}


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
}


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.


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.


C++11 or higher - removes trailing spaces (newline, tab, spaces, ...):

std::ifstream file("filename.ext");
std::string buffer(std::istreambuf_iterator<char>{file},
                   std::istreambuf_iterator<char>{});

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜