16

Is it possible to increase the number of characters that the console app accepts for readln.

It seems to only allow you to type 254 characters

To Reproduce in Delphi

File > New > Other > Console Application

change the code to be as per below

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.

Paste this string in the running app ( its a 300 character string )

ABCDEFHIL1ABCDEFHIL2ABCDEFHIL3ABCDEFHIL4ABCDEFHIL5ABCDEFHIL6ABCDEFHIL7ABCDEFHIL8ABCDEFHIL9ABCDEFHI10ABCDEFHI11ABCDEFHI12ABCDEFHI13ABCDEFHI14ABCDEFHI15ABCDEFHI16ABCDEFHI17ABCDEFHI18ABCDEFHI19ABCDEFHI20ABCDEFHI21ABCDEFHI22ABCDEFHI23ABCDEFHI24ABCDEFHI25ABCDEFHI26ABCDEFHI27ABCDEFHI28ABCDEFHI29ABCDEFHI30

For me, it chops the string off at 254 characters

ABCDEFHIL1ABCDEFHIL2ABCDEFHIL3ABCDEFHIL4ABCDEFHIL5ABCDEFHIL6ABCDEFHIL7ABCDEFHIL8ABCDEFHIL9ABCDEFHI10ABCDEFHI11ABCDEFHI12ABCDEFHI13ABCDEFHI14ABCDEFHI15ABCDEFHI16ABCDEFHI17ABCDEFHI18ABCDEFHI19ABCDEFHI20ABCDEFHI21ABCDEFHI22ABCDEFHI23ABCDEFHI24ABCDEFHI25ABCD

enter image description here

timiTao
  • 1,417
  • 3
  • 20
  • 34
Dangas56
  • 849
  • 1
  • 8
  • 32
  • I have tested in XE5 and Tokyo 10.2.2 – Dangas56 Feb 14 '18 at 00:54
  • This is a limitation of the console window itself, not the RTL. `ReadLn()` runs a reading loop until it encounters a bare-LF, CRLF, or CR+EOF to stop reading. Even C#'s `Console.ReadLine()` maxes out at 254 characters by default, unless you up the buffer size manually. – Remy Lebeau Feb 14 '18 at 00:54
  • I was thinking if visual studio C# could get around the problem there must be a way in delphi https://stackoverflow.com/questions/5557889/console-readline-max-length – Dangas56 Feb 14 '18 at 00:56
  • 1
    That C# approach is redirecting `Console.ReadLine()` to read from a user-defined stream that is attached to STDIN and reads using a custom buffer. In Win32 API terms, that would basically be like calling `GetStdHandle(STD_INPUT_HANDLE)` and then calling `ReadFile()` on it (which does work, BTW, I just tested it and it read the entire 300 characters just fine). You could wrap that with `THandleStream` and `TStreamReader`, for instance. – Remy Lebeau Feb 14 '18 at 01:08

2 Answers2

21

AFAIK, you can't make the RTL's Readln() function accept more characters (though internally, it is coded to run a loop that should be able to handle more than 254 characters). It seems by default, when you paste your 300-char test string into the console window, it stops taking characters at 254 even before you press Enter.

But, you can use a different approach - call GetStdHandle(STD_INPUT_HANDLE) and then call ReadFile() on that HANDLE to read however much you want. If you use a buffer that is at least 300 bytes, it will happily accept your 300-char test string:

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.

You can then let the RTL handle that buffering for you, by wrapping the HANDLE in a THandleStream and TStreamReader (the latter lets you specify a buffer size - it defaults to 1024 bytes), eg:

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.
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
11

The RTL is flexible enough to override file device driver defaults. It even allows you to write your own driver, but for your needs all you need to do is to provide a buffer sufficient enough to hold your input.

Without any file parameter Readln uses the global Input so that's what you're going to modify:

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.
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169