1 (изменено: Freeman, 01.07.2020 в 14:43)

Тема: Виртовский ввод-вывод

Вот, к примеру, так можно теперь делать

WriteLn('My first program', ' ', 100500, 42:10);

Добавлено 2020-06-26 в 18:29

Теперь процедуры Write\WriteLn стали стандартными.
При их использовании появился один нюанс.
Если мы хотим вывести значение переменной типа LongWord,
то компилятор использует _Write0Int64.
Это логично, потому что есть функции для вывода Char, String, LongInt, но вот для вывода LongWord функции нет.
Поэтому компилятор его расширяет до Int64 и использует соответствующую процедуру.

Проблема в том, что консоль в данный момент не поддерживает вывод 64-битных чисел.
То есть, похоже, придётся сделать свою реализацию вывода таких чисел.

Freeman пишет:

AssignFile/Reset/Rewrite

А для файлов это потребуется.

Когда мы делаем

var
  F: File of AnsiChar;
  MyVar: AnsiChar;

begin
    AssignFile(F, 'myfile.txt');
    Rewrite(F);
    Reset(F);
    Read(F, MyVar);
    Write(F, MyVar);
    CloseFile(F);
end.

то вызывается

function _Assign(var t: TTextRec; const s: String): Integer;
begin
  FillChar(t, sizeof(TFileRec), 0);
  t.BufPtr := @t.Buffer;
  t.Mode := fmClosed;
  t.Flags := tfCRLF * Byte(DefaultTextLineBreakStyle);
  t.BufSize := sizeof(t.Buffer);
  t.OpenFunc := @TextOpen;
  Move(S[1], t.Name, Length(s));
  t.Name[Length(s)] := #0;
  Result := 0;
end;

по поводу TFileRec и TTextRec написано, что они "must overlay"

type
{ Typed-file and untyped-file record }

  TFileRec = packed record (* must match the size the compiler generates: 332 bytes *)
    Handle: Integer;
    Mode: Word;
    Flags: Word;
    case Byte of
      0: (RecSize: Cardinal);   //  files of record
      1: (BufSize: Cardinal;    //  text files
          BufPos: Cardinal;
          BufEnd: Cardinal;
          BufPtr: PChar;
          OpenFunc: Pointer;
          InOutFunc: Pointer;
          FlushFunc: Pointer;
          CloseFunc: Pointer;
          UserData: array[1..32] of Byte;
          Name: array[0..259] of Char; );
  end;

{ Text file record structure used for Text files }
  PTextBuf = ^TTextBuf;
  TTextBuf = array[0..127] of Char;
  TTextRec = packed record (* must match the size the compiler generates: 460 bytes *)
    Handle: Integer;       (* must overlay with TFileRec *)
    Mode: Word;
    Flags: Word;
    BufSize: Cardinal;
    BufPos: Cardinal;
    BufEnd: Cardinal;
    BufPtr: PChar;
    OpenFunc: Pointer;
    InOutFunc: Pointer;
    FlushFunc: Pointer;
    CloseFunc: Pointer;
    UserData: array[1..32] of Byte;
    Name: array[0..259] of Char;
    Buffer: TTextBuf;
  end;

ещё нюанс "must match the size the compiler generates"
для файлов

 Name: array[0..259] of Char;

при этом константа для винды

MAX_PATH                 = 260;

а для линукса

MAX_PATH      = 4095;

но в TFileRec\TTextRec имя не может быть больше 260 символов, а в KolibriOS на длину пути таких ограничений нет, многие используют 1024 байта под буфер для файлового пути.

Как планируется решить такой вопрос у нас в SDK для KolibriOS?
Как планируется реализовать такие функции?   

    AssignFile
    Rewrite
    Reset
    Read
    Write
    CloseFile

2

Re: Виртовский ввод-вывод

0CodErr пишет:

но в TFileRec\TTextRec имя не может быть больше 260 символов, а в KolibriOS на длину пути таких ограничений нет, многие используют 1024 байта под буфер для файлового пути.

Я предупреждал. Этот код был написан еще для работы с FCB в DOS, если помнишь, что это такое. Идеология осталась.

