0

編集:この問題の解決策は、以下のコメントで Ulrich Eckhardt によって提供されました。また、この問題には、重複の可能性で説明されているものとはまったく異なる原因と解決策がありました。詳細については、Ulrich Eckhardt のコメントを参照してください。

ここの専門家の助けを借りて、指定されたコード ページで Windows クリップボードの内容をテキスト ファイルに書き込むプログラムを作成することができました。テキスト ファイルの改行が 0d 0a ではなく 0d 0d 0a の 3 バイトであることを除いて、完全に機能するようになりました。これにより、テキストをワープロにインポートすると問題 (追加の行) が発生します。

テキスト ストリームで 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;
}
4

1 に答える 1

2

ここでのコメントは正しい方向に進んでいますが、より多くのコンテキストを提供し、長引く問題を指摘させてください.

さまざまな行末記号/区切り記号の規則があります。Unix 由来のシステムの多くは、すべての行の終わりに改行文字を使用します。ASCII では、'\x0A'. Windows や多くのネットワーク プロトコルなどの他のシステムでは、キャリッジ リターンとそれに続く行間の改行が使用されます。ASCII では、'\x0D' '\x0A'. (他にもスキームはありますが、はるかにまれです。)

テキストを読み書きするための C および C++ 入出力ライブラリは、これらの規則をユーザーから隠すことができるため、基礎となるプラットフォームが何であれ、「正しいこと」を行う 1 つの方法を正しくコーディングできます。

プログラミング規約は を使用すること'\n'です。基盤となるプラットフォームが ASCII または Unicode を使用している場合、これはほぼ間違いなく改行と同等です (ただし、改行文字を持たない EBCDIC を使用している場合はそうではありません)。ファイルに書き込むとき、ライブラリは をインターセプトし、'\n'プラットフォームが必要とする規則を設定します。たとえば、Linux マシンを使用している場合は、改行が出力されます ('\n'値は改行と同じであるため、基本的に何もしません)。'\n'Windows では、ライブラリはキャリッジ リターンとライン フィードをインターセプトして出力します。物事の入力側は反対のことをします。

Windows でクリップボードからテキストを取得する場合、どの規則が使用されているかはわかりません。Windows であるため、おそらく CR+LF を期待するでしょうが、クリップボードにテキストを配置する可能性のある多くのプログラムは、Windows では正しく動作しない可能性があります。

あなたの場合、クリップボードからのテキストには、実際に改行と行間の改行の両方があるようです。それをテキスト モードで出力すると、i/o ライブラリはキャリッジ リターンを出力し、次にライン フィード (それは a であると見なします'\n') を確認するため、別のキャリッジ リターンとそれに続くライン フィードを出力します。そのため、キャリッジ リターンが 2 倍になっています。

出力をバイナリ モードに切り替えると、ライブラリに「変換しない」ように指示されます'\n'。それで、それはあなたの差し迫った問題を解決します。

しかし、クリップボードのテキストの行間 (または行末) に改行しかない場合があるという問題がまだ残っています。それをバイナリモードで出力すると、キャリッジリターンが得られず、ファイルは技術的にプラットフォームが必要とする形式にはなりません。一部のプログラムはこれに対応しますが、メモ帳などの他のプログラムは対応しません。

詳細情報.

于 2015-06-29T21:35:12.740 に答える