C++ (VC) Текстовый вывод разбивает строки с 0d 0d 0a вместо 0d 0a - как это исправить?

РЕДАКТИРОВАТЬ: Решение этой проблемы было предоставлено Ульрихом Экхардтом в комментариях ниже. Кроме того: эта проблема имела совершенно другую причину и решение, чем те, которые описаны в возможных дубликатах. Снова, см. Комментарий Ульриха Экхардта для деталей.

С помощью экспертов здесь мне удалось собрать программу, которая записывает содержимое буфера обмена Windows в текстовый файл на указанной кодовой странице. Теперь он, кажется, работает отлично, за исключением того, что разрывы строк в текстовом файле составляют три байта - 0d 0d 0a - вместо 0d 0a - и это вызывает проблемы (дополнительные строки), когда я импортирую текст в текстовый процессор.

Есть ли простой способ заменить 0d 0d 0a на 0d 0a в текстовом потоке, или я должен что-то делать по-другому в моем коде? Я не нашел ничего подобного в другом месте. Вот код:

#include <stdafx.h>
#include <windows.h>
#include <iostream>
#include <fstream>
#include <codecvt> // for wstring_convert
#include <locale>  // for codecvt_byname
using namespace std;

void BailOut(char *msg)
{
    fprintf(stderr, "Exiting: %s\n", msg);
    exit(1);
}

string ExePath()
{
    char buffer[MAX_PATH];
    GetModuleFileNameA(NULL, buffer, MAX_PATH);
    string::size_type pos = string(buffer).find_last_of("\\/");
    return string(buffer).substr(0, pos);
}

// get output code page from command-line argument; use 1252 by default
int main(int argc, char *argv[])
{
    string codepage = ".1252";

    if (argc > 1) {
        string cpnum = argv[1];
        codepage = "." + cpnum;
    }

    // HANDLE clip;
    string clip_text = "";

    // exit if clipboard not available
    if (!OpenClipboard(NULL))
    { BailOut("Can't open clipboard"); }

    if (IsClipboardFormatAvailable(CF_TEXT)) {
        HGLOBAL hglb = GetClipboardData(CF_TEXT);

        if (hglb != NULL) {
            LPSTR lptstr = (LPSTR)GlobalLock(hglb);

            if (lptstr != NULL) {
                // read the contents of lptstr which just a pointer to the string:
                clip_text = (char *)hglb;
                // release the lock after you're done:
                GlobalUnlock(hglb);
            }
        }
    }

    CloseClipboard();

    // create conversion routines
    typedef std::codecvt_byname<wchar_t, char, std::mbstate_t> codecvt;
    std::wstring_convert<codecvt> cp1252(new codecvt(".1252"));
    std::wstring_convert<codecvt> outpage(new codecvt(codepage));

    std::string OutFile = ExePath() + "\\#clip.txt"; // output file name

    ofstream OutStream;  // open an output stream
    OutStream.open(OutFile, ios::out | ios::trunc);

    // make sure file is successfully opened
    if (!OutStream) {
        cout << "Error opening file " << OutFile << " for writing.\n";
        return 1;
    }

    // convert to DOS/Win codepage number in "outpage"
    OutStream << outpage.to_bytes(cp1252.from_bytes(clip_text)).c_str();
    //OutStream << endl;
    OutStream.close(); // close output stream
    return 0;
}

1 ответ

Решение

Комментарии здесь находятся на правильном пути, но позвольте мне предоставить больше контекста и указать на давнюю проблему.

Существуют различные соглашения об ограничении / разделении строк. Многие производные от Unix системы используют символ перевода строки в конце каждой строки. В ASCII это '\x0A', Другие системы, такие как Windows и многие сетевые протоколы, используют возврат каретки с последующим переводом строки между строками. В ASCII это '\x0D' '\x0A', (Есть и другие схемы, но они гораздо реже.)

Библиотеки ввода / вывода C и C++ для чтения и записи текста могут скрывать эти соглашения от вас, так что вы можете исправить код одним способом, который делает "правильные вещи" на любой базовой платформе.

Соглашение о программировании заключается в использовании '\n', что почти наверняка эквивалентно переводу строки, если ваша базовая платформа использует ASCII или Unicode (но не если она использует EBCDIC, который не имеет символа перевода строки). При записи в файл библиотека будет перехватывать '\n' и поместите любое соглашение, которое требует ваша платформа. Например, если вы работаете на компьютере с Linux, он выведет перевод строки (и так как '\n' имеет то же значение, что и перевод строки, в основном это неоперация). В Windows библиотека будет перехватывать '\n' и вывести возврат каретки и перевод строки. Входная сторона вещей делает обратное.

Когда вы получаете текст из буфера обмена в Windows, вы не знаете, какое соглашение он использует. Поскольку это Windows, вы, вероятно, ожидаете CR+LF, но многие программы, которые могут помещать текст в буфер обмена, могут не работать должным образом в Windows.

В вашем случае кажется, что текст из буфера обмена действительно имеет как возврат каретки, так и перевод строки между строками. Когда вы затем выводите это в текстовом режиме, библиотека ввода-вывода выводит возврат каретки, а затем видит перевод строки (который она считает '\n'), и поэтому он выводит другой возврат каретки, за которым следует перевод строки. Вот почему вы видите удвоение возвратов каретки.

Переключение вывода в двоичный режим говорит библиотеке "не конвертировать" '\n'Итак, это решает вашу непосредственную проблему.

Но по-прежнему существует проблема, заключающаяся в том, что текст буфера обмена иногда может содержать только переводы строк между (или в конце) строками. Если вы выводите это в двоичном режиме, вы не получите возврат каретки, и файл технически не будет в том формате, который требуется вашей платформе. Некоторые программы справятся с этим, а другие, например Notepad, не справятся.

Больше информации

Другие вопросы по тегам