0CodErr пишет:

Как планируется реализовать такие функции?

    AssignFile
    Rewrite
    Reset
    Read
    Write
    CloseFile

Не знаю. Я не буду их реализовывать. Делать виртовский ввод-вывод было твоей идеей.

3

Re: Виртовский ввод-вывод

Freeman пишет:
0CodErr пишет:

Как планируется реализовать такие функции?

    AssignFile
    Rewrite
    Reset
    Read
    Write
    CloseFile

Не знаю. Я не буду их реализовывать. Делать виртовский ввод-вывод было твоей идеей.

Я почему-то думал, что это как раз ты и хотел сделать стандартный SDK для Delphi.
А в моих исходниках обычно под виндой WinApi используется.
Но всё равно должно же быть какое-то API.
Или про кроссплатформенность речь не идёт?
Файловые функции будут только из модуля KolibriOS?
Это всё, конечно, усложнит возможность переносить готовый код.
С другой стороны, есть и другие более важные задачи: менеджер памяти, строки, ООП.
Так что, да, лучше, думаю, обойдёмся пока без этих функций.
Но никто не мешает добавить их потом, это может сделать кто-то ещё кроме нас.

Добавлено 2020-07-20 в 12:50

Так как в XD Pascal эти стандартные функции ввода\вывода уже реализованы, и, к  тому же, XD Pascal теперь работает в самой KolibriOS и компилирует сам себя, то, думаю, что и для Delphi можно сделать реализацию аналогичным образом, кое-что, возможно, даже можно скопировать "как есть".

4

Re: Виртовский ввод-вывод

0CodErr пишет:

Так как в XD Pascal эти стандартные функции ввода\вывода уже реализованы

Я решил взять паузу и сначала решить, как будут реализованы эти функции в Халва-Паскале. SDK для Колибри теперь будет развиваться с оглядкой на Халва-Паскаль.

5

Re: Виртовский ввод-вывод

Реализовал некоторые стандартные файловые возможности.

Список функций
AssignFile     —  Assigns the name of an external file to a file variable.
BlockRead      —  Reads one or more records from an untyped file.
BlockWrite     —  Writes one or more records into an untyped file.
ChDir          —  Changes the current directory.
CloseFile      —  Closes an open file.
Eof            —  Returns the end-of-file status of a file.
Erase          —  Erases an external file.
FilePos        —  Returns the current file position of a typed or untyped file.
FileSize       —  Returns the current size of a file; not used for text files.
GetDir         —  Returns the current directory of a specified drive.
IOResult       —  Returns an integer value that is the status of the last I/O function performed.
MkDir          —  Creates a subdirectory.
Read           —  Reads one or more values from a file into one or more variables.
Reset          —  Opens an existing file.
Rewrite        —  Creates and opens a new file.
RmDir          —  Removes an empty subdirectory.
Seek           —  Moves the current position of a typed or untyped file to a specified component. Not used with text files.
Truncate       —  Truncates a typed or untyped file at the current file position.
Write          —  Writes one or more values to a file.
Несколько тестовых примеров
WriteToTypedFile — записывает две записи в типизированный файл
program WriteToTypedFile;

{$APPTYPE CONSOLE}

uses
  CRT;

type
  TPerson = record
    Name: string[20];
    Family: string[20];
    IsStudent: Boolean;
  end;

var
  TypedFile: file of TPerson;
  Person: TPerson;

begin
  //связываем файловую переменную с файлом на диске
  AssignFile(TypedFile,'MyFile.txt');
  //открываем файл для записи
  Rewrite(TypedFile);
  Person.Name:='John';
  Person.Family:='Johnson';
  Person.IsStudent := True;
  //добавляем запись в файл
  Write(TypedFile, Person);
  Person.Name:='Peter';
  Person.Family:='Peterson';
  Person.IsStudent := False;
  //добавляем запись в файл
  Write(TypedFile, Person);
  //закрываем файл
  CloseFile(TypedFile);
  WriteLn('Done. Now check you file "MyFile.txt".');
end.
AddToTypedFile   — позволяет добавить новую запись в типизированный файл и выводит все записи в консоль
program AddToTypedFile;

