15

コンソールの特定の場所、たとえば5,5からテキストを読む必要があります。

この場所に書き込む必要がある場合は、次のようになります。

Console.SetCursorPosition(5, 5);
Console.Write("My text");

同様の方法で読むことができる方法はありますか?

明確にするために:私はユーザーからの入力を受け取るのをやめたくありません。入力がユーザーからのものではなく、以前に印刷されたものである可能性もあります。私は文字通り、Console.GetCharAtLocation(5,5)などのようなものが必要です。

4

6 に答える 6

16

これは、現在コンソールバッファー(ウィンドウではなくバッファー)にあるものを読み取ることができるC#コードユーティリティです。

使用例:

class Program
{
    static void Main(string[] args)
    {
        // read 10 lines from the top of the console buffer
        foreach (string line in ConsoleReader.ReadFromBuffer(0, 0, (short)Console.BufferWidth, 10))
        {
            Console.Write(line);
        }
    }
}

効用:

public class ConsoleReader
{
    public static IEnumerable<string> ReadFromBuffer(short x, short y, short width, short height)
    {
        IntPtr buffer = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(typeof(CHAR_INFO)));
        if (buffer == null)
            throw new OutOfMemoryException();

        try
        {
            COORD coord = new COORD();
            SMALL_RECT rc = new SMALL_RECT();
            rc.Left = x;
            rc.Top = y;
            rc.Right = (short)(x + width - 1);
            rc.Bottom = (short)(y + height - 1);

            COORD size = new COORD();
            size.X = width;
            size.Y = height;

            const int STD_OUTPUT_HANDLE = -11;
            if (!ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE), buffer, size, coord, ref rc))
            {
                // 'Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            IntPtr ptr = buffer;
            for (int h = 0; h < height; h++)
            {
                StringBuilder sb = new StringBuilder();
                for (int w = 0; w < width; w++)
                {
                    CHAR_INFO ci = (CHAR_INFO)Marshal.PtrToStructure(ptr, typeof(CHAR_INFO));
                    char[] chars = Console.OutputEncoding.GetChars(ci.charData);
                    sb.Append(chars[0]);
                    ptr += Marshal.SizeOf(typeof(CHAR_INFO));
                }
                yield return sb.ToString();
            }
        }
        finally
        {
            Marshal.FreeHGlobal(buffer);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct CHAR_INFO
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] charData;
        public short attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct COORD
    {
        public short X;
        public short Y;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct SMALL_RECT
    {
        public short Left;
        public short Top;
        public short Right;
        public short Bottom;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct CONSOLE_SCREEN_BUFFER_INFO
    {
        public COORD dwSize;
        public COORD dwCursorPosition;
        public short wAttributes;
        public SMALL_RECT srWindow;
        public COORD dwMaximumWindowSize;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadConsoleOutput(IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetStdHandle(int nStdHandle);
}
于 2012-09-11T09:07:27.143 に答える
11

画面上の指定された位置から単一の文字を読み取るためにWindows10で機能する簡略化されたデモ。(X, Y).NET4.7.2でテスト済み

まず、コンソールにデモグリッドを入力するコード行を次に示します。デモを機能させるには、画面の左上隅にレンダリングする必要があることに注意してください。

static void Populate_Console()
{
    Console.Clear();
    Console.Write(@"
 ┌───────┐
1│C D E F│
2│G H I J│
3│K L M N│
4│O P Q R│
 └───────┘
  2 4 6 8          ".Trim());
}

次のようになります。

ここに画像の説明を入力してください

それでは、いくつかの文字を読み返してみましょう。開始するには、 stdoutのネイティブコンソールハンドルが必要です。Win32から取得するためのP/Invokeメソッドは次のとおりです。

[DllImport("kernel32", SetLastError = true)]
static extern IntPtr GetStdHandle(int num);

さて、クールな部分です。ReadConsoleOutputCharacterこれは、 Win32関数を使用するこのページのこれまでのところ唯一の答えのようです。文字の色属性を取得することはできませんが、このアプローチでは、長方形のコピーを処理したり、CreateConsoleScreenBuffer画面バッファーを割り当ててそれらの間でコピーしたりする手間を省くことができます。

AnsiUnicodeのバージョンは別々にあり、コンソールウィンドウでアクティブになっているコードページに応じて適切なバージョンを呼び出す必要があります。ここでは両方のP/Invoke署名を示していますが、簡単にするために、例ではAnsiバージョンを続行します。

    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
    [return: MarshalAs(UnmanagedType.Bool)] //   ̲┌──────────────────^
    static extern bool ReadConsoleOutputCharacterA(
        IntPtr hStdout,   // result of 'GetStdHandle(-11)'
        out byte ch,      // A̲N̲S̲I̲ character result
        uint c_in,        // (set to '1')
        uint coord_XY,    // screen location to read, X:loword, Y:hiword
        out uint c_out);  // (unwanted, discard)

    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)] //   ̲┌───────────────────^
    static extern bool ReadConsoleOutputCharacterW(
        IntPtr hStdout,   // result of 'GetStdHandle(-11)'
        out Char ch,      // U̲n̲i̲c̲o̲d̲e̲ character result
        uint c_in,        // (set to '1')
        uint coord_XY,    // screen location to read, X:loword, Y:hiword
        out uint c_out);  // (unwanted, discard)

一度に1文字のみをフェッチするように設計されたサンプルコードの目的に必要な最小限に、これらのマーシャリングを削除したことに気付くかもしれません。したがって、マネージポインタ宣言'<code> outbytech'および'<code>out Char ch'により、これc_inは常にである必要があります。1

本当に必要なのはそれだけです。上記のように適切なP/Invoke関数を呼び出すことは、1文字の読み取りに制限している場合、ほとんどの場合自明です。これを簡単な例で示すためにConsole、上に描いたグリッドの対角線に沿って、から4文字を読み取るかわいいデモプログラムで終了します。

static void Windows_Console_Readback()
{
    var stdout = GetStdHandle(-11);

    for (uint coord, y = 1; y <= 4; y++)
    {
        coord = (5 - y) * 2;        // loword  <-- X coord to read
        coord |= y << 16;           // hiword  <-- Y coord to read

        if (!ReadConsoleOutputCharacterA(
                stdout,
                out byte chAnsi,    // result: single ANSI char
                1,                  // # of chars to read
                coord,              // (X,Y) screen location to read (see above)
                out _))             // result: actual # of chars (unwanted)
            throw new Win32Exception();

        Console.Write(" " + (Char)chAnsi + " ");
    }
}

そして、あなたはそれを持っています...

ここに画像の説明を入力してください

于 2018-06-26T11:28:16.610 に答える
9

この機能は存在しません。理論的には、コンソールの入力ストリームと出力ストリームをオーバーライドして、読み取り可能なコンソールバッファーの独自のコピーを保持することは可能ですが、それは重要です(おそらく、このようなすべてのエッジケースをサポートできませんでした)。コンソールに接続して読み取り/書き込みを行う外部プログラムとして)。

于 2012-09-10T16:09:35.490 に答える
1

それを忘れてください、あまりにも多くの問題、あなたはバッファから読んで、現在のすべてのコンソール出力を得ることができました、しかしそれは多すぎるでしょう。

私の提案は、ConsoleWriter委任を作成することです。方法を選択すると、クラスまたは単なる静的メソッドになります。このライターは、プロパティの最後の行を保持するため、Console.WriteLineを実行するたびに、委任と呼ぶものを使用します。実装し、最後にConsole.WriteLineを呼び出します。

于 2020-12-16T18:41:29.303 に答える
0

どうですか:

class Program {
    static void Main( string[ ] args ) {
        CustomizedConsole.WriteLine( "Lorem Ipsum" ); //Lorem Ipsum
        Console.WriteLine( CustomizedConsole.ReadContent( 6, 5 ) ); //Ipsum
        Console.WriteLine( CustomizedConsole.GetCharAtLocation( 0, 0 ) ); //L
    }
}

static class CustomizedConsole {
    private static List<char> buffer = new List<char>();
    private static int lineCharCount = 0;

    public static void Write(string s){
        lineCharCount += s.Length;
        buffer.AddRange( s );
        Console.Write( s );
    }

    public static void WriteLine(string s ) {
        for ( int i = 0; i < Console.BufferWidth - lineCharCount - s.Length; i++ )
            s += " ";

        buffer.AddRange( s );
        Console.WriteLine( s );
        lineCharCount = 0;
    }

    public static string ReadContent( int index, int count ) {
        return new String(buffer.Skip( index ).Take( count ).ToArray());
    }

    public static char GetCharAtLocation( int x, int y ) {
        return buffer[ Console.BufferHeight * x + y ];
    }
}

編集 :

他の人が言ったように、これは改善すべき他のことがたくさんある些細なケースです。しかし、私はこれを出発点としてのみ書きました。

于 2012-09-10T16:45:16.890 に答える
0

@Servyが述べているように、あなたが望むことを実行できる組み込み機能(私が知っている、または見つけることができる)はありません。ただし、回避策があります(少しハックですが、機能しました)。

メモリまたはディスクに独自のバッファを作成できます。コンソールに出力するときはいつでも、バッファにも出力してください。その後、バッファを使用して、コンソールでは実行できなかった方法で読み取りを行うことができます。

バッファリングするには、ディスク上またはメモリ内の2つの方法があります。Console.BufferWidthおよびプロパティを使用Console.BufferHeightして、バッファサイズを確認できます。文字列の配列を使用してメモリ内でこれを行う方が簡単であることがわかりました(各文字列は出力の行であり、BufferHeight正しく覚えていれば、配列にはに等しい数の文字列がありました)。同僚がディスク上で同じことをすることになりました。

Console.Writeとを置き換えるメソッドを作成して、Console.WriteLine両方のバッファーに同時に書き込むことができるようにする必要があります。何かのようなもの:

public void MyWrite( string output ) {
    Console.Write( output );
    Array.Write( output );  // obvious pseudo-code
}

配列の周りにクラスをラップし、それをサポートするメソッドを実装すると便利だと思いました...その後、GetCharAtLocation( int i, int j )メソッドや、そこで必要なその他の機能を実装できます。

于 2012-09-10T16:46:28.910 に答える