[Solved] Save a text file with Windows/Dos line endings but with preserving encoding in QT.



  • I have a QT application that can process text files. One of the features that I am trying to implement is to allow the user to save the text files in ASCii, UTF-8, UTF-16LE, or UTF-16BE. I developed the application on Linux (specifically on a Debian 9 VM), and was able to use it just fine on Linux. It works perfectly. Using Emacs to verify the encodings, everything is great.

    At first, I simply cross compiled the application on the Linux machine for Windows. It all seemed to work just fine on Windows at first, but then I noticed that all of the text files (while preserving their encoding just fine), would print out the entire file in one line when opened in something simple like Notepad.

    Emacs in Windows verified what I thought was the problem, and that is that QT is saving to the file with "Unix" style line endings instead of Windows/Dos style line endings.

    I thought that installing QT on the Windows machine and building the project within Windows would solve the problem, but the problem persists.

    I know that there are simple programs such as unix2dos that can transfer the files to a Windows/Dos style line ending, but after doing some research, it is evident that those programs will convert the file back to "ASCii" after doing the line ending conversion.

    I discovered this post https://forum.qt.io/topic/42464/qplaintextedit-eats-line-endings, but nothing on there seemed to work. I think that my main hangup with the solutions offered here, is writing to the file in byte mode with QFile. I am not understanding the problem here because according to one of the solutions, I should perform the replacement of "\n" with "\r\n" and then save to the file in byte (or binary) mode. After reading the documentation for QFile, it is unclear to me how to save to the file in binary mode.

    The code that I am using to open the files is here.

    void MainWindow::on_actionOpen_triggered()
    {
      QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("All Files (*.*);;Text Files (*.txt);;C++ Files (*.cpp *.h)"));  //Opens the QDialog for opening files.
      saveFile =fileName;       //Sets the value of "saveFile" to the value of "fileName." This insures that any time a "Save" function is performed, the file that the text is saved to, is the one that is intended.
        MainWindow::setWindowTitle(saveFile);
        if(!fileName.isEmpty()) // As long as the file name variable is not empty, this will trigger.
        {
            QFile file(fileName);
            if(!file.open(QIODevice::ReadOnly)) // If the file could not be opened, it will trigger this error message.
            {
                QMessageBox::critical(this, tr("Error"), tr("Could not open file"));
                            return;
            }
            QTextStream in(&file);      //This sets the variable "in" to be the contents of the file.
        ui->textEdit->setText(in.readAll());    //This sets the text that is in the text edit field to be the contents of the file.
        file.close();       //This closes the file so that no memory errors are caused by having too many files open.
        }
    }
    

    The code that I am using to save the files is here

    void mainwindow::on_actionSave_triggered()
    {
        if (!saveFile.isEmpty())
        {
          //"saveFile" is populated with the file name when the file is first opened.
            QFile file(saveFile); 
            if (!file.open(QIODevice::WriteOnly))
            {
                qDebug() << "Could not find file."
    
            }
            else
            {
          //The following if statements will save the file in the proper encoding dependant on the value of "encodeString."
                QTextStream stream(&file)
                if (encodeString == "Plain Text")
                {
                    stream << ui->textEdit->toPlainText();
                    stream.flush();
                    file.close();
                }
    
                if (encodeString == "UTF-8")
                {
                    stream.setCodec("UTF-8");
                    stream.setGenerateByteOrderMark(true);
                    stream << ui->textEdit->toPlainText();
                    stream.flush();
                    file.close();
                }
    
                if (encodeString == "UTF-16")
                {
                    stream.setCodec("UTF-16");
                    stream.setGenerateByteOrderMark(true);
                    stream << ui->textEdit->toPlainText();
                    stream.flush();
                    file.close();
                }
    
                if (encodeString == "UTF-16BE")
                {
                    stream.setCodec("UTF-16BE");
                    stream.setGenerateByteOrderMark(true);
                    stream << ui->textEdit->toPlainText();
                    stream.flush();
                    file.close();
                }
    
                if (encodeString == "UTF-32")
                {
                    stream.setCodec("UTF-32");
                    stream.setGenerateByteOrderMark(true);
                    stream << ui->textEdit->toPlainText();
                    stream.flush();
                    file.close();
                }
    
                if (encodeString == "UTF-32BE")
                {
                    stream.setCodec("UTF-32BE");
                    stream.setGenerateByteOrderMark(true);
                    stream << ui->textEdit->toPlainText();
                    stream.flush();
                    file.close();
                }
    
    
            }
        }
    
    
    }
    

    I would like to either be able to

    • Save the file using standard QT tools to keep the encoding, but use the Windows line endings (I believe this is the ideal solution.)

    • Or Alternatively, if it could be figured out how to write to the file using standard library (codecvt, etc) to handle the encodings. I have tried that and had problems with it before moving the project to QT. I chose QT becuause it is easy to save the codec of a stream with "QTextStream.setCodec("codec")." The QT documentation for QFile states that there is a problem with Unix line endings being saved to Windows files and suggests using the standard "fstream" instead. It looks like a huge headache to write to different encodings using standard library. though. Any help is appreciated.



  • @CigEmacs

    After reading the documentation for QFile, it is unclear to me how to save to the file in binary mode.

    Your QFile.open(QIODevice::WriteOnly) is already opening the file in "binary" mode, because you are not passing QIODevice::Text.

    But (presumably) your QTextStream wrapper is putting in whatever you see in the way of "line endings"? Make sure you have indeed stripped any \r or \n off whatever you get back from the plain text edit. You need to find out where the line endings you are talking about are coming from.

    EDIT: Actually from what I can see your QTextStream << it will not add any implicit line endings/conversions. So check what's at the end of the string you pass to it from the text edit? Make sure it has just whatever it is you want that you do want output. Use explicit "\r\n", do not use C++ endl.



  • The text is coming from "ui->textEdit->toPlainText();" I am not using either explicit "\r\n" or "endl" in theory it should just be saved with whatever the line ending is in the text edit field. I'm double checking the documentation for QTextEdit now to see if there is anything about line endings. I tried the method mentioned in the other post that I linked to of doing the following.

    if (lineEndingType == WINDOWS)
    {
    Text.replace("\n", "\r\n");
    Text.replace("\r\r\n", "\r\n");
    }

    but this does not seem to be working with the steam.



  • if you save text use text mode on the device: if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) and if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) everything else comes for free



  • I actually got it figured out. As usual, I was making things more complicated than they needed to be. The suggestion from the previous QT forum post that I mentioned plus a tweak helped me save the file with the proper encoding and "line ending style." I don't know why I missed this earlier. Here is the fixed code. I didn't bother putting an "if" function for the line ending style because I know that this is going to be used solely on Windows.

    void MainWindow::on_actionSave_triggered() // Saves the file
    {
    if (!saveFile.isEmpty())
    {
    QString wSave;
    QFile file(saveFile);
    wSave = ui->textEdit->toPlainText();
    wSave.replace("\n", "\r\n");
    wSave.replace("\r\r\n", "\r\n");
    if (!file.open(QIODevice::WriteOnly)) // This if statement makes sure that the file can be written to.
    {
    // error message

        }
        else
        {
      QTextStream stream(&file);        // Prepares the file to receive the QTextStream
      if (encodeString == "Plain Text")        //The next set of "if" statements set the encoding that the file will be saved in. This is set by the combo box in the text editor.
            {
    
                qDebug() << wSave;
                stream << wSave;
                stream.flush();
                file.close();
            }
    
            if (encodeString == "UTF-8")
            {
    
                stream.setCodec("UTF-8");
                stream.setGenerateByteOrderMark(true);
                stream << wSave;
                stream.flush();
                file.close();
            }
    
            if (encodeString == "UTF-16")
            {
                stream.setCodec("UTF-16");
                stream.setGenerateByteOrderMark(true);
                stream << wSave;
                stream.flush();
                file.close();
            }
    
            if (encodeString == "UTF-16BE")
            {
                stream.setCodec("UTF-16BE");
                stream.setGenerateByteOrderMark(true);
                stream << wSave;
                stream.flush();
                file.close();
            }
    
            if (encodeString == "UTF-32")
            {
                stream.setCodec("UTF-32");
                stream.setGenerateByteOrderMark(true);
                stream << wSave;
                stream.flush();
                file.close();
            }
    
            if (encodeString == "UTF-32BE")
            {
                stream.setCodec("UTF-32BE");
                stream.setGenerateByteOrderMark(true);
                stream << wSave;
                stream.flush();
                file.close();
            }
    
    
        }
    }
    

    }



  • @VRonin said in Save a text file with Windows/Dos line endings but while preserving encoding in QT.:

    if you save text use text mode on the device: if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) and if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) everything else comes for free

    I thought the whole point of his question is precisely that he wants to produce text files which are not native to the OS, specifically he wants Windows \r\n regardless of whether his app is running under Win or Linux. That was why I was saying don't use endl etc., and of course you mustn't use QIODevice::Text. Maybe I misunderstood... :)



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.