{$APPTYPE CONSOLE}

uses
  CRT;

type
  TPerson = record
    Name: string[20];
    Family: string[20];
    IsStudent: Boolean;
  end;

function FileExist(FileName: string): Boolean;
var
  F: file;
begin
  AssignFile(F, FileName);
  Reset(F);
  CloseFile(F);
  Result := (IOResult = 0) and (FileName <> '');
end;

procedure ReadTypedFile;
var
  F: file of TPerson;
  Person: TPerson;
begin
  // связываем файловую переменную с файлом на диске
  AssignFile(F, 'MyFile.txt');
  // открываем файл для чтения
  Reset(F);
  while not Eof(F) do
  begin
    System.Read(F, Person);
    with Person do
      Writeln(Name, ' ', Family, ' ', IsStudent);
  end;
  // закрываем файл
  CloseFile(F);
end;

procedure AppendTypedFile;
var
  F: file of TPerson;
  Person: TPerson;
  Ch: ShortString;
begin
  // связываем файловую переменную с файлом на диске
  AssignFile(F, 'MyFile.txt');
  // открываем файл для чтения и записи
  Reset(F);
  // перемещаем файловый указатель в конец файла
  Seek(F, FileSize(F));
  Writeln('Input name:');
  Readln(Person.Name);
  Writeln('Input family:');
  Readln(Person.Family);
  Writeln('Is he\she student or not(1 - Yes; 2 - No):');
  ReadLn(Ch);
  if Ch[1] = '1' then
    Person.IsStudent := True
  else
    Person.IsStudent := False;
  // добавляем запись в файл
  Write(F, Person);
  // закрываем файл
  CloseFile(F);
end;

var
  TypedFile: file of TPerson;
  YesOrNo: ShortString;

begin
  if not FileExist('MyFile.txt') then
  begin
    // связываем файловую переменную с файлом на диске
    AssignFile(TypedFile, 'MyFile.txt');
    // открываем файл для записи
    Rewrite(TypedFile);
    // закрываем файл
    CloseFile(TypedFile);
  end;
  repeat
    Writeln('Would you like to add an entry to the file?(1 - Yes; 2 - No)');
    ReadLn(YesOrNo);
    case YesOrNo[1] of
      '1': AppendTypedFile;
      '2': Break;
    end;
  until YesOrNo[1] = #0;
  //читаем все записи из типизированного файла
  ReadTypedFile;
end.
DelFromTypedFile — если записей меньше 2, то предлагает удалить все, иначе удаляет 2 не спрашивая
program DelFromTypedFile;

{$APPTYPE CONSOLE}

uses
  CRT;

type
  TPerson = record
    Name: string[20];
    Family: string[20];
    IsStudent: Boolean;
  end;

procedure ReadTypedFile;
var
  F: file of TPerson;
  Person: TPerson;
begin
  AssignFile(F, 'MyFile.txt');
  Reset(F);
  while not Eof(F) do
  begin
    System.Read(F, Person);
    with Person do
      Writeln(Name, ' ', Family, ' ', IsStudent);
  end;
end;

var
  TypedFile: file of TPerson;
  Count: LongInt;
  YesOrNo: ShortString;

begin
  AssignFile(TypedFile,'MyFile.txt');
  Reset(TypedFile);
  Count := FileSize(TypedFile);
  if Count < 2 then
    begin
      Writeln('The count of entries in file is less than 2. Erase all?(1 - Yes; 2 - No)');
      Readln(YesOrNo);
      if YesOrNo[1] = '1' then
        Truncate(TypedFile);
    end
  else
    begin
      Writeln('The count of entries in file is equal or more than 2. I am delete 2 entries.');
      Seek(TypedFile, Count - 2);
      Truncate(TypedFile);
    end;
  ReadTypedFile;
end.
TestFileExist    — позволяет узнать, существует ли файл
program TestFileExist;

{$APPTYPE CONSOLE}

uses
  CRT;

function FileExist(FileName: string): Boolean;
var
  F: file;
