Delphi控制台应用程序如何增加输入字符限制?

16

如何增加控制台应用程序中readln函数所接受的字符数?

目前该函数只允许最多输入254个字符。

在Delphi中复现:

文件 > 新建 > 其他 > 控制台应用程序,然后按照以下代码更改:

program Project3;

{$APPTYPE CONSOLE} 

{$R *.res}

uses
  System.SysUtils;

var MyTest : String;
begin
  try
    readln(MyTest);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

将此字符串粘贴到运行的应用程序中(这是一个300个字符的字符串)

ABCDEFHIL1ABCDEFHIL2ABCDEFHIL3ABCDEFHIL4ABCDEFHIL5ABCDEFHIL6ABCDEFHIL7ABCDEFHIL8ABCDEFHIL9ABCDEFHI10ABCDEFHI11ABCDEFHI12ABCDEFHI13ABCDEFHI14ABCDEFHI15ABCDEFHI16ABCDEFHI17ABCDEFHI18ABCDEFHI19ABCDEFHI20ABCDEFHI21ABCDEFHI22ABCDEFHI23ABCDEFHI24ABCDEFHI25ABCDEFHI26ABCDEFHI27ABCDEFHI28ABCDEFHI29ABCDEFHI30

对我来说,它将字符串截断为254个字符

ABCDEFHIL1ABCDEFHIL2ABCDEFHIL3ABCDEFHIL4ABCDEFHIL5ABCDEFHIL6ABCDEFHIL7ABCDEFHIL8ABCDEFHIL9ABCDEFHI10ABCDEFHI11ABCDEFHI12ABCDEFHI13ABCDEFHI14ABCDEFHI15ABCDEFHI16ABCDEFHI17ABCDEFHI18ABCDEFHI19ABCDEFHI20ABCDEFHI21ABCDEFHI22ABCDEFHI23ABCDEFHI24ABCDEFHI25ABCD

enter image description here


我已在XE5和Tokyo 10.2.2中进行了测试。 - Dangas56
这是控制台窗口本身的限制,而不是RTL的限制。 ReadLn()运行读取循环直到遇到裸LF,CRLF或CR + EOF以停止读取。即使是C#的 Console.ReadLine() 默认情况下也以254个字符为最大值,除非您手动增加缓冲区大小。 - Remy Lebeau
我在想如果Visual Studio C#可以解决这个问题,那么Delphi肯定也有办法。https://dev59.com/EW035IYBdhLWcg3wNdPg - Dangas56
1
这种 C# 方法是将 Console.ReadLine() 重定向到从附加到 STDIN 的用户定义流中读取,并使用自定义缓冲区进行读取。在 Win32 API 方面,这基本上相当于调用 GetStdHandle(STD_INPUT_HANDLE) 然后在其上调用 ReadFile()(顺便说一句,这确实有效,我刚刚测试了它并成功读取了全部 300 个字符)。例如,您可以使用 THandleStreamTStreamReader 进行包装。 - Remy Lebeau
2个回答

21
据我所知,你不能让RTL的Readln()函数接受更多字符(尽管在内部,它被编码为可以处理超过254个字符的循环)。似乎默认情况下,当将300个字符的测试字符串粘贴到控制台窗口时,甚至在按Enter键之前,它就停止接受字符了。
但是,你可以使用不同的方法-调用GetStdHandle(STD_INPUT_HANDLE),然后在该HANDLE上调用ReadFile()来读取任意数量的字符。如果使用一个至少为300字节的缓冲区,它将愉快地接受你的300个字符的测试字符串:
program Project3;

{$APPTYPE CONSOLE} 

{$R *.res}

uses
  System.SysUtils, Winapi.Windows;

var
  buf : array[0..299] of AnsiChar;
  MyTest: AnsiString;//string;
  hStdIn: THandle;
  dwNumRead: DWORD;
begin
  try
    //Readln(MyTest);
    hStdIn := GetStdHandle(STD_INPUT_HANDLE);
    ReadFile(hStdIn, buf, sizeof(buf), dwNumRead, nil);
    SetString(MyTest, buf, dwNumRead);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

您可以通过将HANDLE包装在THandleStreamTStreamReader中(后者允许您指定缓冲区大小-默认为1024字节),然后让RTL为您处理缓冲,例如:

program Project3;

{$APPTYPE CONSOLE} 

{$R *.res}

uses
  System.SysUtils, Winapi.Windows, System.Classes;

var
  MyTest : String;
  strm: THandleStream;
  reader: TStreamReader;
begin
  try
    //Readln(MyTest);
    strm := THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE));
    try
      reader := TStreamReader.Create(strm);
      try
        MyTest := reader.ReadLine;
      finally
        reader.Free;
      end;
    finally
      strm.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

11

RTL可以灵活地覆盖文件设备驱动程序的默认设置。它甚至允许您编写自己的驱动程序,但对于您的需求,您只需要提供一个足够大的缓冲区来容纳输入即可。

如果没有任何file参数,Readln将使用全局的Input,因此这就是您要修改的内容:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

var MyTest : String;
    Buff: array[0..511] of Char;
begin
  try
    TTextRec(Input).BufSize := SizeOf(Buff);
    TTextRec(Input).BufPtr := @Buff;
    ReadLn(MyTest);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接