Skip to content
Lesson 5 of 7

Step 1 of 5 · Reading · ~2 min

Learn

Strings, Procedural Types, Variants

FreePascal's SysUtils and StrUtils units cover most string needs.

SysUtils essentials

  • Format('%s = %d', [name, value]) — printf-style
  • IntToStr(n), StrToInt(s), TryStrToInt(s, n)
  • FloatToStr(x), StrToFloat(s), FormatFloat('0.00', x)
  • UpperCase(s), LowerCase(s)
  • Trim(s), TrimLeft(s), TrimRight(s)
  • StringReplace(s, old, new, [rfReplaceAll, rfIgnoreCase])
  • Concat(s1, s2, ...)

Splitting and joining

FPC 3.x adds string helpers, but only in Delphi mode — line.Split(...) in the default dialect fails with Illegal qualifier, so the {$mode delphi} line is load-bearing:

{$mode delphi}{$H+}
uses SysUtils;
var
    s: string;
    parts: TStringArray;
    word: string;
begin
    s := 'hello world foo bar';
    parts := s.Split([' ']);
    for word in parts do
        WriteLn(word);
end.

Manual split (older code):

uses Classes;
var lines: TStringList;
begin
    lines := TStringList.Create;
    try
        lines.Delimiter := ' ';
        lines.StrictDelimiter := True;
        lines.DelimitedText := s;
        for i := 0 to lines.Count - 1 do
            WriteLn(lines[i]);
    finally
        lines.Free;
    end;
end.

StrUtils — extras

uses StrUtils;

DupeString('ab', 3)              { 'ababab' }
LeftStr('hello', 3)              { 'hel' }
RightStr('hello', 3)             { 'llo' }
MidStr('hello', 2, 3)            { 'ell' }
ReverseString('hello')           { 'olleh' }
ContainsText(haystack, needle)   { case-insensitive }
StartsText('foo', 'foo bar')     { true }

Pos and PosEx

Pos('world', 'hello world')      { 7 — 1-indexed }
Pos('xyz', 'hello world')        { 0 — not found }
PosEx('o', 'foo', 1)             { find 'o' starting at index 1 }

String concatenation in loops

Avoid s := s + chunk in a loop: each + allocates a new string and copies everything written so far, so assembling n pieces costs O(n²) bytes copied. Collect the pieces and join once. Delphi's TStringBuilder is the usual answer, but it is absent from FPC 3.0.4's RTL — here, a TStringList does the same job:

uses Classes, SysUtils;
var
    parts: TStringList;
    i: integer;
begin
    parts := TStringList.Create;
    try
        for i := 1 to 1000 do
            parts.Add(IntToStr(i));
        WriteLn(StringReplace(parts.Text, sLineBreak, '', [rfReplaceAll]));
    finally
        parts.Free;
    end;
end.

parts.Text is the list joined with line breaks; strip them and you have the concatenation, built with one allocation per piece instead of one copy of the whole result per piece.

Format specifiers

Format('%d items', [42])             { '42 items' }
Format('%6.2f%%', [99.5])            { ' 99.50%' }
Format('%-10s|', ['hi'])             { 'hi        |' (left-pad) }
Format('%s = %d', ['x', 5])

The [ ... ] is an open-array of Variant (any type). Format infers from %d, %s, %f.

Conversion best practices

uses SysUtils;
var n: integer;
begin
    if TryStrToInt(input, n) then
        Process(n)
    else
        WriteLn('invalid: ', input);
end.

TryStrToInt/TryStrToFloat return boolean instead of throwing — much nicer for input parsing.

Up nextProcedural Types and CallbacksStrings, Procedural Types, Variants

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…