Istnieje możliwość bezpośredniego odczytu liczb całkowitych i zmiennoprzecinkowych z pliku tekstowego, nawet jeśli kilka liczb zawartych jest w jednej linii; Do tego celu służy uniwersalna procedura Read; Ilość liczb w jednej linii nie musi być znana, dlatego że istnieje prosty sposób na czytanie danych aż do końca linii:
{ dla każdej linii pliku }
while not EoF(LInputFile) do
begin
{ aż do końca bieżącej linii pliku }
while not EoLn(FInputFile)
begin
end;
end;
Powyższy schemat można wykorzystać tylko w przypadku, gdy każda linia zawiera co najmniej jedną dowolną liczbę oraz wtedy, gdy w każdej linii znajdują się tylko i wyłącznie liczby; Ty natomiast nie możesz czytać w ten sposób danych, dlatego że przeszkadzają te ciągi pomiędzy liczbami;
Jeśli te ciągi znaków - string1.1
, string1.2
, string2.1
i string2.2
- nie mogą posiadać białych znaków, czyli przykładowa linia z takiego pliku tekstowego wygląda tak:
String String Integer Integer String String Integer Integer
to możesz wczytać całą linię to zmiennej łańcuchowej, podzielić ją na składowe za pomocą ExtractStrings, a następnie przepisać wartości podciągów z wynikowej listy do pól rekordu (dodatkowo konwertując ciągi liczbowe na liczby natywne);
Inne rozwiązanie to napisanie sobie procedurki, która to będzie potrafiła wczytać podciąg znajdujący się wewnątrz linii, tak aby wczytywała dotąd, aż natrafi albo na znak końca linii, albo na biały znak; Przykład takiej procedury poniżej:
procedure ReadStrFromFile(var AFile: TextFile; out AString: String);
var
LChar: Char;
begin
AString := '';
if not EoLn(AFile) then
begin
Read(AFile, LChar);
if LChar <> ' ' then
AString += LChar;
end;
while not EoLn(AFile) do
begin
Read(AFile, LChar);
if LChar = ' ' then
Exit()
else
AString += LChar;
end;
end;
Pierwszy warunek służy do pominięcia znaku separatora składowych linii - jest to konieczne, dlatego że po odczytaniu liczby, kursor w pliku ustawiony jest przed znakiem separatora i znak ten (jeśli na niego wskazuje) należy pominąć; Dalej jest pętla, która czyta znaki dotąd aż napotka na biały znak lub na znak końca linii; Do argumentu dodaje wczytane znaki - wszystkie oprócz białych;
Do kompletu można sobie napisać dwa pozostałe wrappery, coby zwiększyć czytelność kodu:
procedure ReadIntFromFile(var AFile: TextFile; out AInt: Integer);
begin
Read(AFile, AInt);
end;
procedure ReadRealFromFile(var AFile: TextFile; out AReal: Real);
begin
Read(AFile, AReal);
end;
Liczby całkowite i zmiennoprzecinkowe można wczytywać bezpośrednio, więc to tylko wrappery; Mając już przygotowane procedurki wczytujące, wystarczy ich użyć w odpowiedniej kolejności; Załóżmy, że plik źródłowy posiada linie zbudowane według poniższego schematu:
String String Integer Integer String String Real Real
Przykładowa linia wygląda np. tak:
first second 100 200 third fourth 23.45 67.89
Mamy też przygotowany typ rekordu, do którego chcemy wczytać dane z pliku:
type
TSomeRec = record
StrFirst: String;
StrSecond: String;
IntFirst: Integer;
IntSecond: Integer;
StrThird: String;
StrFourth: String;
RealFirst: Real;
RealSecond: Real;
end;
Aby załadować dane z jednej linii pliku źródłowego bezpośrednio do rekordu, bez używania ExtractStrings, należy skorzystać z poniższego schematu:
var
LInputFile: TextFile;
LSomeRec: TSomeRec;
begin
AssignFile(LInputFile, 'C:\Input.txt');
Reset(LInputFile);
try
ReadStrFromFile(LInputFile, LSomeRec.StrFirst); // składowa "first"
ReadStrFromFile(LInputFile, LSomeRec.StrSecond); // składowa "second"
ReadIntFromFile(LInputFile, LSomeRec.IntFirst); // składowa "100"
ReadIntFromFile(LInputFile, LSomeRec.IntSecond); // składowa "200"
ReadStrFromFile(LInputFile, LSomeRec.StrThird); // składowa "third"
ReadStrFromFile(LInputFile, LSomeRec.StrFourth); // składowa "fourth"
ReadRealFromFile(LInputFile, LSomeRec.RealFirst); // składowa "23.45"
ReadRealFromFile(LInputFile, LSomeRec.RealSecond); // składowa "67.89"
finally
CloseFile(LInputFile);
end;
Jeśli teraz wyświetlimy sobie zawartość rekordu LSomeRec
w taki sposób:
WriteLn('StrFirst ', LSomeRec.StrFirst);
WriteLn('StrSecond ', LSomeRec.StrSecond);
WriteLn('IntFirst ', LSomeRec.IntFirst);
WriteLn('IntSecond ', LSomeRec.IntSecond);
WriteLn('StrThird ', LSomeRec.StrThird);
WriteLn('StrFourth: ', LSomeRec.StrFourth);
WriteLn('RealFirst ', LSomeRec.RealFirst:2:2);
WriteLn('RealSecond ', LSomeRec.RealFirst:2:2);
zobaczymy na ekranie to:
StrFirst first
StrSecond second
IntFirst 100
IntSecond 200
StrThird third
StrFourth: fourth
RealFirst 23.45
RealSecond 23.45
Czyli jak widać, na ekranie wyświetlone zostaną prawidłowo wczytane dane; Aby procedura ReadStrFromFile
działała prawidłowo, separatorem składowych w linii muszą być znaki spacji (składowa, jeden znak spacji, składowa, jeden znak spacji itd.); Taką procedurkę można bardziej rozbudować, aby potrafiła więcej;
Edit: Podany kod jest odpowiedni dla FPC, został też sprawdzony, więc jest w pełni działający.