begin
  AssignFile(F, FileName);
  Reset(F);
  CloseFile(F);
  Result := (IOResult = 0) and (FileName <> '');
end;

begin
  if FileExist('myfile.txt') then
    WriteLn('The file exists.')
  else
    WriteLn('This file does not exist.')
end.
WorkWithDir      — меняет текущую директорию, создаёт папки, узнаёт текущую директорию, удаляет папку
program WorkWithDir;

{$APPTYPE CONSOLE}

uses
  CRT;

var
  CurDir: ShortString;

begin
  // изменяем текущую директорию
  ChDir('/tmp0/1');
  // создаём папку
  MkDir('NewFolder1');
  // создаём папку
  MkDir('NewFolder2');
  // изменяем текущую директорию
  ChDir('/tmp0/1/NewFolder1');
  // создаём папку
  MkDir('NewFolder3');
  // узнаём текущую директорию
  GetDir(0, CurDir);
  WriteLn('GetDir = ', CurDir);
  WriteLn('press key to delete "NewFolder2"');
  ReadKey;
  // удаляем папку
  RmDir('/tmp0/1/NewFolder2');
end.

Прилагаю скриншот, чтобы примерно было понятно, как оно выглядит, действия происходят с файлом 'MyFile.txt'

На скриншоте созданные папки и файл с сохранённой информацией в нём.

misc.php?action=pun_attachment&amp;item=150&amp;download=0

Судя по размеру сжатых исполняемых файлов, скорее всего, используются строковые функции и функции выделения памяти.
В архиве IOTests.7z скомпилированные примеры, интерес в том, что они скомпилировались с использованием SDK и работают в KolibriOS.

FilePos, FileSize, Seek работают с 32-битным значением, но размер файла может быть очень большим.
Если даже в System сделать эти функции 64-битными(Int64 вместо LongInt), то всё равно компилятор будет считать их 32-битными.
Поэтому предлагаю для таких случаев(ну а вдруг кому-то понадобится) создать отдельный модуль IORoutines64,
содержащий 64-битные дубли этих функций, которые при подключении модуля перекроют свои 32-битные аналоги из System.
Читать\писать\копировать(BlockRead\BlockWrite) большие файлы можно даже сейчас без дополнительных ухищрений.
Ещё есть вариант поместить вообще все эти функции в отдельный модуль IORoutines вместо System, сделав их 64-битными, и подключать по необходимости,
при этом не нужно будет дублировать в System некоторые файловые функции из модуля KolibriOS.

Также прилагаю System, с которым это всё компилировалось.
Как уже писал ранее, строковые функции были взяты из https://github.com/synopse
Не обращайте внимание на

{$DEFINE UseMemoryManager}
{$DEFINE UseStrings}
{$DEFINE UseParamStr} 
{$DEFINE UseFileRoutines}

мне просто так удобнее было экспериментировать.

Были ещё проблемы с CRT

Read из CRT перекрывает системную функцию и для файлового чтения приходится писать

System.Read(MyFile, MyRec);

Возможно, ReadLn из CRT неправильно работает: не нужно символ новой строки копировать.
добавил в CRT.ReadLn

P^ := #0;

получилось так 

procedure ReadLn(var Result: ShortString);
var
  P, Limit: PKolibriChar;
begin
  P := PKolibriChar(@Result[1]);
  ConsoleInterface.GetS(P, High(Byte));
  Limit := P + High(Byte);
  while (P < Limit) and not (P^ in [#0, #10]) do
    Inc(P);
  P^ := #0;  // <<<--------
  PByte(@Result)^ := P - PKolibriChar(@Result[1]);
end;

 
стало вроде бы правильно работать, но странно, что для ShortString это было нужно.
А может просто не нужно было переопределять эту процедуру и лучше её сделать стандартной.

Приложенные примеры компилировались с поправленным модулем CRT.

Post's attachments

Иконка вложений IOTests.7z 9.93 Кб, 64 скачиваний с 2022-02-03 

Иконка вложений IOTests.PNG 55.53 Кб, 32 скачиваний с 2022-02-03 

Иконка вложений System.pas 74.82 Кб, 66 скачиваний с 2022-02-03