home *** CD-ROM | disk | FTP | other *** search
Wrap
{$INCLUDE ..\cDefines.inc} unit cStrings; { } { Ansi Strings v3.32 } { } { This unit is copyright ⌐ 1999-2002 by David Butler (david@e.co.za) } { } { This unit is part of Delphi Fundamentals. } { Its original file name is cStrings.pas } { The latest version is available from the Fundamentals home page } { http://fundementals.sourceforge.net/ } { } { I invite you to use this unit, free of charge. } { I invite you to distibute this unit, but it must be for free. } { I also invite you to contribute to its development, } { but do not distribute a modified copy of this file. } { } { A forum is available on SourceForge for general discussion } { http://sourceforge.net/forum/forum.php?forum_id=2117 } { } { } { Revision history: } { 1999/10/19 v0.01 Spawned from Maths unit. } { 1999/10/26 v0.02 Documentation. } { 1999/10/30 v0.03 Added Count, Reverse. } { Implemented the Boyer-Moore-Horspool pattern searching } { algorithm in assembly. } { 1999/10/31 v0.04 Coded Match function in assembly. } { Added Replace, Count, PadInside. } { 1999/11/06 v1.05 261 lines interface, 772 lines implementation. } { Added Remove, TrimEllipse. } { 1999/11/09 v1.06 Added Pack functions. } { 1999/11/17 v1.07 Added Cut to Pad ) } { Added PosN, Before, After and Between. } { Added CountWords. Added Split. } { 1999/11/22 v1.08 Added Join. Added Pad (I : Integer). } { 1999/11/23 v1.09 Added Translate. } { 1999/12/02 v1.10 Added NumToRoman. } { Fixed bugs in Replace and Match reported by } { daiqingbo@netease.com } { 1999/12/27 v1.11 Added SelfTest procedure. } { Bug fixes. Removed flawed NumToRoman. } { 2000/01/04 v1.12 Added InsensitiveCharSet. } { 2000/01/08 v1.13 Added Append. } { 2000/05/08 v1.14 Cleaned up unit. } { 2000/07/20 v1.15 Fixed bug in Match where Position < 0. } { 2000/08/30 v1.16 Fixed bug in Match when S = ''. } { 2000/09/04 v1.17 Added MatchFileMask. } { 2000/09/31 v1.18 Added HexEscapeText and HexUnescapeText. } { 2000/12/04 v1.19 Changes to CopyRange, CopyLeft to avoid memory } { allocation in specific cases. } { 2001/04/22 v1.20 Added CaseSensitive parameter to Match, PosNext, PosN } { 2001/04/25 v1.21 Added CopyEx functions. } { Added MatchLeft/MatchRight. } { Updated test cases. } { 2001/04/26 v1.22 Major refactoring. } { -2001/04/28 Replaced PosNext and PosPrev with Pos. } { Most functions have compatible parameters now. } { Added FindFirst, FindFirstUnmatchedRange, } { IterateMatches. } { 1000 lines interface. 3727 lines implementation. } { 2001/04/29 v1.23 Added some assembly implementations by Andrew N. } { Driazgov <andrey@asp.tstu.ru> } { 2001/05/13 v1.24 Added simple regular expression matching. } { Added CharClassStr conversion routines. } { Added PosNext that uses Pos. } { 1149 lines interface. 4851 lines implementation. } { 2001/06/01 v1.25 Added TQuickLexer } { 2001/07/07 v1.26 Optimizations. } { 2001/07/30 v1.27 Changed Iterators from objects to records. } { 2001/08/22 v1.28 Added LZ-Huffman packer / unpacker. } { 1429 lines interface. 6445 lines implementation. } { 2001/11/11 v2.29 Revision. } { 2002/02/14 v2.30 Added MatchPattern. } { 2002/04/03 v3.31 Added string functions from cUtils. } { 2002/04/14 v3.32 Moved TQuickLexer to cQuickLexer. } { } interface uses // Delphi SysUtils, // Fundamentals cUtils; const UnitName = 'cStrings'; UnitVersion = '3.32'; UnitDesc = 'Ansi String functions'; UnitCopyright = '(c) 1999-2002 by David Butler'; { } { Character constants } { } const // ASCII codes ASCII_NULL = #0; ASCII_SOH = #1; ASCII_STX = #2; ASCII_ETX = #3; ASCII_EOT = #4; ASCII_ENQ = #5; ASCII_ACK = #6; ASCII_BEL = #7; ASCII_BS = #8; ASCII_HT = #9; ASCII_LF = #10; ASCII_VT = #11; ASCII_FF = #12; ASCII_CR = #13; ASCII_NAK = #21; ASCII_SYN = #22; ASCII_CAN = #24; ASCII_EOF = #26; ASCII_ESC = #27; ASCII_SP = #32; ASCII_DEL = #127; ASCII_CTL = [#0..#31]; ASCII_TEXT = [#32..#127]; c_Tab = ASCII_HT; c_Space = ASCII_SP; c_DecimalPoint = '.'; c_Comma = ','; c_BackSlash = '\'; c_ForwardSlash = '/'; c_Plus = '+'; c_Minus = '-'; CRLF = ASCII_CR + ASCII_LF; cs_AllChars = [#0..#255]; cs_ASCII = ASCII_TEXT; cs_NotASCII = cs_AllChars - cs_ASCII; cs_AlphaLow = ['a'..'z']; cs_AlphaUp = ['A'..'Z']; cs_Numeric = ['0'..'9']; cs_NotNumeric = cs_AllChars - cs_Numeric; cs_Alpha = cs_AlphaLow + cs_AlphaUp; cs_NotAlpha = cs_AllChars - cs_Alpha; cs_AlphaNumeric = cs_Numeric + cs_Alpha; cs_NotAlphaNumeric = cs_AllChars - cs_AlphaNumeric; cs_WhiteSpace = ASCII_CTL + [ASCII_SP]; cs_Exponent = ['E', 'e']; cs_HexDigit = cs_Numeric + ['A'..'F', 'a'..'f']; cs_OctalDigit = ['0'..'7']; cs_BinaryDigit = ['0'..'1']; cs_Sign = [c_Plus, c_Minus]; cs_Quotes = ['"', '''', '`']; cs_Parentheses = ['(', ')']; cs_CurlyBrackets = ['{', '}']; cs_BlockBrackets = ['[', ']']; cs_Punctuation = ['.', ',', ':', '/', '?', '<', '>', ';', '"', '''', '[', ']', '{', '}', '+', '=', '-', '\', '(', ')', '*', '&', '^', '%', '$', '#', '@', '!', '`', '~']; { } { Type conversion } { } Function StrToFloatDef (const S : String; const Default : Extended) : Extended; Function BooleanToStr (const B : Boolean) : String; Function StrToBoolean (const S : String) : Boolean; Function TVarRecToString (const V : TVarRec; const QuoteStrings : Boolean) : String; { } { Case conversion } { } { FirstUp returns S with the first letter changed to upper-case. } { } Function LowCase (Ch : Char) : Char; Procedure ConvertUpper (var S : String); overload; Procedure ConvertLower (var S : String); overload; Procedure ConvertFirstUp (var S : String); Function FirstUp (const S : String) : String; Procedure ConvertUpper (var S : StringArray); overload; Procedure ConvertLower (var S : StringArray); overload; { } { Character class strings } { } { Perl-like character class strings, eg the set ['0', 'A'..'Z'] is presented } { as '[0A-Z]'. Negated classes also supported, eg '[^A-Za-z]' is all } { non-alpha characters. The empty and complete sets have special } { representations; '[]' and '.' respectively. } { } Function CharSetToCharClassStr (const C : CharSet) : String; Function CharClassStrToCharSet (const S : String) : CharSet; { } { Duplicate } { } Function Dup (const S : String; const Count : Integer) : String; overload; Function Dup (const Ch : Char; const Count : Integer) : String; overload; Function DupBuf (const Buf; const BufSize : Integer; const Count : Integer) : String; overload; Function DupBuf (const Buf; const BufSize : Integer) : String; overload; { } { Index-based Copy } { } { Variantions on Delphi's Copy. Like Delphi's Copy, invalid values for } { StartIndex (<1,>len), StopIndex (<start,>len) and Count (<0,>end) are } { tolerated (clipped), in other words indexes <1 are treated as 1, } { indexes >len are treated as len and Count past end of string is } { treated as up to end. } { Unlike Delphi's Copy, these versions do not return new strings when } { a reference to an existing string exists. } { } Function CopyRange (const S : String; const StartIndex, StopIndex : Integer) : String; overload; Function CopyFrom (const S : String; const StartIndex : Integer) : String; overload; Function CopyLeft (const S : String; const Count : Integer) : String; overload; Function CopyRight (const S : String; const Count : Integer = 1) : String; overload; { } { Match } { } { Returns True if M matches at S [StartIndexPos]. } { If StartIndex is invalid, returns False. } { For Match with Count parameter, returns True if M matches Count times, } { also returns True if Count <= 0. } { } Function MatchNoCase (const A, B : Char) : Boolean; Function Match (const A, B : Char; const CaseSensitive : Boolean = True) : Boolean; overload; Function Match (const A : CharSet; const B : Char; const CaseSensitive : Boolean = True) : Boolean; overload; Function MatchCount (const M : Char; const S : String; const StartIndex : Integer = 1; const MaxCount : Integer = -1; const CaseSensitive : Boolean = True) : Integer; overload; Function MatchCount (const M : CharSet; const S : String; const StartIndex : Integer = 1; const MaxCount : Integer = -1; const CaseSensitive : Boolean = True) : Integer; overload; Function Match (const M, S : String; const StartIndex : Integer = 1; const CaseSensitive : Boolean = True) : Boolean; overload; Function MatchBuf (const M : String; const Buf; const BufSize : Integer; const CaseSensitive : Boolean = True) : Boolean; Function Match (const M : Char; const S : String; const StartIndex : Integer = 1; const Count : Integer = 1; const CaseSensitive : Boolean = True) : Boolean; overload; Function Match (const M : CharSet; const S : String; const StartIndex : Integer = 1; const Count : Integer = 1; const CaseSensitive : Boolean = True) : Boolean; overload; Function MatchSeq (const M : Array of CharSet; const S : String; const StartIndex : Integer = 1; const CaseSensitive : Boolean = True) : Boolean; Function MatchChars (const M : Char; const S : Array of Char; const CaseSensitive : Boolean = True) : Integer; Function MatchStrings (const M : String; const S : Array of String; const CaseSensitive : Boolean = True; const StartIndex : Integer = 1; const MaxMatchLength : Integer = -1) : Integer; overload; Function MatchStrings (const M : Array of String; const S : Array of String; var MatchedItem : Integer; const CaseSensitive : Boolean = True; const MaxMatchLength : Integer = -1) : Integer; overload; Function MatchLeft (const M, S : String; const CaseSensitive : Boolean = True) : Boolean; Function MatchRight (const M, S : String; const CaseSensitive : Boolean = True) : Boolean; Function IsEqualNoCase (const A, B : String) : Boolean; Function IsEqual (const A, B : String; const CaseSensitive : Boolean = True) : Boolean; overload; { } { Fast abbreviated regular expression matcher } { } { Matches regular expressions of the form: (<charset><quant>)* } { where <charset> is a character set and <quant> is one of the quantifiers } { (mnOnce, mnOptional = ?, mnAny = *, mnLeastOnce = +). } { Supports deterministic/non-deterministic, greedy/non-greedy matching. } { Returns first MatchPos (as opposed to longest). } { Uses a NFA (Non-deterministic Finite Automata). } { } { For example: } { I := 1 } { S := 'a123' } { MatchQuantSeq (I, [['a'..'z'], ['0'..9']], [mqOnce, mqAny], S) = True } { } { is the same as matching the regular expression [a-z][0-9]* } { } type TMatchQuantifier = (mqOnce, mqAny, mqLeastOnce, mqOptional); TMatchQuantSeqOptions = Set of (moDeterministic, moNonGreedy); Function MatchQuantSeq (var MatchPos : Integer; const MatchSeq : Array of CharSet; const Quant : Array of TMatchQuantifier; const S : String; const MatchOptions : TMatchQuantSeqOptions = []; const StartIndex : Integer = 1; const StopIndex : Integer = -1) : Boolean; overload; type TQuantSeq = class Sequence: CharSetArray; Quantity: Array of TMatchQuantifier; Options : TMatchQuantSeqOptions; Constructor Create (const MatchSeq : Array of CharSet; const Quant : Array of TMatchQuantifier; const MatchOptions : TMatchQuantSeqOptions = []); overload; Procedure AddToSequence (const Ch : CharSet; const Quant : TMatchQuantifier); Procedure AddStringToSequence (const S : String; const CaseSensitive : Boolean = True); Function Match (var MatchPos : Integer; const S : String; const StartIndex : Integer = 1; const StopIndex : Integer = -1) : Boolean; end; { } { Fast Pattern Matcher } { } { Matches a subset of regular expressions (* ? and []) } { Matching is non-determistic (ie does backtracking) / non-greedy (ie lazy) } { '*' Matches zero or more of any character } { '?' Matches exactly one character } { [<char set>] Matches character from <char set> } { [^<char set>] Matches character not in <char set> } { where <char set> can include multiple ranges and escaped characters } { '\n' matches NewLine (#10), '\r' matches Return (#13) } { '\\' matches a slash ('\'), '\]' matches a close bracket (']'), etc. } { } { Examples: } { MatchPattern ('[a-z0-9_]bc?*c', 'abcabc') = True } { MatchPattern ('[\\\r\n]+', '\'#13#10) = True } { } Function MatchPattern (M, S : PChar) : Boolean; { } { File Mask Matcher } { Matches classic file mask type regular expressions. } { ? = matches one character (or zero if at end of mask) } { * = matches zero or more characters } { } Function MatchFileMask (const Mask, Key : String; const CaseSensitive : Boolean = False) : Boolean; { } { Format checking } { Number [0-9]+ } { HexNumber [0-9A-Fa-f]+ } { Integer [+-]? <number> } { Real <integer>? ([.] <number>)? } { SciReal <real> ([e] <integer>)? } { QuotedString Quote ([^Quote]* ([Quote][Quote])?)* Quote } { } { The Match functions returns the length of the matched text. } { } Function MatchNumber (const S : String; const Index : Integer = 1) : Integer; Function MatchHexNumber (const S : String; const Index : Integer = 1) : Integer; Function MatchInteger (const S : String; const Index : Integer = 1) : Integer; Function MatchReal (const S : String; const Index : Integer = 1) : Integer; Function MatchSciReal (const S : String; const Index : Integer = 1) : Integer; Function MatchQuotedString (const S : String; const ValidQuotes : CharSet; const Index : Integer = 1) : Integer; Function IsNumber (const S : String) : Boolean; Function IsHexDigit (const C : Char) : Boolean; Function HexDigitValue (const C : Char) : Byte; Function IsHexNumber (const S : String) : Boolean; Function IsInteger (const S : String) : Boolean; Function IsReal (const S : String) : Boolean; Function IsSciReal (const S : String) : Boolean; Function IsQuotedString (const S : String; const ValidQuotes : CharSet = cs_Quotes) : Boolean; { } { Trim } { TrimQuotes removes quotes around a string. } { TrimEllipse trims the string and puts '...' at the end if it's longer } { than Length. } { } Function TrimLeft (const S : String; const TrimSet : CharSet = cs_WhiteSpace) : String; Procedure TrimLeftInPlace (var S : String; const TrimSet : CharSet = cs_WhiteSpace); Function TrimLeftStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean = True) : String; Function TrimRight (const S : String; const TrimSet : CharSet = cs_WhiteSpace) : String; Procedure TrimRightInPlace (var S : String; const TrimSet : CharSet = cs_WhiteSpace); Function TrimRightStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean = True) : String; Function Trim (const S : String; const TrimSet : CharSet) : String; overload; Procedure TrimInPlace (var S : String; const TrimSet : CharSet = cs_WhiteSpace); Function TrimStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean = True) : String; overload; Procedure Trim (var S : StringArray; const TrimSet : CharSet = cs_WhiteSpace); overload; Procedure TrimStr (var S : StringArray; const TrimStr : String; const CaseSensitive : Boolean = True); overload; Function TrimEllipse (const S : String; const Length : Integer) : String; Function TrimQuotes (const S : String; const Quotes : CharSet = cs_Quotes) : String; { } { Pad } { The default for Cut is False which won't shorten the string to Length } { if Length < Length (S). } { PadLeft is equivalent to a right justify, PadRight a left justify, } { Pad centering and PadInside a full justification. } { Pad (I : Integer) left-pad the number with zeros. } { } Function PadLeft (const S : String; const PadChar : Char; const Length : Integer; const Cut : Boolean = False) : String; Function PadRight (const S : String; const PadChar : Char; const Length : Integer; const Cut : Boolean = False) : String; Function Pad (const S : String; const PadChar : Char; const Length : Integer; const Cut : Boolean = False) : String; overload; Function Pad (const I : Integer; const Length : Integer; const Cut : Boolean = False) : String; overload; Function PadInside (const S : String; const PadChar : Char; const Length : Integer) : String; type TPadType = (padNone, padLeftSpace, padLeftZero, padRightSpace); Function IntToPadStr (const I : Integer; const PadType : TPadType; const Len : Integer) : String; { } { Paste } { Paste copies from Source [SourceStart..SourceStop] to Dest [DestIndex]. } { SourceStart, SourceStop and DestPos can be negative to refences indexes } { from the back. Dest will not be grown, Source will be clipped to fit. } { Returns the number of characters moved. } { DestIndex is increased/decreased based on ReverseDirection and the number } { of characters moved. } { } Function Paste (const Source : String; var Dest : String; var DestIndex : Integer; const ReverseDirection : Boolean = False; const SourceStart : Integer = 1; const SourceStop : Integer = -1) : Integer; overload; { } { CopyEx } { CopyEx functions extend Copy so that Start/Stop values can be negative to } { reference indexes from the back, eg. -2 will reference the second last } { character in the string. } { Invalid values for Start, Stop and Count are tolerated (clipped). } { } Function CopyEx (const S : String; const Start, Count : Integer) : String; Function CopyRangeEx (const S : String; const Start, Stop : Integer) : String; Function CopyFromEx (const S : String; const Start : Integer) : String; { } { Find options } { foReverse - Search backwards from Stop downto Start. } { foOverlapping - If Find is a sequence (String, CharSetArray or 'Array } { of CharSet'), also returns overlapping matches } { (matches in matches) } { foCaseInsensitive - Case insensitive matching. } { foNonMatch - Find all non-matches. } { } type TFindOption = (foReverse, foOverlapping, foCaseInsensitive, foNonMatch); TFindOptions = Set of TFindOption; Function FindOptions (const Reverse : Boolean; const CaseInsensitive : Boolean = False; const Overlapping : Boolean = False; const NonMatch : Boolean = False) : TFindOptions; { } { Pos } { Returns first Match of Find in S between (inclusive) Start and Stop. } { Start and Stop can be negative to refence indexes from the back. } { Invalid values for Start and Stop are tolerated (clipped). } { Returns 0 if not found. } { } { Patterns for iterating all matches: } { I := Pos (Find, S) I := 0 } { While I > 0 do Repeat } { begin OR I := PosNext (Find, S, [], I) } { ... R := I > 0 } { I := PosNext (Find, S, I) if R then ... } { end Until not R } { (Also see IterateMatches and FindFirst/FindNext) } { } { TBMHSearcher implements the Boyer-Moore-Horspool pattern searching } { algorithm. The function is faster than Pos for multiple searches for the } { same value of Find (longer strings are better). } { } Function Pos (const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload; Function Pos (const Find : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload; Function Pos (const Find : CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload; Function PosSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload; Function PosBuf (const Find : String; const Buf; const BufSize : Integer; const CaseSensitive : Boolean = True) : Integer; Function PosNext (const Find, S : String; const LastPos : Integer = 0; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload; Function PosNext (const Find : Char; const S : String; const LastPos : Integer = 0; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload; Function PosNext (const Find : CharSet; const S : String; const LastPos : Integer = 0; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload; Function PosNextSeq (const Find : Array of CharSet; const S : String; const LastPos : Integer = 0; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload; Function PosChars (const Find : Array of Char; const S : String; var FindItem : Integer; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; Function PosStrings (const Find : Array of String; const S : String; var FindItem : Integer; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1) : Integer; type TBMHSearcher = class private FTable : Array [#0..#255] of Integer; FFind : String; public Constructor Create (const Find : String); Function Pos (const S : String; const StartIndex : Integer = 1; const StopIndex : Integer = 0) : Integer; end; Function PosBMH (const Find, S : String; const StartIndex : Integer = 1; const StopIndex : Integer = 0) : Integer; { } { FindFirstPos / FindNextPos Iterators } { The iterators are implemented as records. } { } { Usage: } { Procedure X; } { var Iterator : TFindStringIterator } { Begin } { if FindFirstPos (Iterator, ...) then } { ... } { } type TFindIterator = record FS : String; FOptions : TFindOptions; FMaxCount : Integer; FStartIndex : Integer; FStopIndex : Integer; Index : Integer; Count : Integer; end; TFindStringIterator = record Iter : TFindIterator; FFind : String; end; PFindStringIterator = ^TFindStringIterator; TFindCharIterator = record Iter : TFindIterator; FFind : Char; end; PFindCharIterator = ^TFindCharIterator; TFindCharSetIterator = record Iter : TFindIterator; FFind : CharSet; end; PFindCharSetIterator = ^TFindCharSetIterator; TFindCharSetArrayIterator = record Iter : TFindIterator; FFind : CharSetArray; end; PFindCharSetArrayIterator = ^TFindCharSetArrayIterator; TFindItemIterator = record Iter : TFindIterator; ItemIndex : Integer; end; { } { FindFirstPos/FindNextPos } { FindFirst/FindNext returns the index of the match or 0 if no more matches. } { Usage pattern: } { I := FindFirstPos (Iterator, Find, S, ...) } { While I > 0 do } { begin } { ... } { I := FindNextPos (Iterator) } { end } { } Function FindFirstPos (var Iterator : TFindStringIterator; const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function FindNextPos (var Iterator : TFindStringIterator) : Integer; overload; Function FindFirstPos (var Iterator : TFindCharIterator; const Find : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function FindNextPos (var Iterator : TFindCharIterator) : Integer; overload; Function FindFirstPos (var Iterator : TFindCharSetIterator; const Find : CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function FindNextPos (var Iterator : TFindCharSetIterator) : Integer; overload; Function FindFirstPosSeq (var Iterator : TFindCharSetArrayIterator; const Find : Array of CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function FindNextPosSeq (var Iterator : TFindCharSetArrayIterator) : Integer; overload; Function FindFirstPos (var Iterator : TFindItemIterator; const Find : Array of String; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function FindNextPos (var Iterator : TFindItemIterator; const Find : Array of String) : Integer; overload; Function FindFirstPos (var Iterator : TFindItemIterator; const Find : Array of Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function FindNextPos (var Iterator : TFindItemIterator; const Find : Array of Char) : Integer; overload; { } { FindFirstUnmatchedRange/FindNextUnmatchedRange } { Iterates through all the ranges (StartIndex..StopIndex) inbetween matches. } { } Function FindFirstUnmatchedRange (var Iterator : TFindStringIterator; var StartIndex, StopIndex : Integer; const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Boolean; overload; Function FindNextUnmatchedRange (var Iterator : TFindStringIterator; var StartIndex, StopIndex : Integer) : Boolean; overload; Function FindFirstUnmatchedRange (var Iterator : TFindCharIterator; var StartIndex, StopIndex : Integer; const Find : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Boolean; overload; Function FindNextUnmatchedRange (var Iterator : TFindCharIterator; var StartIndex, StopIndex : Integer) : Boolean; overload; Function FindFirstUnmatchedRange (var Iterator : TFindCharSetIterator; var StartIndex, StopIndex : Integer; const Find : CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Boolean; overload; Function FindNextUnmatchedRange (var Iterator : TFindCharSetIterator; var StartIndex, StopIndex : Integer) : Boolean; overload; Function FindFirstUnmatchedRangeSeq (var Iterator : TFindCharSetArrayIterator; var StartIndex, StopIndex : Integer; const Find : Array of CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Boolean; overload; Function FindNextUnmatchedRangeSeq (var Iterator : TFindCharSetArrayIterator; var StartIndex, StopIndex : Integer) : Boolean; overload; Function FindFirstUnmatchedRange (var Iterator : TFindItemIterator; var StartIndex, StopIndex : Integer; const Find : Array of String; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Boolean; overload; Function FindNextUnmatchedRange (var Iterator : TFindItemIterator; var StartIndex, StopIndex : Integer; const Find : Array of String) : Boolean; overload; { } { IterateMatches } { IterateMatches iterate through all matches, calling VisitProcedure for } { every match. The Data parameter is passed along with every call. } { IterateMatches returns the number of matches iterated. } { The callback procedure is called with the match Nr, the Index of the } { match, the passed Data paramater and a Continue variable that can be } { cleared to stop the iteration. } { } type TMatchVisitProcedure = Procedure (const Nr, Index : Integer; const Data : Pointer; var Continue : Boolean; const Iterator : TFindIterator); Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function IterateMatchesSeq (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Array of CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Array of String; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; { } { Count } { Returns the number of occurances of Find in S. } { Start and Stop can be negative to refence indexes from the back. } { If MaxCount = -1 there is no upper limit for counting. } { } Function Count (const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function Count (const Find : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function Count (const Find : CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function CountSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; Function Count (const Find : Array of String; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : Integer; overload; { } { PosEx } { Extended Pos function. } { Returns the index of the Count-th occurance of Find in S (0 if not found). } { Start and Stop can be negative to refence indexes from the back. } { } Function PosEx (const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : Integer; overload; Function PosEx (const Find : CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : Integer; overload; Function PosEx (const Find : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : Integer; overload; Function PosExSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : Integer; overload; { } { FindAll } { Returns an IntegerArray with the indexes of all matched positions. } { Start and Stop can be negative to refence indexes from the back. } { Set MaxCount = -1 for no limit. } { If Algorithm = faSingleAllocation then the memory for the result is } { allocated once, by first counting the matches. This can be faster in } { some cases where a lot of matches are returned. } { } type TFindAllAlgorithm = (faSingleAllocation, faSingleIteration); Function FindAll (const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TFindAllAlgorithm = faSingleIteration) : IntegerArray; overload; Function FindAll (const Find : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TFindAllAlgorithm = faSingleIteration) : IntegerArray; overload; Function FindAll (const Find : CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TFindAllAlgorithm = faSingleIteration) : IntegerArray; overload; Function FindAllSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TFindAllAlgorithm = faSingleIteration) : IntegerArray; overload; { } { Split/Join } { Function Split splits S into an array on a Delimiter. If Delimiter='' } { or S='' then Split returns an empty array. If Token not found in S, } { it returns an array with one element, S. } { Works for foReverse option. foOverlapping option ignored. } { Procedure Split splits S into two parts. } { If SplitPosition = splitLeft, the Delimiter is part of LeftSide; for } { splitRight the Delimiter is part of RightSide; and for splitCenter } { the Delimiter is not part of LeftSide nor RightSide. } { If Delimiter is not found and DelimiterOptional = True then LeftSide = S } { else if DelimiterOptional = False then LeftSide = ''. } { Do not use it as follow: } { Split (S, Delimiter, S, T). Delphi's reference counting gets confused. } { } type TSplitAlgorithm = (saSingleAllocation, saSingleIteration); Function Split (const S, Delimiter : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TSplitAlgorithm = saSingleIteration) : StringArray; overload; Function Split (const S : String; const Delimiter : Char = c_Space; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TSplitAlgorithm = saSingleIteration) : StringArray; overload; Function Split (const S : String; const Delimiter : CharSet = cs_WhiteSpace; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TSplitAlgorithm = saSingleIteration) : StringArray; overload; Function Join (const S : Array of String; const Delimiter : String = c_Space; const Start : Integer = 0) : String; type TSplitPosition = (splitCenter, splitLeft, splitRight); Procedure Split (const S, Delimiter : String; var LeftSide, RightSide : String; const DelimiterOptional : Boolean = True; const SplitPosition : TSplitPosition = splitCenter; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1); overload; Procedure Split (const S : String; const Delimiter : CharSet; var LeftSide, RightSide : String; const DelimiterOptional : Boolean = True; const SplitPosition : TSplitPosition = splitCenter; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1); overload; Function ExtractWords (const S : String; const WordChars : CharSet) : StringArray; { } { Cut } { Cut returns and deletes the specified characters from S. } { } Function Cut (var S : String; const Index, Count : Integer) : String; Function CutLeft (var S : String; const Count : Integer) : String; Function CutRight (var S : String; const Count : Integer) : String; Function CutTo (var S : String; const Delimiter : Char; const DelimiterOptional : Boolean = True; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; { } { Replace } { Replace returns a string with Find replaced with Replace. } { If MaxCount = -1 then all occurances off Find is replaced. } { ReplaceChars uses arrays parameters for Find and Replace. } { It replaces the Find entries with their associated Replace entries. } { Find and Replace must have an equal number of entries. } { Remove removes all characters in Ch from S. } { RemoveDup replaces all duplicate occurances of Ch with a single occurance. } { } type TReplaceAlgorithm = (raSingleAllocation, raSingleIteration); Function Replace (const Find, Replace, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload; Function ReplaceSeq (const Find : Array of CharSet; const Replace, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload; Function Replace (const Find : Char; const Replace, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload; Function Replace (const Find : CharSet; const Replace, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1; const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload; Function Replace (const Find, Replace : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : String; overload; Function Replace (const Find : CharSet; const Replace : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : String; overload; Function ReplaceChars (const Find, Replace : Array of Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const MaxCount : Integer = -1) : String; Function RemoveAll (const Find : Char; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload; Function RemoveAll (const Find : CharSet; const S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload; Function RemoveAll (const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload; Function RemoveFirst (const Find, S : String; const Options : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload; Function RemoveSeq (const C : Array of CharSet; const S : String) : String; Function RemoveDup (const C : Char; const S : String) : String; { } { Delimiter-based Copy } { Similar to Copy functions, but use Delimiters instead of indexes. } { Returns S [Start..Stop] instead of '' if DelimiterOptional and the } { Delimiter is not found in S. } { RemoveBetween := CopyBefore (CopyAfter (S, LeftDelimiter), RightDelimiter) } { For Count <= 0 the Delimiter is not located and result is same as } { when doOptional and Delimiter not found. } { } type TDelimiterOption = (doOptional, doIncludeDelimiter); TDelimiterOptions = Set of TDelimiterOption; Function CopyLeft (const S, Delimiter : String; const DelimiterOptions : TDelimiterOptions = [doOptional]; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyLeft (const S : String; const Delimiter : CharSet; const DelimiterOptions : TDelimiterOptions = [doOptional]; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyRight (const S, Delimiter : String; const DelimiterOptions : TDelimiterOptions = []; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyRight (const S : String; const Delimiter : CharSet; const DelimiterOptions : TDelimiterOptions = []; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyRange (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptions : TDelimiterOptions = []; const RightDelimiterOptions : TDelimiterOptions = [doOptional]; const NotRange : Boolean = False; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function CopyRange (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptions : TDelimiterOptions = []; const RightDelimiterOptions : TDelimiterOptions = [doOptional]; const NotRange : Boolean = False; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function CopyRange (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptions : TDelimiterOptions = []; const RightDelimiterOptions : TDelimiterOptions = [doOptional]; const NotRange : Boolean = False; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function CopyRange (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptions : TDelimiterOptions = []; const RightDelimiterOptions : TDelimiterOptions = [doOptional]; const NotRange : Boolean = False; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function CopyFrom (const S, Delimiter : String; const DelimiterOptional : Boolean = False; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyFrom (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean = False; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyAfter (const S, Delimiter : String; const DelimiterOptional : Boolean = False; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyAfter (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean = False; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyTo (const S, Delimiter : String; const DelimiterOptional : Boolean = True; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyTo (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean = True; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyBefore (const S, Delimiter : String; const DelimiterOptional : Boolean = True; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyBefore (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean = True; const FindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const Count : Integer = 1) : String; overload; Function CopyBetween (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = True; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function CopyBetween (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = True; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function CopyBetween (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = True; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function CopyBetween (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = True; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function RemoveBetween (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = True; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function RemoveBetween (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = True; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function RemoveBetween (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = True; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function RemoveBetween (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = True; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function Remove (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = False; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function Remove (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = False; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function Remove (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = False; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; Function Remove (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean = False; const RightDelimiterOptional : Boolean = False; const LeftFindOptions : TFindOptions = []; const RightFindOptions : TFindOptions = []; const Start : Integer = 1; const Stop : Integer = -1; const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload; { } { Quoting, Escaping and Translating } { } { EscapeText/UnescapeText converts text where escaping is done with a } { single character (EscapePrefix) followed by a single character identifier } { (EscapeChar). } { When AlwaysDropPrefix = True, the prefix will be dropped from the } { resulting string if it is not followed by one of EscapeChar. } { Examples: } { S := EscapeText (S, [#0, #10, #13, '\'], '\', ['0', 'n', 'r', '\']); } { S := UnescapeText (S, '\', ['0', 'n', 'r', '\'], [#0, #10, #13, '\']); } { S := EscapeText (S, [''''], '''', ['''']); } { } { QuoteText, UnquoteText converts text where the string is enclosed in a } { pair of the same quote characters, and two consequetive occurance of the } { quote character inside the quotes indicate a quote character in the text. } { Examples: } { QuoteText ('abc', '"') = '"abc"' } { QuoteText ('a"b"c', '"') = '"a""b""c"' } { UnquoteText ('"a""b""c"') = 'a"b"c' } { } { RemoveQuotes simply removes opening and closing quotes around a string. } { } Function EscapeText (const S : String; const CharsToEscape : Array of Char; const EscapePrefix : Char; const EscapeChar : Array of Char) : String; Function UnescapeText (const S : String; const EscapePrefix : Char; const EscapeChar : Array of Char; const Replacement : Array of String; const AlwaysDropPrefix : Boolean = False) : String; Function CEscapeText (const S : String) : String; Function CUnescapeText (const S : String) : String; Function QuoteText (const S : String; const Quotes : Char = '''') : String; Function UnquoteText (const S : String) : String; Function RemoveQuotes (const S : String; const Quotes : Char = '''') : String; Function HexEscapeText (const S : String; const CharsToEscape : CharSet; const EscapePrefix : String; const EscapePostfix : String = ''; const UpperHex : Boolean = False; const AlwaysTwoDigits : Boolean = True) : String; Function HexUnescapeText (const S : String; const EscapePrefix : Char) : String; Function FindClosingQuote (const S : String; const OpenQuotePos : Integer = 1) : Integer; Function EncodeDotLineTerminated (const S : String) : String; Function EncodeEmptyLineTerminated (const S : String) : String; Function DecodeDotLineTerminated (const S : String) : String; Function DecodeEmptyLineTerminated (const S : String) : String; Function SplitQuotedList (const S : String; const Delimiter : String = ' '; const Quotes : CharSet = ['''', '"']) : StringArray; { } { Natural language } { } Function Number (const Num : Int64; const USStyle : Boolean = False) : String; overload; Function Number (const Num : Extended; const USStyle : Boolean = False) : String; overload; Function StorageSize (const Bytes : Int64; const ShortFormat : Boolean = False) : String; Function TransferRate (const Bytes, MillisecondsElapsed : Int64; const ShortFormat : Boolean = False) : String; { } { Pack/Unpack } { Packs paramater (in its binary format) into a string } { } Function Pack (const D : Int64) : String; overload; Function Pack (const D : Integer) : String; overload; Function Pack (const D : Byte) : String; overload; Function Pack (const D : ShortInt) : String; overload; Function Pack (const D : SmallInt) : String; overload; Function Pack (const D : Word) : String; overload; Function Pack (const D : String) : String; overload; Function PackShortString (const D : ShortString) : String; Function Pack (const D : Extended) : String; overload; Function PackSingle (const D : Single) : String; Function PackDouble (const D : Double) : String; Function PackCurrency (const D : Currency) : String; Function PackDateTime (const D : TDateTime) : String; Function Pack (const D : Boolean) : String; overload; Function UnpackInteger (const D : String) : Integer; Function UnpackSingle (const D : String) : Single; Function UnpackDouble (const D : String) : Double; Function UnpackExtended (const D : String) : Extended; Function UnpackBoolean (const D : String) : Boolean; Function UnpackDateTime (const D : String) : TDateTime; Function UnpackString (const D : String) : String; Function UnpackShortString (const D : String) : ShortString; { } { PChar routines } { } Function MatchString (const P : PChar; const S : String; const CaseSensitive : Boolean = True) : Boolean; Function SkipChar (var P : PChar; const C : Char) : Boolean; overload; Function SkipChar (var P : PChar; const C : CharSet) : Boolean; overload; Function SkipAll (var P : PChar; const C : Char) : Integer; overload; Function SkipAll (var P : PChar; const C : CharSet) : Integer; overload; Function SkipSeq (var P : PChar; const S1, S2 : CharSet) : Boolean; overload; Function SkipSeq (var P : PChar; const S1, S2, S3 : CharSet) : Boolean; overload; Function SkipString (var P : PChar; const S : String; const CaseSensitive : Boolean = True) : Boolean; Function ExtractAll (var P : PChar; const C : Char) : String; overload; Function ExtractAll (var P : PChar; const C : CharSet) : String; overload; Function ExtractTo (var P : PChar; const C : CharSet) : String; overload; Function ExtractTo (var P : PChar; const S : String; const CaseSensitive : Boolean = True) : String; overload; { } { Dynamic array functions } { } Function StringArrayLength (const S : Array of String) : Integer; Function LongestStringLength (const S : Array of String) : Integer; Function Append (var V : CharSetArray; const S : String; const CaseSensitive : Boolean = True) : Integer; overload; Function PosNext (const Find : String; const V : StringArray; const PrevPos : Integer; const IsSortedAscending : Boolean; const CaseSensitive : Boolean) : Integer; overload; Function SingleArrayToStringArray (const V : SingleArray) : StringArray; Function DoubleArrayToStringArray (const V : DoubleArray) : StringArray; Function ExtendedArrayToStringArray (const V : ExtendedArray) : StringArray; Function LongIntArrayToStringArray (const V : LongIntArray) : StringArray; Function Int64ArrayToStringArray (const V : Int64Array) : StringArray; Function StringArrayToLongIntArray (const V : StringArray) : LongIntArray; Function StringArrayToInt64Array (const V : StringArray) : Int64Array; Function StringArrayToSingleArray (const V : StringArray) : SingleArray; Function StringArrayToDoubleArray (const V : StringArray) : DoubleArray; Function StringArrayToExtendedArray (const V : StringArray) : ExtendedArray; Function ByteArrayToStr (const V : ByteArray; const ItemDelimiter : String = ',') : String; Function WordArrayToStr (const V : WordArray; const ItemDelimiter : String = ',') : String; Function LongWordArrayToStr (const V : LongWordArray; const ItemDelimiter : String = ',') : String; Function CardinalArrayToStr (const V : CardinalArray; const ItemDelimiter : String = ',') : String; Function ShortIntArrayToStr (const V : ShortIntArray; const ItemDelimiter : String = ',') : String; Function SmallIntArrayToStr (const V : SmallIntArray; const ItemDelimiter : String = ',') : String; Function LongIntArrayToStr (const V : LongIntArray; const ItemDelimiter : String = ',') : String; Function IntegerArrayToStr (const V : IntegerArray; const ItemDelimiter : String = ',') : String; Function Int64ArrayToStr (const V : Int64Array; const ItemDelimiter : String = ',') : String; Function SingleArrayToStr (const V : SingleArray; const ItemDelimiter : String = ',') : String; Function DoubleArrayToStr (const V : DoubleArray; const ItemDelimiter : String = ',') : String; Function ExtendedArrayToStr (const V : ExtendedArray; const ItemDelimiter : String = ',') : String; Function StringArrayToStr (const V : StringArray; const ItemDelimiter : String = ','; const QuoteItems : Boolean = True; const Quote : Char = '''') : String; Function StrToByteArray (const S : String; const Delimiter : Char = ',') : ByteArray; Function StrToWordArray (const S : String; const Delimiter : Char = ',') : WordArray; Function StrToLongWordArray (const S : String; const Delimiter : Char = ',') : LongWordArray; Function StrToCardinalArray (const S : String; const Delimiter : Char = ',') : CardinalArray; Function StrToShortIntArray (const S : String; const Delimiter : Char = ',') : ShortIntArray; Function StrToSmallIntArray (const S : String; const Delimiter : Char = ',') : SmallIntArray; Function StrToLongIntArray (const S : String; const Delimiter : Char = ',') : LongIntArray; Function StrToIntegerArray (const S : String; const Delimiter : Char = ',') : IntegerArray; Function StrToInt64Array (const S : String; const Delimiter : Char = ',') : Int64Array; Function StrToSingleArray (const S : String; const Delimiter : Char = ',') : SingleArray; Function StrToDoubleArray (const S : String; const Delimiter : Char = ',') : DoubleArray; Function StrToExtendedArray (const S : String; const Delimiter : Char = ',') : ExtendedArray; Function StrToStringArray (const S : String; const Delimiter : Char = ',') : StringArray; { } { Miscellaneous } { } Function Reversed (const S : String) : String; Function WithSuffix (const S : String; const Suffix : String; const CaseSensitive : Boolean = True) : String; Function WithPrefix (const S : String; const Prefix : String; const CaseSensitive : Boolean = True) : String; Function WithoutSuffix (const S : String; const Suffix : String; const CaseSensitive : Boolean = True) : String; Function WithoutPrefix (const S : String; const Prefix : String; const CaseSensitive : Boolean = True) : String; Procedure EnsureSuffix (var S : String; const Suffix : String; const CaseSensitive : Boolean = True); Procedure EnsurePrefix (var S : String; const Prefix : String; const CaseSensitive : Boolean = True); Procedure EnsureNoSuffix (var S : String; const Suffix : String; const CaseSensitive : Boolean = True); Procedure EnsureNoPrefix (var S : String; const Prefix : String; const CaseSensitive : Boolean = True); Procedure SetLengthAndZero (var S : String; const NewLength : Integer); overload; { } { Self testing code } { } Procedure SelfTest; implementation { } { Type conversion } { } Function StrToFloatDef (const S : String; const Default : Extended) : Extended; Begin try Result := StrToFloat (S); except Result := Default; end; End; Function BooleanToStr (const B : Boolean) : String; Begin if B then Result := 'True' else Result := 'False'; End; Function StrToBoolean (const S : String) : Boolean; Begin Result := IsEqualNoCase (S, 'True'); End; Function TVarRecToString (const V : TVarRec; const QuoteStrings : Boolean) : String; Begin With V do Case VType of vtInteger : Result := IntToStr (VInteger); vtInt64 : Result := IntToStr (VInt64^); vtChar : Result := VChar; vtString : Result := VString^; vtPChar : Result := VPChar; vtAnsiString : Result := String (VAnsiString); vtExtended : Result := FloatToStr (VExtended^); vtBoolean : Result := BooleanToStr (VBoolean); vtObject : Result := ObjectToStr (VObject); vtClass : Result := ClassToStr (VClass); vtCurrency : Result := CurrToStr (VCurrency^); vtVariant : Result := String (VVariant^); end; if QuoteStrings and (V.VType in [vtChar, vtString, vtPChar, vtAnsiString]) then Result := QuoteText (Result); End; { } { Miscellaneous } { } {$IFDEF WINTEL} Function LowCase (Ch : Char) : Char; Asm CMP AL,'A' JB @@exit CMP AL,'Z' JA @@exit ADD AL,'a' - 'A' @@exit: End; {$ELSE} Function LowCase (Ch : Char) : Char; Begin if Ch in ['A'..'Z'] then Result := Char (Byte (Ch) - 32) else Result := Ch; End; {$ENDIF} {$IFDEF WINTEL} Procedure ConvertUpper (var S : String); Asm OR EAX, EAX JZ @Exit PUSH EAX MOV EAX, [EAX] OR EAX, EAX JZ @ExitP MOV ECX, [EAX - 4] OR ECX, ECX JZ @ExitP XOR DH, DH @L2: DEC ECX MOV DL, [EAX + ECX] CMP DL, 'a' JB @L1 CMP DL, 'z' JA @L1 OR DH, DH JZ @Uniq @L3: SUB DL, 'a' - 'A' MOV [EAX + ECX], DL @L1: OR ECX, ECX JNZ @L2 OR DH, DH JNZ @Exit @ExitP: POP EAX @Exit: RET @Uniq: POP EAX PUSH ECX PUSH EDX CALL UniqueString POP EDX POP ECX MOV DH, 1 JMP @L3 End; {$ELSE} Procedure ConvertUpper (var S : String); var F : Integer; Begin For F := 0 to Length (S) - 1 do if S [F] in ['a'..'z'] then S [F] := Char (Ord (S [F]) - 32); End; {$ENDIF} {$IFDEF WINTEL} Procedure ConvertLower (var S : String); Asm OR EAX, EAX JZ @Exit PUSH EAX MOV EAX, [EAX] OR EAX, EAX JZ @ExitP MOV ECX, [EAX - 4] OR ECX, ECX JZ @ExitP XOR DH, DH @L2: DEC ECX MOV DL, [EAX + ECX] CMP DL, 'A' JB @L1 CMP DL, 'Z' JA @L1 OR DH, DH JZ @Uniq @L3: ADD DL, 'a' - 'A' MOV [EAX + ECX], DL @L1: OR ECX, ECX JNZ @L2 OR DH, DH JNZ @Exit @ExitP: POP EAX @Exit: RET @Uniq: POP EAX PUSH ECX PUSH EDX CALL UniqueString POP EDX POP ECX MOV DH, 1 JMP @L3 End; {$ELSE} Procedure ConvertLower (var S : String); var F : Integer; Begin For F := 1 to Length (S) do if S [F] in ['A'..'Z'] then S [F] := Char (Ord (S [F]) + 32); End; {$ENDIF} {$IFDEF WINTEL} Procedure ConvertFirstUp (var S : String); Asm TEST EAX, EAX JZ @Exit MOV EDX, [EAX] TEST EDX, EDX JZ @Exit MOV ECX, [EDX - 4] OR ECX, ECX JZ @Exit MOV DL, [EDX] CMP DL, 'a' JB @Exit CMP DL, 'z' JA @Exit CALL UniqueString SUB BYTE PTR [EAX], 'a' - 'A' @Exit: End; {$ELSE} Procedure ConvertFirstUp (var S : String); var P : PChar; Begin if S <> '' then begin P := Pointer (S); if P^ in ['a'..'z'] then S [1] := UpCase (P^); end; End; {$ENDIF} Function FirstUp (const S : String) : String; Begin Result := S; ConvertFirstUp (Result); End; Procedure ConvertUpper (var S : StringArray); var I : Integer; Begin For I := 0 to Length (S) - 1 do ConvertUpper (S [I]); End; Procedure ConvertLower (var S : StringArray); var I : Integer; Begin For I := 0 to Length (S) - 1 do ConvertLower (S [I]); End; { } { Character class strings } { } Function CharSetToCharClassStr (const C : CharSet) : String; Function ChStr (const Ch : Char) : String; Begin Case Ch of '\' : Result := '\\'; ']' : Result := '\]'; ASCII_BEL : Result := '\a'; ASCII_BS : Result := '\b'; ASCII_ESC : Result := '\e'; ASCII_FF : Result := '\f'; ASCII_LF : Result := '\n'; ASCII_CR : Result := '\r'; ASCII_HT : Result := '\t'; ASCII_VT : Result := '\v'; else if (Ch < #32) or (Ch > #127) then // non-printable Result := '\x' + IntToHex (Ord (Ch), 1) else Result := Ch; end; End; Function SeqStr (const SeqStart, SeqEnd : Char) : String; Begin Result := ChStr (SeqStart); if Ord (SeqEnd) = Ord (SeqStart) + 1 then Result := Result + ChStr (SeqEnd) else // consequetive chars if SeqEnd > SeqStart then // range Result := Result + '-' + ChStr (SeqEnd); End; var CS : CharSet; F : Char; SeqStart : Char; Seq : Boolean; Begin if IsComplete (C) then Result := '.' else if IsEmpty (C) then Result := '[]' else begin Result := '['; CS := C; if (#0 in C) and (#255 in C) then begin ComplementCharSet (CS); Result := Result + '^'; end; Seq := False; SeqStart := #0; For F := #0 to #255 do if F in CS then begin if not Seq then begin SeqStart := F; Seq := True; end; end else if Seq then begin Result := Result + SeqStr (SeqStart, Char (Ord (F) - 1)); Seq := False; end; if Seq then Result := Result + SeqStr (SeqStart, #255); Result := Result + ']'; end; End; Function CharClassStrToCharSet (const S : String) : CharSet; var I, L : Integer; Function DecodeChar : Char; var J : Integer; Begin if S [I] = '\' then if I + 1 = L then begin Inc (I); Result := '\'; end else if not MatchQuantSeq (J, [['x'], cs_HexDigit, cs_HexDigit], [mqOnce, mqOnce, mqOptional], S, [moDeterministic], I + 1) then begin Case S [I + 1] of '0' : Result := ASCII_NULL; 'a' : Result := ASCII_BEL; 'b' : Result := ASCII_BS; 'e' : Result := ASCII_ESC; 'f' : Result := ASCII_FF; 'n' : Result := ASCII_LF; 'r' : Result := ASCII_CR; 't' : Result := ASCII_HT; 'v' : Result := ASCII_VT; else Result := S [I + 1]; end; Inc (I, 2); end else begin if J = I + 2 then Result := Char (HexDigitValue (S [J])) else Result := Char (HexDigitValue (S [J - 1]) * 16 + HexDigitValue (S [J])); I := J + 1; end else begin Result := S [I]; Inc (I); end; End; var Neg : Boolean; A, B : Char; Begin L := Length (S); if (L = 0) or (S = '[]') then Result := [] else if L = 1 then if S [1] in ['.', '*', '?'] then Result := CompleteCharSet else Result := [S [1]] else if (S [1] <> '[') or (S [L] <> ']') then raise EConvertError.Create ('Invalid character class string') else begin Neg := S [2] = '^'; I := iif (Neg, 3, 2); Result := []; While I < L do begin A := DecodeChar; if (I + 1 < L) and (S [I] = '-') then begin Inc (I); B := DecodeChar; Result := Result + [A..B]; end else Include (Result, A); end; if Neg then ComplementCharSet (Result); end; End; { } { Dup } { } Function DupBuf (const Buf; const BufSize : Integer; const Count : Integer) : String; var P : PChar; I : Integer; Begin if (Count <= 0) or (BufSize <= 0) then Result := '' else begin SetLength (Result, Count * BufSize); P := Pointer (Result); For I := 1 to Count do begin MoveMem (Buf, P^, BufSize); Inc (P, BufSize); end; end; End; Function DupBuf (const Buf; const BufSize : Integer) : String; Begin if BufSize <= 0 then Result := '' else begin SetLength (Result, BufSize); MoveMem (Buf, Pointer (Result)^, BufSize); end; End; Function Dup (const S : String; const Count : Integer) : String; var L : Integer; Begin L := Length (S); if L = 0 then Result := '' else Result := DupBuf (Pointer (S)^, L, Count); End; Function Dup (const Ch : Char; const Count : Integer) : String; Begin if Count <= 0 then begin Result := ''; exit; end; SetLength (Result, Count); FillChar (Pointer (Result)^, Count, Ch); End; { } { Copy } { } Function CopyRange (const S : String; const StartIndex, StopIndex : Integer) : String; var L, I : Integer; Begin L := Length (S); if (StartIndex > StopIndex) or (StopIndex < 1) or (StartIndex > L) or (L = 0) then Result := '' else begin if StartIndex <= 1 then if StopIndex >= L then begin Result := S; exit; end else I := 1 else I := StartIndex; Result := Copy (S, I, StopIndex - I + 1); end; End; Function CopyFrom (const S : String; const StartIndex : Integer) : String; var L : Integer; Begin if StartIndex <= 1 then Result := S else begin L := Length (S); if (L = 0) or (StartIndex > L) then Result := '' else Result := Copy (S, StartIndex, L - StartIndex + 1); end; End; Function CopyLeft (const S : String; const Count : Integer) : String; var L : Integer; Begin L := Length (S); if (L = 0) or (Count <= 0) then Result := '' else if Count >= L then Result := S else Result := Copy (S, 1, Count); End; Function CopyRight (const S : String; const Count : Integer) : String; var L : Integer; Begin L := Length (S); if (L = 0) or (Count <= 0) then Result := '' else if Count >= L then Result := S else Result := Copy (S, L - Count + 1, Count); End; Function CutLeft (var S : String; const Count : Integer) : String; Begin if Count <= 0 then Result := '' else begin Result := CopyLeft (S, Count); Delete (S, 1, Count); end; End; Function CutRight (var S : String; const Count : Integer) : String; Begin if Count <= 0 then Result := '' else begin Result := CopyRight (S, Count); SetLength (S, MaxI (0, Length (S) - Count)); end; End; Function Cut (var S : String; const Index, Count : Integer) : String; var L, I, C : Integer; Begin L := Length (S); if (L = 0) or (Count <= 0) or (Index > L) then begin Result := ''; exit; end; I := Index; C := Count; if I <= 0 then begin Inc (C, I - 1); if C <= 0 then begin Result := ''; exit; end; I := 1; end; Result := Copy (S, I, C); Delete (S, I, C); End; Function CutTo (var S : String; const Delimiter : Char; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; var I : Integer; Begin Result := CopyTo (S, Delimiter, DelimiterOptional, FindOptions, Start, Stop, Count); I := Length (Result); if I > 0 then Delete (S, 1, I); End; { } { Match } { } var LowCaseLookup : Array [#0..#255] of Char = ( #$00, #$01, #$02, #$03, #$04, #$05, #$06, #$07, #$08, #$09, #$0A, #$0B, #$0C, #$0D, #$0E, #$0F, #$10, #$11, #$12, #$13, #$14, #$15, #$16, #$17, #$18, #$19, #$1A, #$1B, #$1C, #$1D, #$1E, #$1F, #$20, #$21, #$22, #$23, #$24, #$25, #$26, #$27, #$28, #$29, #$2A, #$2B, #$2C, #$2D, #$2E, #$2F, #$30, #$31, #$32, #$33, #$34, #$35, #$36, #$37, #$38, #$39, #$3A, #$3B, #$3C, #$3D, #$3E, #$3F, #$40, #$61, #$62, #$63, #$64, #$65, #$66, #$67, #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F, #$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77, #$78, #$79, #$7A, #$5B, #$5C, #$5D, #$5E, #$5F, #$60, #$61, #$62, #$63, #$64, #$65, #$66, #$67, #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F, #$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77, #$78, #$79, #$7A, #$7B, #$7C, #$7D, #$7E, #$7F, #$80, #$81, #$82, #$83, #$84, #$85, #$86, #$87, #$88, #$89, #$8A, #$8B, #$8C, #$8D, #$8E, #$8F, #$90, #$91, #$92, #$93, #$94, #$95, #$96, #$97, #$98, #$99, #$9A, #$9B, #$9C, #$9D, #$9E, #$9F, #$A0, #$A1, #$A2, #$A3, #$A4, #$A5, #$A6, #$A7, #$A8, #$A9, #$AA, #$AB, #$AC, #$AD, #$AE, #$AF, #$B0, #$B1, #$B2, #$B3, #$B4, #$B5, #$B6, #$B7, #$B8, #$B9, #$BA, #$BB, #$BC, #$BD, #$BE, #$BF, #$C0, #$C1, #$C2, #$C3, #$C4, #$C5, #$C6, #$C7, #$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF, #$D0, #$D1, #$D2, #$D3, #$D4, #$D5, #$D6, #$D7, #$D8, #$D9, #$DA, #$DB, #$DC, #$DD, #$DE, #$DF, #$E0, #$E1, #$E2, #$E3, #$E4, #$E5, #$E6, #$E7, #$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF, #$F0, #$F1, #$F2, #$F3, #$F4, #$F5, #$F6, #$F7, #$F8, #$F9, #$FA, #$FB, #$FC, #$FD, #$FE, #$FF); {$IFDEF WINTEL} Function MatchNoCase (const A, B : Char) : Boolean; Asm and eax, $000000FF and edx, $000000FF mov al, byte ptr [LowCaseLookup + eax] cmp al, byte ptr [LowCaseLookup + edx] setz al End; {$ELSE} Function MatchNoCase (const A, B : Char) : Boolean; Begin Result := LowCaseLookup [A] = LowCaseLookup [B]; End; {$ENDIF} {$IFDEF WINTEL} Function Match (const A, B : Char; const CaseSensitive : Boolean) : Boolean; Asm or cl, cl jz MatchNoCase cmp al, dl setz al End; {$ELSE} Function Match (const A, B : Char; const CaseSensitive : Boolean) : Boolean; Begin if CaseSensitive then Result := A = B else Result := LowCaseLookup [A] = LowCaseLookup [B]; End; {$ENDIF} {$IFDEF WINTEL} const ModMaskLookup : Array [0..2] of LongWord = ($000000FF, $0000FFFF, $00FFFFFF); Function Match (const M, S : String; const StartIndex : Integer; const CaseSensitive : Boolean) : Boolean; Asm push esi push edi push ebx // Save state mov edi, S // edi = S [1] or edi, edi jz @NoMatch // if S = '' then @NoMatch mov esi, M // esi = M [1] or esi, esi jz @NoMatch // if M = '' then @NoMatch mov edx, StartIndex cmp edx, 1 js @NoMatch // if StartIndex < 1 then @NoMatch add edx, [esi - 4] dec edx // edx = StartIndex + Length (M) - 1 cmp edx, [edi - 4] ja @NoMatch // if StartIndex + Length (M) - 1 > Length (S) then @NoMatch add edi, ecx dec edi // edi = S [StartIndex] mov ecx, [esi - 4] // ecx = Length (M) or ecx, ecx jz @NoMatch // If M = '' then @NoMatch mov bl, CaseSensitive // bl = CaseSensitive or bl, bl jnz @CompareSensitive xor eax, eax xor edx, edx @NextInsensitive: mov al, [esi + ecx - 1] mov dl, [edi + ecx - 1] mov al, byte ptr [LowCaseLookup + eax] cmp al, byte ptr [LowCaseLookup + edx] jne @NoMatch dec ecx jnz @NextInsensitive jmp @Match @CompareSensitive: // rep cmsb // // je @Match // mov dl, cl // and edx, $00000003 // shr ecx, 2 // jz @CheckMod { Length (M) < 4 } // // { rep cmpsd {} // { jne @NoMatch {} // @loop1: {} // mov eax, [esi] {} // cmp eax, [edi] {} // jne @NoMatch {} // add esi, 4 {} // add edi, 4 {} // dec ecx {} // jnz @loop1 {} // // or dl, dl // jz @Match // // { Check remaining dl (0-3) bytes {} // @CheckMod: {} // mov eax, [esi] {} // mov ecx, [edi] {} // dec edx {} // shl edx, 2 {} // and eax, dword ptr [ModMaskLookup + edx] {} // and ecx, dword ptr [ModMaskLookup + edx] {} // cmp eax, ecx je @Match @NoMatch: xor al, al // Result := False jmp @Fin @Match: mov al, 1 // Result := True @Fin: pop ebx // Restore state pop edi pop esi End; {$ELSE} Function Match (const M, S : String; const StartIndex : Integer; const CaseSensitive : Boolean) : Boolean; var I, L : Integer; Begin L := Length (M); if (L = 0) or (L + StartIndex - 1 > Length (S)) then begin Result := False; exit; end; For I := 1 to L do if not Match (M [I], S [StartIndex + I - 1], CaseSensitive) then begin Result := False; exit; end; Result := True; End; {$ENDIF} Function MatchBuf (const M : String; const Buf; const BufSize : Integer; const CaseSensitive : Boolean) : Boolean; var L, I : Integer; P, Q : PChar; Begin L := Length (M); if (L = 0) or (L > BufSize) then begin Result := False; exit; end; P := @Buf; Q := Pointer (M); if CaseSensitive then Result := CompareMem (P^, Q^, L) else begin For I := 1 to L do if not MatchNoCase (P^, Q^) then begin Result := False; exit; end else begin Inc (P); Inc (Q); end; Result := True; end; End; Function Match (const A : CharSet; const B : Char; const CaseSensitive : Boolean) : Boolean; Begin if CaseSensitive then Result := B in A else Result := (UpCase (B) in A) or (LowCase (B) in A); End; Function MatchCount (const M : Char; const S : String; const StartIndex : Integer; const MaxCount : Integer; const CaseSensitive : Boolean) : Integer; var I, Start, Stop : Integer; Begin if MaxCount = 0 then Result := 0 else begin Start := MaxI (StartIndex, 1); if MaxCount < 0 then Stop := Length (S) else Stop := MinI (Start + MaxCount - 1, Length (S)); Result := 0; For I := Start to Stop do if not Match (M, S [I], CaseSensitive) then exit else Inc (Result); end; End; Function Match (const M : Char; const S : String; const StartIndex : Integer; const Count : Integer; const CaseSensitive : Boolean) : Boolean; var StopIndex : Integer; Begin if Count <= 0 then Result := True else begin StopIndex := StartIndex + Count - 1; if (StartIndex < 1) or (StopIndex > Length (S)) then Result := False else Result := MatchCount (M, S, StartIndex, Count, CaseSensitive) = Count; end; End; Function MatchCount (const M : CharSet; const S : String; const StartIndex : Integer; const MaxCount : Integer; const CaseSensitive : Boolean) : Integer; var I, Start, Stop : Integer; Begin if MaxCount = 0 then Result := 0 else begin Start := MaxI (StartIndex, 1); if MaxCount < 0 then Stop := Length (S) else Stop := MinI (Start + MaxCount - 1, Length (S)); Result := 0; For I := Start to Stop do if not Match (M, S [I], CaseSensitive) then exit else Inc (Result); end; End; Function Match (const M : CharSet; const S : String; const StartIndex : Integer; const Count : Integer; const CaseSensitive : Boolean) : Boolean; var StopIndex : Integer; Begin if Count <= 0 then Result := True else begin StopIndex := StartIndex + Count - 1; if (StartIndex < 1) or (StopIndex > Length (S)) then Result := False else Result := MatchCount (M, S, StartIndex, Count, CaseSensitive) = Count; end; End; Function MatchSeq (const M : Array of CharSet; const S : String; const StartIndex : Integer = 1; const CaseSensitive : Boolean = True) : Boolean; var J, C, L : Integer; Begin C := Length (M); L := Length (S); if (C = 0) or (StartIndex < 1) or (StartIndex + C - 1 > L) or (L = 0) then begin Result := False; exit; end; For J := 0 to C - 1 do if not Match (M [J], S [J + StartIndex], CaseSensitive) then begin Result := False; exit; end; Result := True; End; Function MatchLeft (const M, S : String; const CaseSensitive : Boolean) : Boolean; Begin Result := Match (M, S, 1, CaseSensitive); End; Function MatchRight (const M, S : String; const CaseSensitive : Boolean) : Boolean; Begin Result := Match (M, S, Length (S) - Length (M) + 1, CaseSensitive); End; Function IsEqual (const A, B : String; const CaseSensitive : Boolean) : Boolean; var L1, L2 : Integer; Begin L1 := Length (A); L2 := Length (B); Result := L1 = L2; if not Result or (L1 = 0) then exit; Result := Match (A, B, 1, CaseSensitive); End; Function IsEqualNoCase (const A, B : String) : Boolean; var L1, L2 : Integer; Begin L1 := Length (A); L2 := Length (B); Result := L1 = L2; if not Result or (L1 = 0) then exit; Result := Match (A, B, 1, False); End; Function MatchChars (const M : Char; const S : Array of Char; const CaseSensitive : Boolean) : Integer; var I : Integer; Begin For I := 0 to High (S) do if Match (M, S [I], CaseSensitive) then begin Result := I; exit; end; Result := -1; End; Function MatchStrings (const M : String; const S : Array of String; const CaseSensitive : Boolean; const StartIndex : Integer; const MaxMatchLength : Integer) : Integer; var I : Integer; Begin For I := 0 to High (S) do if ((MaxMatchLength < 0) or (Length (S [I]) <= MaxMatchLength)) and Match (S [I], M, StartIndex, CaseSensitive) then begin Result := I; exit; end; Result := -1; End; Function MatchStrings (const M : Array of String; const S : Array of String; var MatchedItem : Integer; const CaseSensitive : Boolean; const MaxMatchLength : Integer) : Integer; overload; var I : Integer; Begin For I := 0 to High (M) do begin Result := MatchStrings (M [I], S, CaseSensitive, 1, MaxMatchLength); if Result >= 0 then begin MatchedItem := I; exit; end; end; Result := -1; MatchedItem := -1; End; { } { Abbreviated regular expression matcher } { } Function MatchQuantSeq (var MatchPos : Integer; const MatchSeq : Array of CharSet; const Quant : Array of TMatchQuantifier; const S : String; const MatchOptions : TMatchQuantSeqOptions; const StartIndex : Integer; const StopIndex : Integer) : Boolean; var Stop : Integer; Deterministic, NonGreedy : Boolean; Function MatchAt (MPos, SPos : Integer; var MatchPos : Integer) : Boolean; Function MatchAndAdvance : Boolean; var I : Integer; Begin I := SPos; Result := S [I] in MatchSeq [MPos]; if Result then begin MatchPos := I; Inc (SPos); end; End; Function MatchAndSetResult (var Res : Boolean) : Boolean; Begin Result := MatchAndAdvance; Res := Result; if not Result then MatchPos := 0; End; Function MatchAny : Boolean; var I, L : Integer; P : PChar; Begin L := Stop; if Deterministic then begin While (SPos <= L) and MatchAndAdvance do ; Result := False; end else if NonGreedy then Repeat Result := MatchAt (MPos + 1, SPos, MatchPos); if Result or not MatchAndAdvance then exit; Until SPos > L else begin I := SPos; P := Pointer (S); Inc (P, I - 1); While (I <= L) and (P^ in MatchSeq [MPos]) do begin Inc (I); Inc (P); end; Repeat MatchPos := I - 1; Result := MatchAt (MPos + 1, I, MatchPos); if Result then exit; Dec (I); Until SPos > I; end; End; var Q : TMatchQuantifier; L, M : Integer; Begin L := Length (MatchSeq); M := Stop; While (MPos < L) and (SPos <= M) do begin Q := Quant [MPos]; if Q in [mqOnce, mqLeastOnce] then if not MatchAndSetResult (Result) then exit; if (Q = mqAny) or ((Q = mqLeastOnce) and (SPos <= M)) then begin Result := MatchAny; if Result then exit; end else if Q = mqOptional then if Deterministic then MatchAndAdvance else begin if NonGreedy then begin Result := MatchAt (MPos + 1, SPos, MatchPos); if Result or not MatchAndSetResult (Result) then exit; end else begin Result := (MatchAndAdvance and MatchAt (MPos + 1, SPos, MatchPos)) or MatchAt (MPos + 1, SPos, MatchPos); exit; end; end; Inc (MPos); end; While (MPos < L) and (Quant [MPos] in [mqAny, mqOptional]) do Inc (MPos); Result := MPos = L; if not Result then MatchPos := 0; End; Begin Assert (Length (MatchSeq) = Length (Quant), 'MatchSeq and Quant not of equal length'); if StopIndex < 0 then Stop := Length (S) else Stop := MinI (StopIndex, Length (S)); MatchPos := 0; if (Length (MatchSeq) = 0) or (StartIndex > Stop) or (StartIndex <= 0) then begin Result := False; exit; end; NonGreedy := moNonGreedy in MatchOptions; Deterministic := moDeterministic in MatchOptions; Result := MatchAt (0, StartIndex, MatchPos); End; Constructor TQuantSeq.Create (const MatchSeq : Array of CharSet; const Quant : Array of TMatchQuantifier; const MatchOptions : TMatchQuantSeqOptions); var I, L : Integer; Begin Assert (Length (MatchSeq) = Length (Quant), 'Incomplete definition'); inherited Create; L := Length (MatchSeq); SetLength (Sequence, L); SetLength (Quantity, L); For I := 0 to L - 1 do begin Sequence [I] := MatchSeq [I]; Quantity [I] := Quant [I]; end; Options := MatchOptions; End; Procedure TQuantSeq.AddToSequence (const Ch : CharSet; const Quant : TMatchQuantifier); var L : Integer; Begin Append (Sequence, Ch); L := Length (Quantity); SetLength (Quantity, L + 1); Quantity [L] := Quant; End; Procedure TQuantSeq.AddStringToSequence (const S : String; const CaseSensitive : Boolean); var I, L : Integer; Begin L := Append (Sequence, S, CaseSensitive); For I := 0 to Length (S) - 1 do Quantity [L + I] := mqOnce; End; Function TQuantSeq.Match (var MatchPos : Integer; const S : String; const StartIndex : Integer; const StopIndex : Integer) : Boolean; Begin Result := MatchQuantSeq (MatchPos, Sequence, Quantity, S, Options, StartIndex, StopIndex); End; { } { MatchPattern } { Based on MatchPattern from a Delphi 3000 article by Paramjeet Reen } { (http://www.delphi3000.com/articles/article_1561.asp). } { } Function MatchPattern (M, S : PChar) : Boolean; Function EscapedChar (const C : Char) : Char; Begin Case C of 'b' : Result := ASCII_BS; 'e' : Result := ASCII_ESC; 'f' : Result := ASCII_FF; 'n' : Result := ASCII_LF; 'r' : Result := ASCII_CR; 't' : Result := ASCII_HT; 'v' : Result := ASCII_VT; else Result := C; end; End; var A, C, D : Char; N : Boolean; Begin Repeat Case M^ of #0 : // end of pattern begin Result := S^ = #0; exit; end; '?' : // match one if S^ = #0 then begin Result := False; exit; end else begin Inc (M); Inc (S); end; '*' : begin Inc (M); if M^ = #0 then // always match at end of mask begin Result := True; exit; end else while S^ <> #0 do if MatchPattern (M, S) then begin Result := True; Exit; end else Inc (S); end; '[' : // character class begin A := S^; Inc (M); C := M^; N := C = '^'; Result := N; While C <> ']' do begin if C = #0 then begin Result := False; exit; end; Inc (M); if C = '\' then // escaped character begin C := M^; if C = #0 then begin Result := False; exit; end; C := EscapedChar (C); Inc (M); end; D := M^; if D = '-' then // match range begin Inc (M); D := M^; if D = #0 then begin Result := False; exit; end; if D = '\' then // escaped character begin Inc (M); D := M^; if D = #0 then begin Result := False; exit; end; D := EscapedChar (D); Inc (M); end; if (A >= C) and (A <= D) then begin Result := not N; break; end; Inc (M); C := M^; end else begin // match single character if A = C then begin Result := not N; break; end; C := D; end; end; if not Result then exit; Inc (S); // Locate closing bracket While M^ <> ']' do if M^ = #0 then begin Result := False; exit; end else Inc (M); Inc (M); end; else // single character match if M^ <> S^ then begin Result := False; exit; end else begin Inc (M); Inc (S); end; end; Until False; End; { } { MatchFileMask } { } Function MatchFileMask (const Mask, Key : String; const CaseSensitive : Boolean) : Boolean; var ML, KL : Integer; Function MatchAt (MaskPos, KeyPos : Integer) : Boolean; Begin While (MaskPos <= ML) and (KeyPos <= KL) do Case Mask [MaskPos] of '?' : begin Inc (MaskPos); Inc (KeyPos); end; '*' : begin While (MaskPos <= ML) and (Mask [MaskPos] = '*') do Inc (MaskPos); if MaskPos > ML then begin Result := True; exit; end; Repeat if MatchAt (MaskPos, KeyPos) then begin Result := True; exit; end; Inc (KeyPos); Until KeyPos > KL; Result := False; exit; end; else if not Match (Mask [MaskPos], Key [KeyPos], CaseSensitive) then begin Result := False; exit; end else begin Inc (MaskPos); Inc (KeyPos); end; end; While (MaskPos <= ML) and (Mask [MaskPos] in ['?', '*']) do Inc (MaskPos); if (MaskPos <= ML) or (KeyPos <= KL) then begin Result := False; exit; end; Result := True; End; Begin ML := Length (Mask); if ML = 0 then begin Result := True; exit; end; KL := Length (Key); Result := MatchAt (1, 1); End; { } { Format testing } { } Function MatchNumber (const S : String; const Index : Integer = 1) : Integer; Begin Result := MatchCount (cs_Numeric, S, Index); End; Function MatchHexNumber (const S : String; const Index : Integer = 1) : Integer; Begin Result := MatchCount (cs_HexDigit, S, Index); End; Function MatchInteger (const S : String; const Index : Integer = 1) : Integer; Begin MatchQuantSeq (Result, [cs_Sign, cs_Numeric], [mqOptional, mqLeastOnce], S, [moDeterministic], Index); if Result > 0 then Dec (Result, Index - 1); End; Function MatchReal (const S : String; const Index : Integer = 1) : Integer; var I, J : Integer; Begin I := MatchInteger (S, Index); MatchQuantSeq (J, [['.'], cs_Numeric], [mqOnce, mqLeastOnce], S, [moDeterministic], Index + I); if J > 0 then Dec (J, Index + I - 1); Result := I + J; End; Function MatchSciReal (const S : String; const Index : Integer = 1) : Integer; var I : Integer; Begin Result := MatchReal (S, Index); if (Result = 0) or (Result + Index > Length (S)) then exit; MatchQuantSeq (I, [['E', 'e'], cs_Sign, cs_Numeric], [mqOnce, mqOptional, mqLeastOnce], S, [moDeterministic], Index + Result); if I > 0 then Inc (Result, I - (Index + Result - 1)); End; Function MatchQuotedString (const S : String; const ValidQuotes : CharSet; const Index : Integer) : Integer; var Quote : Char; I, L : Integer; R : Boolean; Begin L := Length (S); if (Index < 1) or (L < Index + 1) or not (S [Index] in ValidQuotes) then begin Result := 0; exit; end; Quote := S [Index]; I := Index + 1; R := False; Repeat I := Pos (Quote, S, [], I); if I = 0 then // no closing quote begin Result := 0; exit; end else if I = L then // closing quote is last character R := True else if S [I + 1] <> Quote then // not double quoted R := True else Inc (I, 2); Until R; Result := I - Index + 1; End; Function IsNumber (const S : String) : Boolean; var L : Integer; Begin L := Length (S); Result := (L > 0) and (MatchNumber (S) = L); End; Function IsHexNumber (const S : String) : Boolean; var L : Integer; Begin L := Length (S); Result := (L > 0) and (MatchHexNumber (S) = L); End; Function IsInteger (const S : String) : Boolean; var L : Integer; Begin L := Length (S); Result := (L > 0) and (MatchInteger (S) = L); End; Function IsReal (const S : String) : Boolean; var L : Integer; Begin L := Length (S); Result := (L > 0) and (MatchReal (S) = L); End; Function IsSciReal (const S : String) : Boolean; var L : Integer; Begin L := Length (S); Result := (L > 0) and (MatchSciReal (S) = L); End; Function IsQuotedString (const S : String; const ValidQuotes : CharSet) : Boolean; var L : Integer; Begin L := Length (S); if (L < 2) or (S [1] <> S [L]) or not (S [1] in ValidQuotes) then Result := False else Result := MatchQuotedString (S, ValidQuotes) = L; End; Function IsHexDigit (const C : Char) : Boolean; Begin Result := C in ['0'..'9', 'A'..'F', 'a'..'f']; End; Function HexDigitValue (const C : Char) : Byte; Begin Case C of '0'..'9' : Result := Byte (C) - Byte ('0'); 'A'..'F' : Result := Byte (C) - Byte ('A') + 10; 'a'..'f' : Result := Byte (C) - Byte ('a') + 10; else raise EConvertError.Create ('Not a valid hex digit'); end; End; { } { Trim } { } Function TrimLeft (const S : String; const TrimSet : CharSet) : String; var F, L : Integer; Begin L := Length (S); F := 1; While (F <= L) and (S [F] in TrimSet) do Inc (F); Result := CopyFrom (S, F); End; Procedure TrimLeftInPlace (var S : String; const TrimSet : CharSet); var F, L : Integer; P : PChar; Begin L := Length (S); F := 1; While (F <= L) and (S [F] in TrimSet) do Inc (F); if F > L then S := '' else if F > 1 then begin L := L - F + 1; if L > 0 then begin P := Pointer (S); Inc (P, F - 1); MoveMem (P^, Pointer (S)^, L); end; SetLength (S, L); end; End; Function TrimLeftStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean) : String; var F, L, M : Integer; Begin L := Length (TrimStr); M := Length (S); F := 1; While (F <= M) and Match (TrimStr, S, F, CaseSensitive) do Inc (F, L); Result := CopyFrom (S, F); End; Function TrimRight (const S : String; const TrimSet : CharSet) : String; var F : Integer; Begin F := Length (S); While (F >= 1) and (S [F] in TrimSet) do Dec (F); Result := CopyLeft (S, F); End; Procedure TrimRightInPlace (var S : String; const TrimSet : CharSet); var F : Integer; Begin F := Length (S); While (F >= 1) and (S [F] in TrimSet) do Dec (F); if F = 0 then S := '' else SetLength (S, F); End; Function TrimRightStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean) : String; var F, L : Integer; Begin L := Length (TrimStr); F := Length (S) - L + 1; While (F >= 1) and Match (TrimStr, S, F, CaseSensitive) do Dec (F, L); Result := CopyLeft (S, F + L - 1); End; Function Trim (const S : String; const TrimSet : CharSet) : String; var F, G, L : Integer; Begin L := Length (S); F := 1; While (F <= L) and (S [F] in TrimSet) do Inc (F); G := L; While (G >= F) and (S [G] in TrimSet) do Dec (G); Result := CopyRange (S, F, G); End; Procedure TrimInPlace (var S : String; const TrimSet : CharSet); Begin TrimLeftInPlace (S, TrimSet); TrimRightInPlace (S, TrimSet); End; Function TrimStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean) : String; var F, G, L : Integer; Begin L := Length (S); F := 1; While (F <= L) and Match (TrimStr, S, F, CaseSensitive) do Inc (F); G := L; While (G >= F) and Match (TrimStr, S, G, CaseSensitive) do Dec (G); Result := CopyRange (S, F, G); End; Procedure Trim (var S : StringArray; const TrimSet : CharSet); var I : Integer; Begin For I := 0 to Length (S) - 1 do TrimInPlace (S [I], TrimSet); End; Procedure TrimStr (var S : StringArray; const TrimStr : String; const CaseSensitive : Boolean); var I : Integer; Begin For I := 0 to Length (S) - 1 do S [I] := cStrings.TrimStr (S [I], TrimStr, CaseSensitive); End; Function TrimEllipse (const S : String; const Length : Integer) : String; Begin if System.Length (S) <= Length then Result := S else if Length < 3 then Result := Dup ('.', Length) else Result := CopyLeft (S, Length - 3) + '...'; End; Function TrimQuotes (const S : String; const Quotes : CharSet) : String; var L : Integer; Begin L := Length (S); if (L >= 2) and (S [1] = S [L]) and (S [1] in Quotes) then Result := Copy (S, 2, L - 2) else Result := S; End; { } { Pad } { } Function PadLeft (const S : String; const PadChar : Char; const Length : Integer; const Cut : Boolean = False) : String; var F, L, P, M : Integer; I, J : PChar; Begin if Length = 0 then begin if Cut then Result := '' else Result := S; exit; end; M := System.Length (S); if Length = M then begin Result := S; exit; end; if Cut then L := Length else L := MaxI (Length, M); P := MaxI (0, L - M); SetLength (Result, L); if P > 0 then FillChar (Pointer (Result)^, P, PadChar); if L > P then begin I := Pointer (Result); J := Pointer (S); Inc (I, P); For F := 1 to L - P do begin I^ := J^; Inc (I); Inc (J); end; end; End; Function PadRight (const S : String; const PadChar : Char; const Length : Integer; const Cut : Boolean = False) : String; var F, L, P, M : Integer; I, J : PChar; Begin if Length = 0 then begin if Cut then Result := '' else Result := S; exit; end; M := System.Length (S); if Length = M then begin Result := S; exit; end; if Cut then L := Length else L := MaxI (Length, M); P := MaxI (0, L - M); SetLength (Result, L); if L > P then begin I := Pointer (Result); J := Pointer (S); For F := 1 to L - P do begin I^ := J^; Inc (I); Inc (J); end; end; if P > 0 then FillChar (Result [L - P + 1], P, PadChar); End; Function Pad (const S : String; const PadChar : Char; const Length : Integer; const Cut : Boolean) : String; var I : Integer; Begin I := Length - System.Length (S); Result := Dup (PadChar, I div 2) + S + Dup (PadChar, (I + 1) div 2); if Cut then SetLength (Result, Length); End; Function Pad (const I : Integer; const Length : Integer; const Cut : Boolean) : String; Begin Result := PadLeft (IntToStr (I), '0', Length, Cut); End; {$WARNINGS OFF} Function PadInside (const S : String; const PadChar : Char; const Length : Integer) : String; var I, J, K, C, M : Integer; P : CharSetArray; Begin if System.Length (S) >= Length then begin Result := S; exit; end; P := AsCharSetArray ([[PadChar], cs_AllChars - [PadChar]]); I := CountSeq (P, S); if I = 0 then // nowhere to pad inside begin Result := S; exit; end; C := (Length - System.Length (S)) div I; M := (Length - System.Length (S)) mod I; I := PosSeq (P, S); K := 0; Result := CopyLeft (S, I); Repeat Result := Result + Dup (PadChar, C); if K < M then Result := Result + PadChar; Inc (K); J := I; I := PosSeq (P, S, [], J + 1); if I > 0 then Result := Result + CopyRange (S, J + 1, I); Until I = 0; Result := Result + CopyFrom (S, J + 1); End; {$WARNINGS ON} Function IntToPadStr (const I : Integer; const PadType : TPadType; const Len : Integer) : String; var J : Integer; N : Boolean; Begin J := I; N := J < 0; if N then J := -J; Result := LongWordToStr (LongWord (J)); if PadType = padLeftZero then Result := PadLeft (Result, '0', Len, False); if N then Result := '-' + Result; Case PadType of padLeftSpace : Result := PadLeft (Result, ' ', Len, False); padRightSpace : Result := PadRight (Result, ' ', Len, False); end; End; { TranslateStartStop translates Start, Stop parameters (negative values are } { indexed from back of string) into StartIdx and StopIdx (relative to start). } { Returns False if the Start, Stop does not specify a valid range. } Function TranslateStart (const S : String; const Start : Integer; var Len, StartIndex : Integer) : Boolean; Begin Len := Length (S); if Len = 0 then Result := False else begin StartIndex := Start; if Start < 0 then Inc (StartIndex, Len + 1); if StartIndex > Len then Result := False else begin if StartIndex < 1 then StartIndex := 1; Result := True; end; end; End; Function TranslateStartStop (const S : String; const Start, Stop : Integer; var Len, StartIndex, StopIndex : Integer) : Boolean; Begin Len := Length (S); if Len = 0 then Result := False else begin StartIndex := Start; if Start < 0 then Inc (StartIndex, Len + 1); StopIndex := Stop; if StopIndex < 0 then Inc (StopIndex, Len + 1); if (StopIndex < 1) or (StartIndex > Len) or (StopIndex < StartIndex) then Result := False else begin if StopIndex > Len then StopIndex:= Len; if StartIndex < 1 then StartIndex := 1; Result := True; end; end; End; { } { Paste } { } Function Paste (const Source : String; var Dest : String; var DestIndex : Integer; const ReverseDirection : Boolean; const SourceStart : Integer; const SourceStop : Integer) : Integer; var SI, SJ, SL, DI, DL : Integer; Begin Result := -1; if not TranslateStartStop (Source, SourceStart, SourceStop, SL, SI, SJ) then exit; if not TranslateStart (Dest, DestIndex, DL, DI) then exit; if ReverseDirection then DI := MaxI (DI - (SJ - SI), 1); Result := MinI (SJ - SI + 1, DL - DI + 1); if Result > 0 then begin MoveMem (Source [SI], Dest [DI], Result); if ReverseDirection then DestIndex := DI - 1 else DestIndex := DI + Result; end; End; { } { CopyEx } { } Function CopyEx (const S : String; const Start, Count : Integer) : String; var I, L : Integer; Begin if (Count < 0) or not TranslateStart (S, Start, L, I) then Result := '' else if (I = 1) and (Count >= L) then Result := S else Result := Copy (S, I, Count); End; Function CopyRangeEx (const S : String; const Start, Stop : Integer) : String; var I, J, L : Integer; Begin if not TranslateStartStop (S, Start, Stop, L, I, J) then Result := '' else if (I = 1) and (J = L) then Result := S else Result := Copy (S, I, J - I + 1); End; Function CopyFromEx (const S : String; const Start : Integer) : String; var I, L : Integer; Begin if not TranslateStart (S, Start, L, I) then Result := '' else if I <= 1 then Result := S else Result := Copy (S, I, L - I + 1); End; { } { Find Options } { } Function FindOptions (const Reverse : Boolean; const CaseInsensitive : Boolean; const Overlapping : Boolean; const NonMatch : Boolean) : TFindOptions; Begin Result := []; if Reverse then Include (Result, foReverse); if CaseInsensitive then Include (Result, foCaseInsensitive); if Overlapping then Include (Result, foOverlapping); if NonMatch then Include (Result, foNonMatch); End; { } { Pos } { } {$IFDEF WINTEL} { Q_PosStr by Andrew N. Driazgov (andrey@asp.tstu.ru) } { Optimized version of the general Pos case. } Function Q_PosStr (const Find, S : String; const StartIndex : Integer) : Integer; Asm PUSH ESI PUSH EDI PUSH EBX PUSH EDX TEST EAX, EAX JE @@qt TEST EDX, EDX JE @@qt0 MOV ESI, EAX MOV EDI, EDX MOV EAX, [EAX - 4] MOV EDX, [EDX - 4] DEC EAX SUB EDX, EAX DEC ECX SUB EDX, ECX JNG @@qt0 XCHG EAX, EDX ADD EDI, ECX MOV ECX, EAX JMP @@nx @@fr: INC EDI DEC ECX JE @@qt0 @@nx: MOV EBX,EDX MOV AL, BYTE PTR [ESI] @@lp1: CMP AL, BYTE PTR [EDI] JE @@uu INC EDI DEC ECX JE @@qt0 CMP AL, BYTE PTR [EDI] JE @@uu INC EDI DEC ECX JE @@qt0 CMP AL, BYTE PTR [EDI] JE @@uu INC EDI DEC ECX JE @@qt0 CMP AL, BYTE PTR [EDI] JE @@uu INC EDI DEC ECX JNE @@lp1 @@qt0: XOR EAX, EAX @@qt: POP ECX POP EBX POP EDI POP ESI RET @@uu: TEST EDX, EDX JE @@fd @@lp2: MOV AL, BYTE PTR [ESI + EBX] CMP AL, BYTE PTR [EDI + EBX] JNE @@fr DEC EBX JE @@fd MOV AL, BYTE PTR [ESI + EBX] CMP AL, BYTE PTR [EDI + EBX] JNE @@fr DEC EBX JE @@fd MOV AL, BYTE PTR [ESI + EBX] CMP AL, BYTE PTR [EDI + EBX] JNE @@fr DEC EBX JE @@fd MOV AL, BYTE PTR [ESI + EBX] CMP AL, BYTE PTR [EDI + EBX] JNE @@fr DEC EBX JNE @@lp2 @@fd: LEA EAX, [EDI + 1] SUB EAX, [ESP] POP ECX POP EBX POP EDI POP ESI End; {$ENDIF} Function PosBuf (const Find : String; const Buf; const BufSize : Integer; const CaseSensitive : Boolean) : Integer; var I : Integer; P : PChar; Begin if Find = '' then begin Result := -1; exit; end; P := @Buf; I := BufSize; While I > 0 do if MatchBuf (Find, P^, I, CaseSensitive) then begin Result := BufSize - I; exit; end else begin Inc (P); Dec (I); end; Result := -1; End; Function Pos (const Find : String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var C, F, I, J, L, G : Integer; CaseSensitive, FindNonMatch, Overlapping : Boolean; Begin C := Length (Find); if (C > 0) and TranslateStartStop (S, Start, Stop, L, I, J) then begin {$IFDEF WINTEL} if (J = L) and (Options = []) then // Optimzation for standard case begin Result := Q_PosStr (Find, S, I); exit; end; {$ENDIF} CaseSensitive := not (foCaseInsensitive in Options); FindNonMatch := foNonMatch in Options; Overlapping := foOverlapping in Options; if foReverse in Options then begin F := J - C + 1; While F >= I do if Match (Find, S, F, CaseSensitive) xor FindNonMatch then begin Result := F; exit; end else if FindNonMatch and not Overlapping then Dec (F, C) else Dec (F); end else begin F := I; G := J - C + 1; While F <= G do if Match (Find, S, F, CaseSensitive) xor FindNonMatch then begin Result := F; exit; end else if FindNonMatch and not Overlapping then Inc (F, C) else Inc (F); end; end; Result := 0; End; Function Pos (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var F, I, J, L : Integer; FindNonMatch : Boolean; P : PChar; Begin if TranslateStartStop (S, Start, Stop, L, I, J) then begin FindNonMatch := foNonMatch in Options; if foReverse in Options then begin if foCaseInsensitive in Options then begin P := Pointer (S); Inc (P, J - 1); For F := J downto I do if MatchNoCase (P^, Find) xor FindNonMatch then begin Result := F; exit; end else Dec (P); end else begin P := Pointer (S); Inc (P, J - 1); For F := J downto I do if (P^ = Find) xor FindNonMatch then begin Result := F; exit; end else Dec (P); end; end else if foCaseInsensitive in Options then begin P := Pointer (S); Inc (P, I - 1); For F := I to J do if MatchNoCase (P^, Find) xor FindNonMatch then begin Result := F; exit; end else Inc (P); end else begin P := Pointer (S); Inc (P, I - 1); For F := I to J do if (P^ = Find) xor FindNonMatch then begin Result := F; exit; end else Inc (P); end; end; Result := 0; End; Function PosNext (const Find : String; const S : String; const LastPos : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var C, L, I, J : Integer; Begin C := Length (Find); if (C > 0) and TranslateStartStop (S, Start, Stop, L, I, J) then begin if LastPos > 0 then if foReverse in Options then J := MinI (J, LastPos - C) else I := MaxI (I, LastPos + C); Result := Pos (Find, S, Options, I, J); end else Result := 0; End; Function PosNextSeq (const Find : Array of CharSet; const S : String; const LastPos : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var C, L, I, J : Integer; Begin C := Length (Find); if (C > 0) and TranslateStartStop (S, Start, Stop, L, I, J) then begin if LastPos > 0 then if foReverse in Options then J := MinI (J, LastPos - C) else I := MaxI (I, LastPos + C); Result := PosSeq (Find, S, Options, I, J); end else Result := 0; End; Function PosNext (const Find : Char; const S : String; const LastPos : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var L, I, J : Integer; Begin if TranslateStartStop (S, Start, Stop, L, I, J) then begin if LastPos > 0 then if foReverse in Options then J := MinI (J, LastPos - 1) else I := MaxI (I, LastPos + 1); Result := Pos (Find, S, Options, I, J); end else Result := 0; End; Function PosNext (const Find : CharSet; const S : String; const LastPos : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var L, I, J : Integer; Begin if TranslateStartStop (S, Start, Stop, L, I, J) then begin if LastPos > 0 then if foReverse in Options then J := MinI (J, LastPos - 1) else I := MaxI (I, LastPos + 1); Result := Pos (Find, S, Options, I, J); end else Result := 0; End; Function PosStrings (const Find : Array of String; const S : String; var FindItem : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var F, I, J, L : Integer; CaseSensitive, FindNonMatch, Overlapping : Boolean; Begin if (High (Find) >= 0) and TranslateStartStop (S, Start, Stop, L, I, J) then begin CaseSensitive := not (foCaseInsensitive in Options); FindNonMatch := foNonMatch in Options; Overlapping := foOverlapping in Options; if foReverse in Options then begin F := J; While F >= I do begin FindItem := MatchStrings (S, Find, CaseSensitive, F, J - F + 1); if (FindItem >= 0) xor FindNonMatch then begin Result := F; exit; end else if FindNonMatch and not Overlapping then Dec (F, Length (Find [FindItem])) else Dec (F); end; end else begin F := I; While F <= J do begin FindItem := MatchStrings (S, Find, CaseSensitive, F, J - F + 1); if (FindItem >= 0) xor FindNonMatch then begin Result := F; exit; end else if FindNonMatch and not Overlapping then Inc (F, Length (Find [FindItem])) else Inc (F); end; end; end; Result := 0; End; Function PosSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var C, F, I, J, L, G : Integer; CaseSensitive, FindNonMatch, Overlapping : Boolean; Begin C := Length (Find); if (C > 0) and TranslateStartStop (S, Start, Stop, L, I, J) then begin CaseSensitive := not (foCaseInsensitive in Options); FindNonMatch := foNonMatch in Options; Overlapping := foOverlapping in Options; if foReverse in Options then begin F := J - C + 1; While F >= I do if MatchSeq (Find, S, F, CaseSensitive) xor FindNonMatch then begin Result := F; exit; end else if FindNonMatch and not Overlapping then Dec (F, C) else Dec (F); end else begin F := I; G := J - C + 1; While F <= G do if MatchSeq (Find, S, F, CaseSensitive) xor FindNonMatch then begin Result := F; exit; end else if FindNonMatch and not Overlapping then Inc (F, C) else Inc (F); end; end; Result := 0; End; Function PosChars (const Find : Array of Char; const S : String; var FindItem : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var F, I, J, L : Integer; CaseSensitive, FindNonMatch : Boolean; Begin if (High (Find) >= 0) and TranslateStartStop (S, Start, Stop, L, I, J) then begin CaseSensitive := not (foCaseInsensitive in Options); FindNonMatch := foNonMatch in Options; if foReverse in Options then begin For F := J downto I do begin FindItem := MatchChars (S [F], Find, CaseSensitive); if (FindItem >= 0) xor FindNonMatch then begin Result := F; exit; end; end; end else For F := I to J do begin FindItem := MatchChars (S [F], Find, CaseSensitive); if (FindItem >= 0) xor FindNonMatch then begin Result := F; exit; end; end; end; Result := 0; End; Function Pos (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer; var F, I, J, L : Integer; CaseSensitive, FindNonMatch : Boolean; Begin if TranslateStartStop (S, Start, Stop, L, I, J) and (Find <> []) then begin CaseSensitive := not (foCaseInsensitive in Options); FindNonMatch := foNonMatch in Options; if foReverse in Options then begin For F := J downto I do if Match (Find, S [F], CaseSensitive) xor FindNonMatch then begin Result := F; exit; end; end else For F := I to J do if Match (Find, S [F], CaseSensitive) xor FindNonMatch then begin Result := F; exit; end; end; Result := 0; End; { Boyer-Moore-Horspool pattern searching } { Converted to a class and rewritten in assembly by David Butler } { (david@e.co.za) from a highly optimized Pascal unit BMH 1.11a written by } { Jody R Cairns (jodyc@cs.mun.ca) as she took it from the 'Handbook of } { Algorithms and Data Structures in Pascal and C', Second Edition, } { by G.H Gonnet and R. Baeza-Yates. } Constructor TBMHSearcher.Create (const Find : String); Begin inherited Create; FFind := Find; { Creates a Boyer-Moore-Horspool index table for the search string } asm push eax push ebx push edx push esi push edi // save state mov eax, self mov edx, offset FTable add edx, eax // edx = FTable [0] mov ebx, [eax + FFind] // ebx = FFind [1] or ebx, ebx jz @exit // FTable [0..255] := Length (FFind) // mov eax, [ebx - 4] { eax = Length (FFind) } // mov edi, edx { edi = FTable [0] } // mov ecx, 256 // rep stosd // // FTable [FFind [i = 1..Length (FFind)]] = Length (FFind) - i // mov ecx, eax { ecx = Length (FFind) } // dec ecx // mov edi, edx { edi = FTable [0] } // xor esi, esi { esi = i - 1 = 0 } // xor edx, edx // @c1: // mov dl, [ebx + esi] { edx = FFind [i] } // mov [edi + edx * 4], ecx { FTable [edx] := Length (FFind) - i } // inc esi // loop @c1 // @exit: pop edi // restore state pop esi pop edx pop ebx pop eax end; End; Function TBMHSearcher.Pos (const S : String; const StartIndex : Integer; const StopIndex : Integer) : Integer; Asm push ebp push ebx push edx push esi push edi // save state push StartIndex push S push self // push parameters pop ebp // ebp = self pop eax // eax = S pop ebx // ebx = StartIndex or eax, eax jz @NoMatch cmp ebx, 1 jae @StartIndexValid mov ebx, 1 @StartIndexValid: mov edx, [eax - 4] push StopIndex pop ecx cmp ecx, 1 jae @StopIndexValid1 mov ecx, edx jmp @StopIndexValid2 @StopIndexValid1: cmp ecx, edx jbe @StopIndexValid2 mov ecx, edx @StopIndexValid2: // ecx = Min (StopIndex, Length (S)) mov ecx, [eax - 4] mov edx, [ebp + FFind] // edx = FFind or edx, edx jz @NoMatch // if FFind = '' then NoMatch add ebx, [edx - 4] dec ebx // ebx = counter, starting at StartIndex + Length (FFind) - 1 // while ebx < Min (Length (s), StopIndex) // @WhileNotEnd: // cmp ebx, ecx // ja @NoMatch // push ecx // // mov ecx, [edx - 4] { loop count = Length (FFind) } // // mov esi, eax // add esi, ebx // sub esi, ecx { esi = S [1 + ebx - Length (FFind)] } // // mov edi, edx { edi = FFind [1] } // // { This is actually faster than REP CMPSB on a Pentium {} // @c1: {} // cmpsb {} // jne @NotEq {} // loop @c1 {} // // pop ecx // jmp @Match { Match found } // // @NotEq: // xor ecx, ecx // mov cl, [eax + ebx - 1] { ecx = S [ebx] } // add ebx, dword ptr [ebp + FTable + ecx * 4] { Inc (ebx, FTable [ecx]) } // // pop ecx // jmp @WhileNotEnd // @NoMatch: xor eax, eax // Result = 0 jmp @Fin @Match: mov eax, ebx inc eax sub eax, [edx - 4] // Result = ebx - Length (FFind) + 1 @Fin: pop edi // Restore state pop esi pop edx pop ebx pop ebp End; Function PosBMH (const Find : String; const S : String; const StartIndex : Integer; const StopIndex : Integer) : Integer; var B : TBMHSearcher; Begin B := TBMHSearcher.Create (Find); try Result := B.Pos (S, StartIndex, StopIndex); finally FreeAndNil (B); end; End; { } { NextStartIndex } { } Function NextStartIndex (const LastPos : Integer; const Options : TFindOptions; const FindLen : Integer) : Integer; Begin if foReverse in Options then begin if not (foOverlapping in Options) and not (foNonMatch in Options) then Result := LastPos - FindLen else Result := LastPos - 1; end else if not (foOverlapping in Options) and not (foNonMatch in Options) then Result := LastPos + FindLen else Result := LastPos + 1; End; { } { FindFirst/FindNext } { } Function FindFirstPos (var Iterator : TFindStringIterator; const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var L : Integer; Begin With Iterator.Iter do begin FS := S; FOptions := Options; FMaxCount := MaxCount; if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then begin Index := 0; Count := 0; Result := 0; exit; end; Result := Pos (Find, S, Options, Start, Stop); Index := Result; if Result > 0 then begin Iterator.FFind := Find; Count := 1; end else Count := 0; end; End; Function FindNextPos (var Iterator : TFindStringIterator) : Integer; Begin With Iterator do With Iter do if Count = FMaxCount then Result := 0 else begin if foReverse in FOptions then begin if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then Dec (Index, Length (FFind)) else Dec (Index); Index := Pos (FFind, FS, FOptions, FStartIndex, Index); end else begin if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then Inc (Index, Length (FFind)) else Inc (Index); Index := Pos (FFind, FS, FOptions, Index, FStopIndex); end; Result := Index; if Result > 0 then Inc (Count); end; End; Function FindFirstPosSeq (var Iterator : TFindCharSetArrayIterator; const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var L : Integer; Begin With Iterator.Iter do begin FS := S; FOptions := Options; FMaxCount := MaxCount; if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then begin Index := 0; Count := 0; Result := 0; exit; end; Result := PosSeq (Find, S, Options, Start, Stop); Index := Result; if Result > 0 then begin Iterator.FFind := AsCharSetArray (Find); Count := 1; end else Count := 0; end; End; Function FindNextPosSeq (var Iterator : TFindCharSetArrayIterator) : Integer; Begin With Iterator do With Iter do if Count = FMaxCount then Result := 0 else begin if foReverse in FOptions then begin if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then Dec (Index, Length (FFind)) else Dec (Index); Index := PosSeq (FFind, FS, FOptions, FStartIndex, Index); end else begin if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then Inc (Index, Length (FFind)) else Inc (Index); Index := PosSeq (FFind, FS, FOptions, Index, FStopIndex); end; Result := Index; if Result > 0 then Inc (Count); end; End; Function FindFirstPos (var Iterator : TFindCharIterator; const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var L : Integer; Begin With Iterator.Iter do begin FS := S; FOptions := Options; FMaxCount := MaxCount; if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then begin Index := 0; Count := 0; Result := 0; exit; end; Result := Pos (Find, S, Options, Start, Stop); Index := Result; if Result > 0 then begin Iterator.FFind := Find; Count := 1; end else Count := 0; end; End; Function FindNextPos (var Iterator : TFindCharIterator) : Integer; Begin With Iterator do With Iter do if Count = FMaxCount then Result := 0 else begin if foReverse in FOptions then begin Dec (Index); Index := Pos (FFind, FS, FOptions, FStartIndex, Index); end else begin Inc (Index); Index := Pos (FFind, FS, FOptions, Index, FStopIndex); end; Result := Index; if Result > 0 then Inc (Count); end; End; Function FindFirstPos (var Iterator : TFindCharSetIterator; const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var L : Integer; Begin With Iterator.Iter do begin FS := S; FOptions := Options; FMaxCount := MaxCount; if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then begin Index := 0; Count := 0; Result := 0; exit; end; Result := Pos (Find, S, Options, Start, Stop); Index := Result; if Result > 0 then begin Iterator.FFind := Find; Count := 1; end else Count := 0; end; End; Function FindNextPos (var Iterator : TFindCharSetIterator) : Integer; Begin With Iterator do With Iter do if Count = FMaxCount then Result := 0 else begin if foReverse in FOptions then begin Dec (Index); Index := Pos (FFind, FS, FOptions, FStartIndex, Index); end else begin Inc (Index); Index := Pos (FFind, FS, FOptions, Index, FStopIndex); end; Result := Index; if Result > 0 then Inc (Count); end; End; Function FindFirstPos (var Iterator : TFindItemIterator; const Find : Array of String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var L : Integer; Begin With Iterator.Iter do begin FS := S; FOptions := Options; FMaxCount := MaxCount; if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then begin Index := 0; Count := 0; Result := 0; exit; end; Result := PosStrings (Find, S, Iterator.ItemIndex, Options, Start, Stop); Index := Result; if Result > 0 then Count := 1 else Count := 0; end; End; Function FindNextPos (var Iterator : TFindItemIterator; const Find : Array of String) : Integer; Begin With Iterator do With Iter do if Count = FMaxCount then Result := 0 else begin if foReverse in FOptions then begin if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then Dec (Index, Length (Find [ItemIndex])) else Dec (Index); Index := PosStrings (Find, FS, ItemIndex, FOptions, FStartIndex, Index); end else begin if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then Inc (Index, Length (Find [ItemIndex])) else Inc (Index); Index := PosStrings (Find, FS, ItemIndex, FOptions, Index, FStopIndex); end; Result := Index; if Result > 0 then Inc (Count); end; End; Function FindFirstPos (var Iterator : TFindItemIterator; const Find : Array of Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var L : Integer; Begin With Iterator.Iter do begin FS := S; FOptions := Options; FMaxCount := MaxCount; if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then begin Index := 0; Count := 0; Result := 0; exit; end; Result := PosChars (Find, S, Iterator.ItemIndex, Options, Start, Stop); Index := Result; if Result > 0 then Count := 1 else Count := 0; end; End; Function FindNextPos (var Iterator : TFindItemIterator; const Find : Array of Char) : Integer; Begin With Iterator do With Iter do if Count = FMaxCount then Result := 0 else begin if foReverse in FOptions then begin if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then Dec (Index, Length (Find [ItemIndex])) else Dec (Index); Index := PosChars (Find, FS, ItemIndex, FOptions, FStartIndex, Index); end else begin if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then Inc (Index, Length (Find [ItemIndex])) else Inc (Index); Index := PosChars (Find, FS, ItemIndex, FOptions, Index, FStopIndex); end; Result := Index; if Result > 0 then Inc (Count); end; End; { } { FindFirstUnmatchedRange/FindNextUnmatchedRange } { } Procedure CalcUnmatchedRange (const Iterator : TFindIterator; const FindLen : Integer; const Index, LastIndex : Integer; var StartIndex, StopIndex : Integer); Begin if Index > 0 then if foReverse in Iterator.FOptions then begin StartIndex := Index + FindLen; StopIndex := LastIndex - 1; end else begin StartIndex := LastIndex + FindLen; StopIndex := Index - 1; end else if foReverse in Iterator.FOptions then begin StartIndex := Iterator.FStartIndex; StopIndex := LastIndex - 1; end else begin StartIndex := LastIndex + FindLen; StopIndex := Iterator.FStopIndex; end; End; Function FindFirstUnmatchedRange (var Iterator : TFindStringIterator; var StartIndex, StopIndex : Integer; const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean; var Index, L : Integer; Begin if (Find = '') or not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then begin Result := False; exit; end; Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount); Result := True; if Index > 0 then if foReverse in Options then StartIndex := Index + Length (Find) else StopIndex := Index - 1; End; Function FindNextUnmatchedRange (var Iterator : TFindStringIterator; var StartIndex, StopIndex : Integer) : Boolean; Begin With Iterator.Iter do begin Result := not ((Count = 0) or (Index = 0)); if Result then CalcUnmatchedRange (Iterator.Iter, Length (Iterator.FFind), FindNextPos (Iterator), Index, StartIndex, StopIndex); end; End; Function FindFirstUnmatchedRangeSeq (var Iterator : TFindCharSetArrayIterator; var StartIndex, StopIndex : Integer; const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean; var Index, L : Integer; Begin if (Length (Find) = 0) or not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then begin Result := False; exit; end; Index := FindFirstPosSeq (Iterator, Find, S, Options, Start, Stop, MaxCount); Result := True; if Index > 0 then if foReverse in Options then StartIndex := Index + Length (Find) else StopIndex := Index - 1; End; Function FindNextUnmatchedRangeSeq (var Iterator : TFindCharSetArrayIterator; var StartIndex, StopIndex : Integer) : Boolean; Begin With Iterator.Iter do begin Result := not ((Count = 0) or (Index = 0)); if Result then CalcUnmatchedRange (Iterator.Iter, Length (Iterator.FFind), FindNextPosSeq (Iterator), Index, StartIndex, StopIndex); end; End; Function FindFirstUnmatchedRange (var Iterator : TFindCharIterator; var StartIndex, StopIndex : Integer; const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean; var Index, L : Integer; Begin if not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then begin Result := False; exit; end; Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount); Result := True; if Index > 0 then if foReverse in Options then StartIndex := Index + 1 else StopIndex := Index - 1; End; Function FindNextUnmatchedRange (var Iterator : TFindCharIterator; var StartIndex, StopIndex : Integer) : Boolean; Begin With Iterator.Iter do begin Result := not ((Count = 0) or (Index = 0)); if Result then CalcUnmatchedRange (Iterator.Iter, Length (Iterator.FFind), FindNextPos (Iterator), Index, StartIndex, StopIndex); end; End; Function FindFirstUnmatchedRange (var Iterator : TFindCharSetIterator; var StartIndex, StopIndex : Integer; const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean; var Index, L : Integer; Begin if not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then begin Result := False; exit; end; Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount); Result := True; if Index > 0 then if foReverse in Options then StartIndex := Index + 1 else StopIndex := Index - 1; End; Function FindNextUnmatchedRange (var Iterator : TFindCharSetIterator; var StartIndex, StopIndex : Integer) : Boolean; Begin With Iterator.Iter do begin Result := not ((Count = 0) or (Index = 0)); if Result then CalcUnmatchedRange (Iterator.Iter, 1, FindNextPos (Iterator), Index, StartIndex, StopIndex); end; End; Function FindFirstUnmatchedRange (var Iterator : TFindItemIterator; var StartIndex, StopIndex : Integer; const Find : Array of String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean; var Index, L : Integer; Begin if (High (Find) < 0) or not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then begin Result := False; exit; end; Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount); Result := True; if Index > 0 then if foReverse in Options then StartIndex := Index + Length (Find [Iterator.ItemIndex]) else StopIndex := Index - 1; End; Function FindNextUnmatchedRange (var Iterator : TFindItemIterator; var StartIndex, StopIndex : Integer; const Find : Array of String) : Boolean; Begin With Iterator.Iter do begin Result := not ((Count = 0) or (Index = 0)); if Result then CalcUnmatchedRange (Iterator.Iter, Length (Find [Iterator.ItemIndex]), FindNextPos (Iterator, Find), Index, StartIndex, StopIndex); end; End; { } { Match Visitor functions } { Handles iterating through matches. } { } Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; overload; var Index : Integer; Continue : Boolean; Iterator : TFindStringIterator; Begin Continue := True; Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount); While Index > 0 do begin if Assigned (VisitProcedure) then begin With Iterator.Iter do VisitProcedure (Count, Index, Data, Continue, Iterator.Iter); if not Continue then break; end; Index := FindNextPos (Iterator); end; Result := Iterator.Iter.Count; End; Function IterateMatchesSeq (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; overload; var Index : Integer; Continue : Boolean; Iterator : TFindCharSetArrayIterator; Begin Continue := True; Index := FindFirstPosSeq (Iterator, Find, S, Options, Start, Stop, MaxCount); While Index > 0 do begin if Assigned (VisitProcedure) then begin With Iterator.Iter do VisitProcedure (Count, Index, Data, Continue, Iterator.Iter); if not Continue then break; end; Index := FindNextPosSeq (Iterator); end; Result := Iterator.Iter.Count; End; Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; overload; var Index : Integer; Continue : Boolean; Iterator : TFindCharIterator; Begin Continue := True; Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount); While Index > 0 do begin if Assigned (VisitProcedure) then begin With Iterator.Iter do VisitProcedure (Count, Index, Data, Continue, Iterator.Iter); if not Continue then break; end; Index := FindNextPos (Iterator); end; Result := Iterator.Iter.Count; End; Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; overload; var Index : Integer; Continue : Boolean; Iterator : TFindCharSetIterator; Begin Continue := True; Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount); While Index > 0 do begin if Assigned (VisitProcedure) then begin With Iterator.Iter do VisitProcedure (Count, Index, Data, Continue, Iterator.Iter); if not Continue then break; end; Index := FindNextPos (Iterator); end; Result := Iterator.Iter.Count; End; Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Array of String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var Index : Integer; Continue : Boolean; Iterator : TFindItemIterator; Begin Continue := True; Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount); While Index > 0 do begin if Assigned (VisitProcedure) then begin With Iterator.Iter do VisitProcedure (Count, Index, Data, Continue, Iterator.Iter); if not Continue then break; end; Index := FindNextPos (Iterator, Find); end; Result := Iterator.Iter.Count; End; { } { Count } { } Function Count (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; Begin Result := IterateMatches (nil, nil, Find, S, Options, Start, Stop, MaxCount); End; Function CountSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; Begin Result := IterateMatchesSeq (nil, nil, Find, S, Options, Start, Stop, MaxCount); End; {$IFDEF WINTEL} { Q_CharsCount by Andrew N. Driazgov (andrey@asp.tstu.ru) } { Optimized version of the general Count (CharSet) case. } Function Q_CharsCount (const S : String; const CharSet : CharSet) : Integer; Asm TEST EAX, EAX JE @@qt MOV ECX, [EAX - 4] TEST ECX, ECX JE @@zq PUSH EBX PUSH ESI LEA EBX, [EAX - 1] XOR EAX, EAX @@lp: MOVZX ESI, BYTE PTR [EBX + ECX] BT [EDX], ESI JC @@fn DEC ECX JNE @@lp POP ESI POP EBX RET @@fn: INC EAX DEC ECX JNE @@lp POP ESI POP EBX RET @@zq: XOR EAX, EAX @@qt: End; {$ENDIF} Function Count (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var L : Integer; Begin {$IFDEF WINTEL} L := Length (S); if (Options = []) and ((Start = 1) or (Start = -L)) and ((Stop = -1) or (Stop = L)) and (MaxCount < 0) then // Optimization for the general case begin Result := Q_CharsCount (S, Find); exit; end; {$ENDIF} Result := IterateMatches (nil, nil, Find, S, Options, Start, Stop, MaxCount); End; {$IFDEF WINTEL} { Q_CharCount by Andrew N. Driazgov (andrey@asp.tstu.ru) } { Optimized version of the general Count (Char) case. } Function Q_CharCount (const S : String; const Ch : Char) : Integer; Asm TEST EAX, EAX JE @@qt MOV ECX, [EAX - 4] TEST ECX, ECX JE @@zq PUSH EBX LEA EBX, [EAX - 1] XOR EAX, EAX @@lp: CMP DL, BYTE PTR [EBX + ECX] JE @@fn DEC ECX JNE @@lp POP EBX RET @@fn: INC EAX DEC ECX JNE @@lp POP EBX RET @@zq: XOR EAX, EAX @@qt: end; {$ENDIF} Function Count (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; var L : Integer; Begin {$IFDEF WINTEL} L := Length (S); if (Options = []) and ((Start = 1) or (Start = -L)) and ((Stop = -1) or (Stop = L)) and (MaxCount < 0) then // Optimization for the general case begin Result := Q_CharCount (S, Find); exit; end; {$ENDIF} Result := IterateMatches (nil, nil, Find, S, Options, Start, Stop, MaxCount); End; Function Count (const Find : Array of String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; Begin Result := IterateMatches (nil, nil, Find, S, Options, Start, Stop, MaxCount); End; { } { PosEx } { } Procedure PosExVisitProcedure (const Nr, Index : Integer; const Data : Pointer; var Continue : Boolean; const Iterator : TFindIterator); var Result : ^Integer absolute Data; Begin Result^ := Index; End; Function PosEx (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : Integer; Begin if IterateMatches (PosExVisitProcedure, @Result, Find, S, Options, Start, Stop, Count) < Count then Result := 0; End; Function PosEx (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : Integer; Begin if IterateMatches (PosExVisitProcedure, @Result, Find, S, Options, Start, Stop, Count) < Count then Result := 0; End; Function PosEx (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : Integer; Begin if IterateMatches (PosExVisitProcedure, @Result, Find, S, Options, Start, Stop, Count) < Count then Result := 0; End; Function PosExSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : Integer; Begin if IterateMatchesSeq (PosExVisitProcedure, @Result, Find, S, Options, Start, Stop, Count) < Count then Result := 0; End; { } { FindAll } { } Function DoFindAllSingleAllocation (var R : IntegerArray; const Count : Integer) : Boolean; Begin SetLength (R, Count); Result := Count > 0; End; Function FindAll (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TFindAllAlgorithm) : IntegerArray; var Iterator : TFindStringIterator; Begin if Algorithm = faSingleIteration then SetLength (Result, 0) else if not DoFindAllSingleAllocation (Result, Count (Find, S, Options, Start, Stop, MaxCount)) then exit; if FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount) > 0 then Repeat if Algorithm = faSingleAllocation then Result [Iterator.Iter.Count - 1] := Iterator.Iter.Index else Append (Result, Iterator.Iter.Index); Until FindNextPos (Iterator) = 0; End; Function FindAll (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TFindAllAlgorithm) : IntegerArray; var Iterator : TFindCharIterator; Begin if Algorithm = faSingleIteration then SetLength (Result, 0) else if not DoFindAllSingleAllocation (Result, Count (Find, S, Options, Start, Stop, MaxCount)) then exit; if FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount) > 0 then Repeat if Algorithm = faSingleAllocation then Result [Iterator.Iter.Count - 1] := Iterator.Iter.Index else Append (Result, Iterator.Iter.Index); Until FindNextPos (Iterator) = 0; End; Function FindAll (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TFindAllAlgorithm) : IntegerArray; var Iterator : TFindCharSetIterator; Begin if Algorithm = faSingleIteration then SetLength (Result, 0) else if not DoFindAllSingleAllocation (Result, Count (Find, S, Options, Start, Stop, MaxCount)) then exit; if FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount) > 0 then Repeat if Algorithm = faSingleAllocation then Result [Iterator.Iter.Count - 1] := Iterator.Iter.Index else Append (Result, Iterator.Iter.Index); Until FindNextPos (Iterator) = 0; End; Function FindAllSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TFindAllAlgorithm) : IntegerArray; var Iterator : TFindCharSetArrayIterator; Begin if Algorithm = faSingleIteration then SetLength (Result, 0) else if not DoFindAllSingleAllocation (Result, CountSeq (Find, S, Options, Start, Stop, MaxCount)) then exit; if FindFirstPosSeq (Iterator, Find, S, Options, Start, Stop, MaxCount) > 0 then Repeat if Algorithm = faSingleAllocation then Result [Iterator.Iter.Count - 1] := Iterator.Iter.Index else Append (Result, Iterator.Iter.Index); Until FindNextPosSeq (Iterator) = 0; End; { } { Split } { } Function DoSplitSingleAllocation (var R : StringArray; const Count : Integer; const S : String) : Boolean; Begin SetLength (R, Count + 1); if Count = 0 then begin Result := False; R [0] := S; end else Result := True; End; Function Split (const S, Delimiter : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TSplitAlgorithm) : StringArray; var StartIndex, StopIndex : Integer; Iterator : TFindStringIterator; T : String; Begin if Algorithm = saSingleIteration then SetLength (Result, 0) else if not DoSplitSingleAllocation (Result, Count (Delimiter, S, Options, Start, Stop, MaxCount), S) then exit; if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Delimiter, S, Options, Start, Stop, MaxCount) then Repeat T := CopyRange (S, StartIndex, StopIndex); if Algorithm = saSingleAllocation then Result [Iterator.Iter.Count - 1] := T else Append (Result, T); Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); End; Function Split (const S : String; const Delimiter : Char; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TSplitAlgorithm) : StringArray; var StartIndex, StopIndex : Integer; Iterator : TFindCharIterator; T : String; Begin if Algorithm = saSingleIteration then SetLength (Result, 0) else if not DoSplitSingleAllocation (Result, Count (Delimiter, S, Options, Start, Stop, MaxCount), S) then exit; if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Delimiter, S, Options, Start, Stop, MaxCount) then Repeat T := CopyRange (S, StartIndex, StopIndex); if Algorithm = saSingleAllocation then Result [Iterator.Iter.Count - 1] := T else Append (Result, T); Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); End; Function Split (const S : String; const Delimiter : CharSet; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TSplitAlgorithm) : StringArray; var StartIndex, StopIndex : Integer; Iterator : TFindCharSetIterator; T : String; Begin if Algorithm = saSingleIteration then SetLength (Result, 0) else if not DoSplitSingleAllocation (Result, Count (Delimiter, S, Options, StartIndex, StopIndex, MaxCount), S) then exit; if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Delimiter, S, Options, Start, Stop, MaxCount) then Repeat T := CopyRange (S, StartIndex, StopIndex); if Algorithm = saSingleAllocation then Result [Iterator.Iter.Count - 1] := T else Append (Result, T); Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); End; Function Join (const S : Array of String; const Delimiter : String; const Start : Integer) : String; var I, L, D, C : Integer; P : PChar; T : String; Begin L := Length (S); if L = 0 then begin Result := ''; exit; end; D := Length (Delimiter); SetLength (Result, StringArrayLength (S) + (L - 1) * D); P := Pointer (Result); For I := Start to L - 1 do begin if (I > Start) and (D > 0) then begin MoveMem (Pointer (Delimiter)^, P^, D); Inc (P, D); end; T := S [I]; C := Length (T); if C > 0 then begin MoveMem (Pointer (T)^, P^, C); Inc (P, C); end; end; End; Procedure Split (const S : String; const Delimiter : String; var LeftSide, RightSide : String; const DelimiterOptional : Boolean; const SplitPosition : TSplitPosition; const Options : TFindOptions; const Start : Integer; const Stop : Integer); var L, I, J, F, D : Integer; Begin if (Delimiter = '') or not TranslateStartStop (S, Start, Stop, L, I, J) then begin LeftSide := ''; RightSide := ''; exit; end; F := Pos (Delimiter, S, Options, I, J); if F = 0 then begin RightSide := ''; if DelimiterOptional then LeftSide := S else LeftSide := ''; end else begin D := Length (Delimiter); if SplitPosition = splitLeft then LeftSide := CopyRange (S, I, F + D - 1) else LeftSide := CopyRange (S, I, F - 1); if SplitPosition = splitRight then RightSide := CopyRange (S, F, MaxI (J, F + D - 1)) else RightSide := CopyRange (S, F + D, J); end; End; Procedure Split (const S : String; const Delimiter : CharSet; var LeftSide, RightSide : String; const DelimiterOptional : Boolean; const SplitPosition : TSplitPosition; const Options : TFindOptions; const Start : Integer; const Stop : Integer); var L, I, J, F : Integer; Begin if (Delimiter = []) or not TranslateStartStop (S, Start, Stop, L, I, J) then begin LeftSide := ''; RightSide := ''; exit; end; F := Pos (Delimiter, S, Options, I, J); if F = 0 then begin RightSide := ''; if DelimiterOptional then LeftSide := S else LeftSide := ''; end else begin if SplitPosition = splitLeft then LeftSide := CopyRange (S, I, F) else LeftSide := CopyRange (S, I, F - 1); if SplitPosition = splitRight then RightSide := CopyRange (S, F, MaxI (J, F)) else RightSide := CopyRange (S, F + 1, J); end; End; Function ExtractWords (const S : String; const WordChars : CharSet) : StringArray; var P, Q : PChar; L, M : Integer; T : String; Begin Result := nil; L := Length (S); P := Pointer (S); Q := P; M := 0; While L > 0 do if P^ in WordChars then begin Inc (P); Dec (L); Inc (M); end else begin if M > 0 then begin SetLength (T, M); MoveMem (Q^, Pointer (T)^, M); Append (Result, T); end; M := 0; Inc (P); Dec (L); Q := P; end; if M > 0 then begin SetLength (T, M); MoveMem (Q^, Pointer (T)^, M); Append (Result, T); end; End; { } { Replace } { } type ReplaceCharVisitData = record Result : String; Replace : Char; end; Procedure ReplaceCharVisitProcedure (const Nr, Index : Integer; const Data : Pointer; var Continue : Boolean; const Iterator : TFindIterator); var D : ^ReplaceCharVisitData absolute Data; Begin With D^ do Result [Index] := Replace; End; Function Replace (const Find, Replace : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : String; var I, J : Integer; P, Q : PChar; C, D : Char; Data : ReplaceCharVisitData; Begin I := Length (S); if (Options = []) and ((Start = 1) or (Start = -I)) and ((Stop = -1) or (Stop = I)) and (MaxCount < 0) then // Optimization for the general case begin SetLength (Result, I); if I = 0 then exit; P := Pointer (Result); Q := Pointer (S); For J := 1 to I do begin C := Q^; if C = Find then D := Replace else D := C; P^ := D; Inc (P); Inc (Q); end; exit; end; Data.Result := S; Data.Replace := Replace; IterateMatches (ReplaceCharVisitProcedure, @Data, Find, S, Options, Start, Stop, MaxCount); Result := Data.Result; End; Function Replace (const Find : CharSet; const Replace : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : String; var Data : ReplaceCharVisitData; Begin Data.Result := S; Data.Replace := Replace; IterateMatches (ReplaceCharVisitProcedure, @Data, Find, S, Options, Start, Stop, MaxCount); Result := Data.Result; End; Function Replace (const Find : Char; const Replace, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TReplaceAlgorithm) : String; var Iterator : TFindCharIterator; I, C, L, ResultIndex, StartIndex, StopIndex : Integer; Reverse : Boolean; Begin Reverse := foReverse in Options; if Algorithm = raSingleAllocation then begin TranslateStartStop(S, Start, Stop, L, StartIndex, StopIndex); C := Count (Find, S, Options, StartIndex, StopIndex, MaxCount); if C = 0 then begin Result := CopyRange (S, StartIndex, StopIndex); exit; end; I := Length (S) + C * (Length (Replace) - 1) - (L - (StopIndex - StartIndex + 1)); SetLength (Result, I); if Result = '' then exit; ResultIndex := iif (Reverse, I, 1); if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then Repeat Paste (S, Result, ResultIndex, Reverse, StartIndex, StopIndex); if Iterator.Iter.Index > 0 then Paste (Replace, Result, ResultIndex, Reverse); Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); end else begin Result := ''; ResultIndex := 1; if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then Repeat if Reverse then Result := iif (Iterator.Iter.Index > 0, Replace, '') + CopyRange (S, StartIndex, StopIndex) + Result else begin I := MaxI (StopIndex - StartIndex + 1, 0) + iif (Iterator.Iter.Index > 0, Length (Replace), 0); if I > 0 then begin SetLength (Result, Length (Result) + I); Paste (S, Result, ResultIndex, False, StartIndex, StopIndex); if Iterator.Iter.Index > 0 then Paste (Replace, Result, ResultIndex); end; end; Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); end; End; Function Replace (const Find : CharSet; const Replace, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TReplaceAlgorithm) : String; var Iterator : TFindCharSetIterator; I, C, L, ResultIndex, StartIndex, StopIndex : Integer; Reverse : Boolean; Begin Reverse := foReverse in Options; if Algorithm = raSingleAllocation then begin TranslateStartStop(S, Start, Stop, L, StartIndex, StopIndex); C := Count (Find, S, Options, StartIndex, StopIndex, MaxCount); if C = 0 then begin Result := CopyRange (S, StartIndex, StopIndex); exit; end; I := Length (S) + C * (Length (Replace) - 1) - (L - (StopIndex - StartIndex + 1)); SetLength (Result, I); if Result = '' then exit; ResultIndex := iif (Reverse, I, 1); if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then Repeat Paste (S, Result, ResultIndex, Reverse, StartIndex, StopIndex); if Iterator.Iter.Index > 0 then Paste (Replace, Result, ResultIndex, Reverse); Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); end else begin Result := ''; ResultIndex := 1; if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then Repeat if Reverse then Result := iif (Iterator.Iter.Index > 0, Replace, '') + CopyRange (S, StartIndex, StopIndex) + Result else begin I := MaxI (StopIndex - StartIndex + 1, 0) + iif (Iterator.Iter.Index > 0, Length (Replace), 0); if I > 0 then begin SetLength (Result, Length (Result) + I); Paste (S, Result, ResultIndex, False, StartIndex, StopIndex); if Iterator.Iter.Index > 0 then Paste (Replace, Result, ResultIndex); end; end; Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); end; End; Function ReplaceSeq (const Find : Array of CharSet; const Replace, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TReplaceAlgorithm) : String; var Iterator : TFindCharSetArrayIterator; I, C, L, ResultIndex, StartIndex, StopIndex : Integer; Reverse : Boolean; Begin Reverse := foReverse in Options; if Algorithm = raSingleAllocation then begin TranslateStartStop(S, Start, Stop, L, StartIndex, StopIndex); C := CountSeq (Find, S, Options, StartIndex, StopIndex, MaxCount); if C = 0 then begin Result := CopyRange (S, StartIndex, StopIndex); exit; end; I := Length (S) + C * (Length (Replace) - Length (Find)) - (L - (StopIndex - StartIndex + 1)); SetLength (Result, I); if Result = '' then exit; ResultIndex := iif (Reverse, I, 1); if FindFirstUnmatchedRangeSeq (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then Repeat Paste (S, Result, ResultIndex, Reverse, StartIndex, StopIndex); if Iterator.Iter.Index > 0 then Paste (Replace, Result, ResultIndex, Reverse); Until not FindNextUnmatchedRangeSeq (Iterator, StartIndex, StopIndex); end else begin Result := ''; ResultIndex := 1; if FindFirstUnmatchedRangeSeq (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then Repeat if Reverse then Result := iif (Iterator.Iter.Index > 0, Replace, '') + CopyRange (S, StartIndex, StopIndex) + Result else begin I := MaxI (StopIndex - StartIndex + 1, 0) + iif (Iterator.Iter.Index > 0, Length (Replace), 0); if I > 0 then begin SetLength (Result, Length (Result) + I); Paste (S, Result, ResultIndex, False, StartIndex, StopIndex); if Iterator.Iter.Index > 0 then Paste (Replace, Result, ResultIndex); end; end; Until not FindNextUnmatchedRangeSeq (Iterator, StartIndex, StopIndex); end; End; {$IFDEF WINTEL} { Quick version of the general replace case, adapted from a routine by } { Andrew N. Driazgov (andrey@asp.tstu.ru) } Function Q_ReplaceStr (const Find, Replace, S : String) : String; var P, PS : PChar; L, L1, L2, Cnt : Integer; I, J, K, M : Integer; Begin L1 := Length (Find); Cnt := 0; I := Pos (Find, S); while I <> 0 do begin Inc (I, L1); asm PUSH I end; Inc (Cnt); I := Pos (Find, S, [], I); end; if Cnt <> 0 then begin L := Length (S); L2 := Length (Replace); J := L + 1; Inc (L, (L2 - L1) * Cnt); if L <> 0 then begin SetString (Result, nil, L); P := Pointer (Result); Inc (P, L); PS := Pointer (LongWord (S) - 1); if L2 <= 32 then for I := 0 to Cnt - 1 do begin asm POP K end; M := J - K; if M > 0 then begin Dec (P, M); MoveMem (PS [K], P^, M); end; Dec (P, L2); if L2 > 0 then MoveMem (Pointer (Replace)^, P^, L2); J := K - L1; end else for I := 0 to Cnt-1 do begin asm POP K end; M := J - K; if M > 0 then begin Dec (P, M); MoveMem (PS [K], P^, M); end; Dec (P, L2); if L2 > 0 then MoveMem (Pointer (Replace)^, P^, L2); J := K - L1; end; Dec (J); if J > 0 then MoveMem (Pointer (S)^, Pointer (Result)^, J); end else Result := ''; end else Result := S; End; {$ENDIF} Function Replace (const Find, Replace, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TReplaceAlgorithm) : String; var Iterator : TFindStringIterator; I, C, L, ResultIndex, StartIndex, StopIndex : Integer; Reverse : Boolean; Begin {$IFDEF WINTEL} I := Length (S); if (Options = []) and ((Start = 1) or (Start = -I)) and ((Stop = -1) or (Stop = I)) and (MaxCount < 0) then // Optimization for the general case begin Result := Q_ReplaceStr (Find, Replace, S); exit; end; {$ENDIF} Reverse := foReverse in Options; if Algorithm = raSingleAllocation then begin TranslateStartStop(S, Start, Stop, L, StartIndex, StopIndex); C := Count (Find, S, Options, StartIndex, StopIndex, MaxCount); if C = 0 then begin Result := CopyRange(S, StartIndex, StopIndex); exit; end; I := Length (S) + C * (Length (Replace) - Length (Find)) - (L - (StopIndex - StartIndex + 1)); SetLength (Result, I); if Result = '' then exit; ResultIndex := iif (Reverse, I, 1); if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then Repeat Paste (S, Result, ResultIndex, Reverse, StartIndex, StopIndex); if Iterator.Iter.Index > 0 then Paste (Replace, Result, ResultIndex, Reverse); Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); end else begin Result := ''; ResultIndex := 1; if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then Repeat if Reverse then Result := iif (Iterator.Iter.Index > 0, Replace, '') + CopyRange (S, StartIndex, StopIndex) + Result else begin I := MaxI (StopIndex - StartIndex + 1, 0) + iif (Iterator.Iter.Index > 0, Length (Replace), 0); if I > 0 then begin SetLength (Result, Length (Result) + I); Paste (S, Result, ResultIndex, False, StartIndex, StopIndex); if Iterator.Iter.Index > 0 then Paste (Replace, Result, ResultIndex); end; end; Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex); end; End; Function ReplaceChars (const Find, Replace : Array of Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : String; var L, I, J, F, C : Integer; CaseSensitive : Boolean; Function CheckMatch (var Res : String) : Boolean; var G : Integer; Begin Result := True; G := MatchChars (S [F], Find, CaseSensitive); if G >= 0 then begin Res [F] := Replace [G]; if MaxCount > 0 then begin Inc (C); if C = MaxCount then Result := False; end; end; End; Begin Assert (High (Find) = High (Replace), 'Find and Replace arrays must be of equal length'); Result := S; if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, I, J) then exit; CaseSensitive := not (foCaseInsensitive in Options); C := 0; if foReverse in Options then begin For F := J downto I do if not CheckMatch (Result) then exit; end else For F := I to J do if not CheckMatch (Result) then exit; End; Function RemoveSeq (const C : Array of CharSet; const S : String) : String; Begin Result := ReplaceSeq (C, '', S); End; Function RemoveDup (const C : Char; const S : String) : String; var P, Q : PChar; D, E : Char; I, L, M : Integer; Begin L := Length (S); if L <= 1 then begin Result := S; exit; end; Result := S; SetLength (Result, L); P := Pointer (S); Q := Pointer (Result); D := P^; Q^ := D; Inc (P); Inc (Q); M := 1; For I := 2 to L do begin E := P^; if (D <> C) or (E <> C) then begin D := E; Q^ := E; Inc (M); Inc (Q); end; Inc (P); end; SetLength (Result, M); End; {$IFDEF WINTEL} { Q_DelChar by Andrew N. Driazgov (andrey@asp.tstu.ru) } { Quick version of general RemoveAll (Char, S) case } Function Q_DelChar (const S : String; Ch : Char) : String; Asm PUSH ESI PUSH EBX PUSH EDI MOV ESI, ECX TEST EAX, EAX JE @@qt MOV ECX, [EAX - 4] TEST ECX, ECX JE @@qt MOV EBX, EAX MOV EDI, EDX XOR EDX, EDX MOV EAX, ESI CALL System.@LStrFromPCharLen MOV EDX,EDI MOV ECX, [EBX-4] MOV EDI, [ESI] @@lp: MOV AL, BYTE PTR [EBX] CMP DL, AL JE @@nx MOV BYTE PTR [EDI], AL INC EDI @@nx: INC EBX DEC ECX JNE @@lp MOV EAX, [ESI] MOV BYTE PTR [EDI],0 SUB EDI, EAX JE @@qt MOV [EAX-4], EDI POP EDI POP EBX POP ESI RET @@qt: MOV EAX,ESI CALL System.@LStrClr POP EDI POP EBX POP ESI End; {$ENDIF} Function RemoveAll (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer;const Algorithm : TReplaceAlgorithm) : String; var L : Integer; Begin {$IFDEF WINTEL} L := Length (S); if (Options = []) and ((Start = 1) or (Start = -L)) and ((Stop = -1) or (Stop = L)) then // Optimization for the general case begin Result := Q_DelChar (S, Find); exit; end; {$ENDIF} Result := Replace (Find, '', S, Options, Start, Stop, -1, Algorithm); End; Function RemoveAll (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer;const Algorithm : TReplaceAlgorithm) : String; Begin Result := Replace (Find, '', S, Options, Start, Stop, -1, Algorithm); End; {$IFDEF WINTEL} { Q_DelChars by Andrew N. Driazgov (andrey@asp.tstu.ru) } { Quick version of general RemoveAll (CharSet, S) case } Procedure Q_DelChars (var S : string; const CharsToRemove : CharSet); Asm PUSH ESI PUSH EDI MOV ESI, EDX PUSH EAX CALL UniqueString TEST EAX, EAX JE @@qt MOV ECX, [EAX - 4] MOV EDI, EAX TEST ECX, ECX JE @@zq0 @@lp1: MOVZX EDX,BYTE PTR [EAX] BT [ESI], EDX JC @@rp @@nx1: MOV BYTE PTR [EDI], DL INC EAX INC EDI DEC ECX JNE @@lp1 @@nx2: POP EAX MOV ECX, [EAX] MOV BYTE PTR [EDI], 0 SUB EDI, ECX JE @@zq1 MOV [ECX - 4], EDI POP EDI POP ESI RET @@qt: POP ECX POP EDI POP ESI RET @@zq0: POP EAX @@zq1: CALL System.@LStrClr POP EDI POP ESI RET @@rp: INC EAX DEC ECX JE @@nx2 @@lp2: MOVZX EDX, BYTE PTR [EAX] BT [ESI], EDX JNC @@nx1 INC EAX DEC ECX JNE @@lp2 JMP @@nx2 End; {$ENDIF} Function RemoveAll (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer;const Algorithm : TReplaceAlgorithm) : String; var L : Integer; Begin {$IFDEF WINTEL} L := Length (S); if (Options = []) and ((Start = 1) or (Start = -L)) and ((Stop = -1) or (Stop = L)) then // Optimization for the general case begin Result := S; Q_DelChars (Result, Find); exit; end; {$ENDIF} Result := Replace (Find, '', S, Options, Start, Stop, -1, Algorithm); End; Function RemoveFirst (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer;const Algorithm : TReplaceAlgorithm) : String; Begin Result := Replace (Find, '', S, Options, Start, Stop, 1, Algorithm); End; { } { Reverse } { } Function Reversed (const S : String) : String; var I, L : Integer; P, Q : PChar; Begin L := Length (S); if L = 0 then begin Result := ''; exit; end; if L = 1 then begin Result := S; exit; end; SetLength (Result, L); P := Pointer (S); Q := Pointer (Result); Inc (Q, L - 1); For I := 1 to L do begin Q^ := P^; Dec (Q); Inc (P); end; End; { } { Delimiter-based Copy } { } Function CopyLeft (const S, Delimiter : String; const DelimiterOptions : TDelimiterOptions; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; var StartIndex, StopIndex, I : Integer; Begin if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then Result := '' else begin I := PosEx (Delimiter, S, FindOptions, StartIndex, StopIndex, Count); if I > 0 then if doIncludeDelimiter in DelimiterOptions then Result := CopyRange (S, StartIndex, I + Length (Delimiter) - 1) else Result := CopyRange (S, StartIndex, I - 1) else if (Count <= 0) or (doOptional in DelimiterOptions) then Result := CopyRange (S, StartIndex, StopIndex) else Result := ''; end; End; Function CopyLeft (const S : String; const Delimiter : CharSet; const DelimiterOptions : TDelimiterOptions; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; var StartIndex, StopIndex, I : Integer; Begin if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then Result := '' else begin I := PosEx (Delimiter, S, FindOptions, StartIndex, StopIndex, Count); if I > 0 then if doIncludeDelimiter in DelimiterOptions then Result := CopyRange (S, StartIndex, I) else Result := CopyRange (S, StartIndex, I - 1) else if (Count <= 0) or (doOptional in DelimiterOptions) then Result := CopyRange (S, StartIndex, StopIndex) else Result := ''; end; End; Function CopyRight (const S, Delimiter : String; const DelimiterOptions : TDelimiterOptions; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; var StartIndex, StopIndex, I : Integer; Begin if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then Result := '' else begin I := PosEx (Delimiter, S, FindOptions, StartIndex, StopIndex, Count); if I > 0 then if doIncludeDelimiter in DelimiterOptions then Result := CopyRange (S, I, StopIndex) else Result := CopyRange (S, I + Length (Delimiter), StopIndex) else if (Count <= 0) or (doOptional in DelimiterOptions) then Result := CopyRange (S, StartIndex, StopIndex) else Result := ''; end; End; Function CopyRight (const S : String; const Delimiter : CharSet; const DelimiterOptions : TDelimiterOptions; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; var StartIndex, StopIndex, I : Integer; Begin if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then Result := '' else begin I := PosEx (Delimiter, S, FindOptions, StartIndex, StopIndex, Count); if I > 0 then if doIncludeDelimiter in DelimiterOptions then Result := CopyRange (S, I, StopIndex) else Result := CopyRange (S, I + 1, StopIndex) else if (Count <= 0) or (doOptional in DelimiterOptions) then Result := CopyRange (S, StartIndex, StopIndex) else Result := ''; end; End; Function CopyRange (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptions : TDelimiterOptions; const RightDelimiterOptions : TDelimiterOptions; const NotRange : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; var StartIndex, StopIndex, I, J : Integer; Begin if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then Result := '' else begin I := PosEx (LeftDelimiter, S, LeftFindOptions, StartIndex, StopIndex, LeftCount); if I = 0 then if (LeftCount <= 0) or (doOptional in LeftDelimiterOptions) then I := StartIndex else begin Result := ''; exit; end else if not (doIncludeDelimiter in LeftDelimiterOptions) then Inc (I, Length (LeftDelimiter)); J := PosEx (RightDelimiter, S, RightFindOptions, I, StopIndex, RightCount); if J = 0 then if (RightCount <= 0) or (doOptional in RightDelimiterOptions) then J := StopIndex else begin Result := ''; exit; end else if doIncludeDelimiter in RightDelimiterOptions then Inc (J, Length (RightDelimiter) - 1) else Dec (J); if NotRange then begin Result := CopyRange (S, StartIndex, StopIndex); Delete (Result, I - StartIndex + 1, J - I + 1); end else Result := CopyRange (S, I, J); end; End; Function CopyRange (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptions : TDelimiterOptions; const RightDelimiterOptions : TDelimiterOptions; const NotRange : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; var StartIndex, StopIndex, I, J : Integer; Begin if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then Result := '' else begin I := PosEx (LeftDelimiter, S, LeftFindOptions, StartIndex, StopIndex, LeftCount); if I = 0 then if (LeftCount <= 0) or (doOptional in LeftDelimiterOptions) then I := StartIndex else begin Result := ''; exit; end else if not (doIncludeDelimiter in LeftDelimiterOptions) then Inc (I, Length (LeftDelimiter)); J := PosEx (RightDelimiter, S, RightFindOptions, I, StopIndex, RightCount); if J = 0 then if (RightCount <= 0) or (doOptional in RightDelimiterOptions) then J := StopIndex else begin Result := ''; exit; end else if not (doIncludeDelimiter in RightDelimiterOptions) then Dec (J); if NotRange then begin Result := CopyRange (S, StartIndex, StopIndex); Delete (Result, I - StartIndex + 1, J - I + 1); end else Result := CopyRange (S, I, J); end; End; Function CopyRange (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptions : TDelimiterOptions; const RightDelimiterOptions : TDelimiterOptions; const NotRange : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; var StartIndex, StopIndex, I, J : Integer; Begin if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then Result := '' else begin I := PosEx (LeftDelimiter, S, LeftFindOptions, StartIndex, StopIndex, LeftCount); if I = 0 then if (LeftCount <= 0) or (doOptional in LeftDelimiterOptions) then I := StartIndex else begin Result := ''; exit; end else if not (doIncludeDelimiter in LeftDelimiterOptions) then Inc (I); J := PosEx (RightDelimiter, S, RightFindOptions, I, StopIndex, RightCount); if J = 0 then if (RightCount <= 0) or (doOptional in RightDelimiterOptions) then J := StopIndex else begin Result := ''; exit; end else if doIncludeDelimiter in RightDelimiterOptions then Inc (J, Length (RightDelimiter) - 1) else Dec (J); if NotRange then begin Result := CopyRange (S, StartIndex, StopIndex); Delete (Result, I - StartIndex + 1, J - I + 1); end else Result := CopyRange (S, I, J); end; End; Function CopyRange (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptions : TDelimiterOptions; const RightDelimiterOptions : TDelimiterOptions; const NotRange : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; var StartIndex, StopIndex, I, J : Integer; Begin if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then Result := '' else begin I := PosEx (LeftDelimiter, S, LeftFindOptions, StartIndex, StopIndex, LeftCount); if I = 0 then if (LeftCount <= 0) or (doOptional in LeftDelimiterOptions) then I := StartIndex else begin Result := ''; exit; end else if not (doIncludeDelimiter in LeftDelimiterOptions) then Inc (I); J := PosEx (RightDelimiter, S, RightFindOptions, I, StopIndex, RightCount); if J = 0 then if (RightCount <= 0) or (doOptional in RightDelimiterOptions) then J := StopIndex else begin Result := ''; exit; end else if not (doIncludeDelimiter in RightDelimiterOptions) then Dec (J); if NotRange then begin Result := CopyRange (S, StartIndex, StopIndex); Delete (Result, I - StartIndex + 1, J - I + 1); end else Result := CopyRange (S, I, J); end; End; Function DelimiterOptions (const Optional, IncludeDelimiter : Boolean) : TDelimiterOptions; Begin if Optional then Result := [doOptional] else Result := []; if IncludeDelimiter then Include (Result, doIncludeDelimiter); End; Function CopyFrom (const S, Delimiter : String; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; Begin Result := CopyRight (S, Delimiter, DelimiterOptions (DelimiterOptional, True), FindOptions, Start, Stop, Count); End; Function CopyFrom (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; Begin Result := CopyRight (S, Delimiter, DelimiterOptions (DelimiterOptional, True), FindOptions, Start, Stop, Count); End; Function CopyAfter (const S, Delimiter : String; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; Begin Result := CopyRight (S, Delimiter, DelimiterOptions (DelimiterOptional, False), FindOptions, Start, Stop, Count); End; Function CopyAfter (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; Begin Result := CopyRight (S, Delimiter, DelimiterOptions (DelimiterOptional, False), FindOptions, Start, Stop, Count); End; Function CopyTo (const S, Delimiter : String; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; Begin Result := CopyLeft (S, Delimiter, DelimiterOptions (DelimiterOptional, True), FindOptions, Start, Stop, Count); End; Function CopyTo (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; Begin Result := CopyLeft (S, Delimiter, DelimiterOptions (DelimiterOptional, True), FindOptions, Start, Stop, Count); End; Function CopyBefore (const S, Delimiter : String; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; Begin Result := CopyLeft (S, Delimiter, DelimiterOptions (DelimiterOptional, False), FindOptions, Start, Stop, Count); End; Function CopyBefore (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String; Begin Result := CopyLeft (S, Delimiter, DelimiterOptions (DelimiterOptional, False), FindOptions, Start, Stop, Count); End; Function CopyBetween (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), False, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function CopyBetween (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), False, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function CopyBetween (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), False, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function CopyBetween (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), False, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function RemoveBetween (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, True), DelimiterOptions (RightDelimiterOptional, True), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function RemoveBetween (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, True), DelimiterOptions (RightDelimiterOptional, True), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function RemoveBetween (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, True), DelimiterOptions (RightDelimiterOptional, True), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function RemoveBetween (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, True), DelimiterOptions (RightDelimiterOptional, True), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function Remove (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function Remove (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function Remove (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; Function Remove (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String; Begin Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount); End; { } { Quoting and Escaping } { } Function EscapeText (const S : String; const CharsToEscape : Array of Char; const EscapePrefix : Char; const EscapeChar : Array of Char) : String; var I, J : Integer; Iterator : TFindItemIterator; Begin Result := ''; J := 1; I := FindFirstPos (Iterator, CharsToEscape, S); While I > 0 do begin Result := Result + CopyRange (S, J, I - 1) + EscapePrefix + EscapeChar [Iterator.ItemIndex]; J := I + 1; I := FindNextPos (Iterator, CharsToEscape); end; if J = 1 then Result := S else Result := Result + CopyFrom (S, J); End; Function HexEscapeText (const S : String; const CharsToEscape : CharSet; const EscapePrefix : String; const EscapePostfix : String; const UpperHex : Boolean; const AlwaysTwoDigits : Boolean) : String; var I, J : Integer; Iterator : TFindCharSetIterator; H : String; Begin Result := ''; J := 1; I := FindFirstPos (Iterator, CharsToEscape, S); While I > 0 do begin if AlwaysTwoDigits then H := LongWordToHex (Ord (S [I]), 2) else H := LongWordToHex (Ord (S [I]), 1); if UpperHex then ConvertUpper (H) else ConvertLower (H); Result := Result + CopyRange (S, J, I - 1) + EscapePrefix + H + EscapePostfix; J := I + 1; I := FindNextPos (Iterator); end; if J = 1 then Result := S else Result := Result + CopyFrom (S, J); End; Function HexUnescapeText (const S : String; const EscapePrefix : Char) : String; var I, J : Integer; Begin Result := ''; J := 1; Repeat I := Pos (EscapePrefix, S, [], J); if I > 0 then begin Result := Result + CopyRange (S, J + 1, I - 1); if I < Length (S) - 1 then begin Result := Result + Char (StrToIntDef ('$' + Copy (S, I + 1, 2), 32)); J := I + 3; end else J := I + 1; end; Until I = 0; if (I = 0) and (J = 0) then Result := S else Result := Result + CopyFrom (S, J + 1); End; Function UnescapeText (const S : String; const EscapePrefix : Char; const EscapeChar : Array of Char; const Replacement : Array of String; const AlwaysDropPrefix : Boolean) : String; var I, J : Integer; F, G : Integer; Ch : Char; T : String; Begin Assert (High (EscapeChar) = High (Replacement), 'Arrays must be of equal length'); Result := ''; J := 1; Repeat I := Pos (EscapePrefix, S, [], J); if I > 0 then begin G := -1; if I < Length (S) then begin Ch := S [I + 1]; For F := 0 to High (EscapeChar) do if EscapeChar [F] = Ch then begin G := F; break; end; end; Result := Result + CopyRange (S, J + 1, I - 1); if G >= 0 then begin T := Replacement [G]; Result := Result + T; J := I + Length (T); end else begin if not AlwaysDropPrefix then Result := Result + EscapePrefix; J := I + 1; end; end; Until I = 0; if (I = 0) and (J = 0) then Result := S else Result := Result + CopyFrom (S, J + 1); End; Function CEscapeText (const S : String) : String; Begin Result := EscapeText (S, [#13, #10, #0, #7, #27, '\'], '\', ['n', 'l', '0', 'b', 'e', '\']); End; Function CUnescapeText (const S : String) : String; Begin Result := UnescapeText (S, '\', ['n', 'l', '0', 'b', 'e', '\'], [#13, #10, #0, #7, #27, '\']); End; Function QuoteText (const S : String; const Quotes : Char) : String; Begin Result := Quotes + Replace (Quotes, Quotes + Quotes, S) + Quotes; End; Function UnquoteText (const S : String) : String; var Quote : Char; L : Integer; Begin L := Length (S); if (L < 2) or (S [1] <> S [L]) then begin Result := S; exit; end; Quote := S [1]; Result := Replace (Quote + Quote, Quote, S, [], 2, L - 1); End; Function FindClosingQuote (const S : String; const OpenQuotePos : Integer) : Integer; var I : Integer; OpenQuote : Char; R : Boolean; Begin if (OpenQuotePos <= 0) or (OpenQuotePos > Length (S)) then begin Result := 0; exit; end; I := OpenQuotePos; OpenQuote := S [I]; Repeat I := Pos (OpenQuote, S, [], I + 1); if I = 0 then begin Result := 0; exit; end; R := (I = Length (S)) or (S [I + 1] <> OpenQuote); if not R then Inc (I); Until R; Result := I; End; Function RemoveQuotes (const S : String; const Quotes : Char = '''') : String; var L : Integer; Begin L := Length (S); if (L >= 2) and (S [1] = Quotes) and (S [L] = Quotes) then Result := CopyRange (S, 2, L - 1) else Result := S; End; Function EncodeDotLineTerminated (const S : String) : String; Begin Result := S; if (Length (Result) >= 1) and (Result [1] = '.') then Insert ('.', Result, 1); Result := Replace (CRLF + '.', CRLF + '..', Result) + '.' + CRLF; End; Function DecodeDotLineTerminated (const S : String) : String; Begin if not MatchRight ('.' + CRLF, S) then raise EConvertError.Create ('Not dot line terminated'); Result := Replace (CRLF + '.', CRLF, S); Delete (Result, Length (Result) - 1, 2); if (Length (Result) >= 1) and (Result [1] = '.') then Delete (Result, 1, 1); End; Function EncodeEmptyLineTerminated (const S : String) : String; Begin Result := WithSuffix (S, CRLF); if (Length (Result) >= 2) and (Result [1] = ASCII_CR) and (Result [2] = ASCII_LF) then Insert ('.', Result, 1); Result := Replace (CRLF + CRLF, CRLF + '.' + CRLF, Result) + CRLF; End; Function DecodeEmptyLineTerminated (const S : String) : String; Begin if not MatchRight (CRLF, S) then raise EConvertError.Create ('Not dot line terminated'); Result := Replace (CRLF + '.', CRLF, CopyLeft (S, Length (S) - 2)); if (Length (Result) >= 1) and (Result [1] = '.') then Delete (Result, 1, 1); End; Function SplitQuotedList (const S : String; const Delimiter : String; const Quotes : CharSet) : StringArray; var I, J : Integer; Begin I := 1; While I <= Length (S) do begin J := MatchQuotedString (S, Quotes, I); if J > 0 then begin Append (Result, UnquoteText (CopyRange (S, I, I + J - 1))); Inc (I, J); if Match (Delimiter, S, I) then Inc (I, Length (Delimiter)); end else begin J := Pos (Delimiter, S, [], I); if J > 0 then begin Append (Result, CopyRange (S, I, J - 1)); I := J + Length (Delimiter); end else begin Append (Result, CopyFrom (S, I)); exit; end; end; end; End; Function WithSuffix (const S : String; const Suffix : String; const CaseSensitive : Boolean) : String; Begin if not MatchRight (Suffix, S, CaseSensitive) then Result := S + Suffix else Result := S; End; Function WithPrefix (const S : String; const Prefix : String; const CaseSensitive : Boolean) : String; Begin if not MatchLeft (Prefix, S, CaseSensitive) then Result := Prefix + S else Result := S; End; Function WithoutSuffix (const S : String; const Suffix : String; const CaseSensitive : Boolean) : String; Begin if MatchRight (Suffix, S, CaseSensitive) then Result := Copy (S, 1, Length (S) - Length (Suffix)) else Result := S; End; Function WithoutPrefix (const S : String; const Prefix : String; const CaseSensitive : Boolean) : String; Begin if MatchLeft (Prefix, S, CaseSensitive) then Result := CopyFrom (S, Length (Prefix) + 1) else Result := S; End; Procedure EnsureSuffix (var S : String; const Suffix : String; const CaseSensitive : Boolean); var L, M : Integer; P : PChar; Begin if (Suffix <> '') and not MatchRight (Suffix, S, CaseSensitive) then begin L := Length (S); M := Length (Suffix); SetLength (S, L + M); P := Pointer (S); Inc (P, L); MoveMem (Pointer (Suffix)^, P^, M); end; End; Procedure EnsurePrefix (var S : String; const Prefix : String; const CaseSensitive : Boolean); var L, M : Integer; P : PChar; Begin if (Prefix <> '') and not MatchLeft (Prefix, S, CaseSensitive) then begin L := Length (S); M := Length (Prefix); SetLength (S, L + M); if L > 0 then begin P := Pointer (S); Inc (P, M); MoveMem (Pointer (S)^, P^, L); end; MoveMem (Pointer (Prefix)^, Pointer (S)^, M); end; End; Procedure EnsureNoSuffix (var S : String; const Suffix : String; const CaseSensitive : Boolean); Begin if MatchRight (Suffix, S, CaseSensitive) then SetLength (S, Length (S) - Length (Suffix)); End; Procedure EnsureNoPrefix (var S : String; const Prefix : String; const CaseSensitive : Boolean); var L, M : Integer; P : PChar; Begin if MatchLeft (Prefix, S, CaseSensitive) then begin L := Length (S); M := Length (Prefix); P := Pointer (S); Inc (P, M); MoveMem (P^, Pointer (S)^, L - M); SetLength (S, L - M); end; End; Procedure SetLengthAndZero (var S : String; const NewLength : Integer); var L : Integer; P : PChar; Begin L := Length (S); if L = NewLength then exit; SetLength (S, NewLength); if L > NewLength then exit; P := Pointer (S); Inc (P, L); FillChar (P^, NewLength - L, #0); End; { } { Natural language } { US style billion = 1,000 million } { UK style billion = 1,000,000 million } { } Function Number (const Num : Int64; const USStyle : Boolean = False) : String; var I : Int64; const Eng_minus = 'minus '; Eng_Numbers : Array [0..12] of String = ('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve'); Eng_Prefixes : Array [2..9] of String = ('twen', 'thir', 'four', 'fif', 'six', 'seven', 'eigh', 'nin'); Eng_teen = 'teen'; Eng_ty = 'ty'; Eng_hundred = ' hundred'; Eng_and = ' and '; Eng_thousand = ' thousand'; Eng_million = ' million'; Eng_billion = ' billion'; USBillion : Int64 = 1000000000; UKBillion : Int64 = 1000000000000; Begin if Num < 0 then Result := Eng_minus + Number (-Num) else if Num <= 12 then Result := Eng_Numbers [Num] else if Num <= 19 then Result := Eng_Prefixes [Num mod 10] + Eng_teen else if Num <= 99 then begin Result := Eng_Prefixes [Num div 10] + Eng_ty; if Num mod 10 > 0 then Result := Result + ' ' + Eng_Numbers [Num mod 10]; end else if Num <= 999 then begin Result := Number (Num div 100) + Eng_hundred; if Num mod 100 > 0 then Result := Result + Eng_and + Number (Num mod 100); end else if Num <= 999999 then begin Result := Number (Num div 1000) + Eng_thousand; if Num mod 1000 > 0 then Result := Result + ' ' + Number (Num mod 1000); end else if ((Num < USBillion) and USStyle) or ((Num < UKBillion) and not USStyle) then begin Result := Number (Num div 1000000) + Eng_million; if Num mod 1000000 > 0 then Result := Result + ' ' + Number (Num mod 1000000); end else begin if USStyle then I := USBillion else I := UKBillion; Result := Number (Num div I) + Eng_billion; if Num mod I > 0 then Result := Result + ' ' + Number (Num mod I, USStyle); end; End; Function Number (const Num : Extended; const USStyle : Boolean = False) : String; const Eng_point = ' point'; var N, I : Int64; Begin Result := Number (Trunc (Num), USStyle); N := Abs (Round (Frac (Num) * 1000000)); if N > 0 then begin Result := Result + Eng_point; I := 100000; While (I > 1) and (N > 0) do begin Result := Result + ' ' + Number (N div I); N := N mod I; I := I div 10; end; end; End; Function StorageSize (const Bytes : Int64; const ShortFormat : Boolean) : String; var Size, Suffix : String; Fmt : String; Begin Fmt := iif (ShortFormat, '%1.0f', '%0.1f'); if Bytes < 1024 then begin Size := IntToStr (Bytes); Suffix := iif (ShortFormat, 'b', 'bytes'); end else if Bytes < 1024 * 1024 then begin Size := Format (Fmt, [Bytes / 1024]); Suffix := iif (ShortFormat, 'K', 'Kb'); end else if Bytes < 1024 * 1024 * 1024 then begin Size := Format (Fmt, [Bytes / (1024 * 1024)]); Suffix := iif (ShortFormat, 'M', 'Mb'); end else if Bytes < Int64 (1024) * 1024 * 1024 * 1024 then begin Size := Format (Fmt, [Bytes / (1024 * 1024 * 1024)]); Suffix := iif (ShortFormat, 'G', 'Gb'); end else begin Size := Format (Fmt, [Bytes / (Int64 (1024) * 1024 * 1024 * 1024)]); Suffix := iif (ShortFormat, 'T', 'Tb'); end; if Match ('.0', Size, Length (Size) - 1) then SetLength (Size, Length (Size) - 2); Result := Size + ' ' + Suffix; End; Function TransferRate (const Bytes, MillisecondsElapsed : Int64; const ShortFormat : Boolean) : String; Begin if MillisecondsElapsed <= 0 then Result := '' else Result := StorageSize (Trunc (Bytes / (MillisecondsElapsed / 1000.0)), ShortFormat) + '/s'; End; { } { Pack } { } Function Pack (const D : Int64) : String; Begin SetLength (Result, Sizeof (D)); Move (D, Pointer (Result)^, Sizeof (D)); End; Function Pack (const D : Integer) : String; Begin SetLength (Result, Sizeof (D)); PInteger (Result)^ := D; End; Function Pack (const D : SmallInt) : String; Begin SetLength (Result, Sizeof (D)); PSmallInt (Result)^ := D; End; Function Pack (const D : ShortInt) : String; Begin SetLength (Result, Sizeof (D)); PShortInt (Result)^ := D; End; Function Pack (const D : Byte) : String; Begin Result := Char (D); End; Function Pack (const D : Word) : String; Begin Result := Char (Lo (D)) + Char (Hi (D)); End; Function Pack (const D : String) : String; Begin Result := Pack (Length (D)) + D; End; Function PackShortString (const D : ShortString) : String; Begin Result := D [0] + D; End; Function PackSingle (const D : Single) : String; Begin SetLength (Result, Sizeof (D)); Move (D, Pointer (Result)^, Sizeof (D)); End; Function PackDouble (const D : Double) : String; Begin SetLength (Result, Sizeof (D)); Move (D, Pointer (Result)^, Sizeof (D)); End; Function Pack (const D : Extended) : String; Begin SetLength (Result, Sizeof (D)); Move (D, Pointer (Result)^, Sizeof (D)); End; Function PackCurrency (const D : Currency) : String; Begin SetLength (Result, Sizeof (D)); Move (D, Pointer (Result)^, Sizeof (D)); End; Function PackDateTime (const D : TDateTime) : String; Begin SetLength (Result, Sizeof (D)); Move (D, Pointer (Result)^, Sizeof (D)); End; Function Pack (const D : Boolean) : String; Begin Result := Char (Ord (D)); End; Function UnpackShortString (const D : String) : ShortString; var L : Byte; Begin Assert (Length (D) > 0, 'Invalid argument: String too short'); L := Byte (D [1]); Assert (Length (D) >= L + 1, 'Invalid argument: String too short'); SetLength (Result, L); if L > 0 then Move (D [2], Result [1], L); End; Function UnpackString (const D : String) : String; var L : Integer; Begin L := UnpackInteger (CopyLeft (D, Sizeof (Integer))); Assert (Length (D) >= Sizeof (Integer) + L, 'Invalid argument: String too short'); SetLength (Result, L); if L > 0 then Move (D [5], Result [1], L); End; Function UnpackInteger (const D : String) : Integer; Begin Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short'); Result := PInteger (D)^; End; Function UnpackSingle (const D : String) : Single; Begin Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short'); Move (Pointer (D)^, Result, Sizeof (Result)); End; Function UnpackDouble (const D : String) : Double; Begin Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short'); Move (Pointer (D)^, Result, Sizeof (Result)); End; Function UnpackExtended (const D : String) : Extended; Begin Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short'); Move (Pointer (D)^, Result, Sizeof (Result)); End; Function UnpackBoolean (const D : String) : Boolean; Begin Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short'); Result := PBoolean (D)^; End; Function UnpackDateTime (const D : String) : TDateTime; Begin Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short'); Move (Pointer (D)^, Result, Sizeof (Result)); End; { } { PChar routines } { } Function SkipChar (var P : PChar; const C : Char) : Boolean; var Q : PChar; D : Char; Begin Assert (C <> #0, 'Invalid parameter'); Q := P; if not Assigned (Q) then Result := False else begin D := Q^; if D = #0 then Result := False else if D = C then begin Inc (P); Result := True; end else Result := False; end; End; Function SkipChar (var P : PChar; const C : CharSet) : Boolean; var Q : PChar; D : Char; Begin Q := P; if not Assigned (Q) then Result := False else begin D := Q^; if D = #0 then Result := False else if D in C then begin Inc (P); Result := True; end else Result := False; end; End; Function SkipAll (var P : PChar; const C : Char) : Integer; var Q : PChar; Begin Assert (C <> #0, 'Invalid parameter'); Result := 0; Q := P; if not Assigned (Q) then exit; While (Q^ <> #0) and (Q^ = C) do begin Inc (Q); Inc (Result); end; P := Q; End; Function SkipAll (var P : PChar; const C : CharSet) : Integer; var Q : PChar; Begin Result := 0; Q := P; if not Assigned (Q) then exit; While (Q^ <> #0) and (Q^ in C) do begin Inc (Q); Inc (Result); end; P := Q; End; Function SkipSeq (var P : PChar; const S1, S2 : CharSet) : Boolean; var Q : PChar; C : Char; Begin Q := P; if not Assigned (Q) then begin Result := False; exit; end; C := Q^; if (C = #0) or not (C in S1) then begin Result := False; exit; end; Inc (Q); C := Q^; if (C = #0) or not (C in S2) then Result := False else begin Inc (P, 2); Result := True; end; End; Function SkipSeq (var P : PChar; const S1, S2, S3 : CharSet) : Boolean; var Q : PChar; C : Char; Begin Q := P; if not Assigned (Q) then begin Result := False; exit; end; C := Q^; if (C = #0) or not (C in S1) then begin Result := False; exit; end; Inc (Q); C := Q^; if (C = #0) or not (C in S2) then begin Result := False; exit; end; Inc (Q); C := Q^; if (C = #0) or not (C in S3) then Result := False else begin Inc (P, 3); Result := True; end; End; Function ExtractAll (var P : PChar; const C : Char) : String; var Q : PChar; I : Integer; Begin Q := P; I := SkipAll (P, C); if I = 0 then begin Result := ''; exit; end; SetLength (Result, I); MoveMem (Q^, Pointer (Result)^, I); End; Function ExtractAll (var P : PChar; const C : CharSet) : String; var Q : PChar; I : Integer; Begin Q := P; I := SkipAll (P, C); if I = 0 then begin Result := ''; exit; end; SetLength (Result, I); MoveMem (Q^, Pointer (Result)^, I); End; Function ExtractTo (var P : PChar; const C : CharSet) : String; var S : CharSet; Begin S := C; ComplementCharSet (S); Result := ExtractAll (P, S); End; Function MatchString (const P : PChar; const S : String; const CaseSensitive : Boolean) : Boolean; var T, Q : PChar; I, L : Integer; Begin L := Length (S); if L = 0 then begin Result := False; exit; end; T := P; Q := Pointer (S); if CaseSensitive then begin For I := 1 to L do if (T^ = #0) or (T^ <> Q^) then begin Result := False; exit; end; Result := True; end else begin For I := 1 to L do if (T^ = #0) or not MatchNoCase (T^, Q^) then begin Result := False; exit; end; Result := True; end; End; Function SkipString (var P : PChar; const S : String; const CaseSensitive : Boolean) : Boolean; Begin Result := MatchString (P, S, CaseSensitive); if Result then Inc (P, Length (S)); End; Function ExtractTo (var P : PChar; const S : String; const CaseSensitive : Boolean) : String; var Q : PChar; L : Integer; Begin Q := P; L := 0; While (P^ <> #0) and not MatchString (P, S, CaseSensitive) do begin Inc (P); Inc (L); end; SetLength (Result, L); if L = 0 then exit; MoveMem (Q^, Pointer (Result)^, L); End; { } { Dynamic array functions } { } Function StringArrayLength (const S : Array of String) : Integer; var I : Integer; Begin Result := 0; For I := 0 to Length (S) - 1 do Inc (Result, Length (S [I])); End; Function LongestStringLength (const S : Array of String) : Integer; var I, L : Integer; Begin Result := 0; For I := 0 to Length (S) - 1 do begin L := Length (S [I]); if L > Result then Result := L; end; End; Function Append (var V : CharSetArray; const S : String; const CaseSensitive : Boolean) : Integer; var I, L : Integer; Begin Result := Length (V); L := Length (S); if L > 0 then begin SetLength (V, Result + L); For I := 1 to L do if CaseSensitive then V [Result + I - 1] := [S [I]] else V [Result + I - 1] := [LowCase (S [I]), UpCase (S [I])]; end; End; Function PosNext (const Find : String; const V : StringArray; const PrevPos : Integer; const IsSortedAscending : Boolean; const CaseSensitive : Boolean) : Integer; var I, L, H : Integer; Begin if IsSortedAscending then // binary search begin if MaxI (PrevPos + 1, 0) = 0 then // find first begin L := 0; H := Length (V) - 1; Repeat I := (L + H) div 2; if IsEqual (V [I], Find, CaseSensitive) then begin While (I > 0) and IsEqual (V [I - 1], Find, CaseSensitive) do Dec (I); Result := I; exit; end else if (CaseSensitive and (V [I] > Find)) or (not CaseSensitive and (LowerCase (V [I]) > LowerCase (Find))) then H := I - 1 else L := I + 1; Until L > H; Result := -1; end else // find next if PrevPos >= Length (V) - 1 then Result := -1 else if IsEqual (V [PrevPos + 1], Find, CaseSensitive) then Result := PrevPos + 1 else Result := -1; end else begin // linear search For I := MaxI (PrevPos + 1, 0) to Length (V) - 1 do if IsEqual (V [I], Find, CaseSensitive) then begin Result := I; exit; end; Result := -1; end; End; Function SingleArrayToStringArray (const V : SingleArray) : StringArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := FloatToStr (V [I]); End; Function DoubleArrayToStringArray (const V : DoubleArray) : StringArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := FloatToStr (V [I]); End; Function ExtendedArrayToStringArray (const V : ExtendedArray) : StringArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := FloatToStr (V [I]); End; Function LongIntArrayToStringArray (const V : LongIntArray) : StringArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := IntToStr (V [I]); End; Function Int64ArrayToStringArray (const V : Int64Array) : StringArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := IntToStr (V [I]); End; Function StringArrayToLongIntArray (const V : StringArray) : LongIntArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := StrToInt (V [I]); End; Function StringArrayToInt64Array (const V : StringArray) : Int64Array; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := StrToInt64 (V [I]); End; Function StringArrayToSingleArray (const V : StringArray) : SingleArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := StrToFloat (V [I]); End; Function StringArrayToDoubleArray (const V : StringArray) : DoubleArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := StrToFloat (V [I]); End; Function StringArrayToExtendedArray (const V : StringArray) : ExtendedArray; var I, L : Integer; Begin L := Length (V); SetLength (Result, L); For I := 0 to L - 1 do Result [I] := StrToFloat (V [I]); End; Function ByteArrayToStr (const V : ByteArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function WordArrayToStr (const V : WordArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function LongWordArrayToStr (const V : LongWordArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function CardinalArrayToStr (const V : CardinalArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function ShortIntArrayToStr (const V : ShortIntArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function SmallIntArrayToStr (const V : SmallIntArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function LongIntArrayToStr (const V : LongIntArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function IntegerArrayToStr (const V : IntegerArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function Int64ArrayToStr (const V : Int64Array; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := IntToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + IntToStr (V [I]); End; Function SingleArrayToStr (const V : SingleArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := FloatToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + FloatToStr (V [I]); End; Function DoubleArrayToStr (const V : DoubleArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := FloatToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + FloatToStr (V [I]); End; Function ExtendedArrayToStr (const V : ExtendedArray; const ItemDelimiter : String) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit; Result := FloatToStr (V [0]); For I := 1 to L - 1 do Result := Result + ItemDelimiter + FloatToStr (V [I]); End; Function StringArrayToStr (const V : StringArray; const ItemDelimiter : String; const QuoteItems : Boolean; const Quote : Char) : String; var I, L : Integer; Begin Result := ''; L := Length (V); if L = 0 then exit else Result := V [0]; if QuoteItems then Result := QuoteText (Result, Quote); For I := 1 to L - 1 do if not QuoteItems then Result := Result + ItemDelimiter + V [I] else Result := Result + ItemDelimiter + QuoteText (V [I], Quote); End; { } { String to Dynamic array } { } Function StrToByteArray (const S : String; const Delimiter : Char) : ByteArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToWordArray (const S : String; const Delimiter : Char) : WordArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToLongWordArray (const S : String; const Delimiter : Char) : LongWordArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToCardinalArray (const S : String; const Delimiter : Char) : CardinalArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToShortIntArray (const S : String; const Delimiter : Char) : ShortIntArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToSmallIntArray (const S : String; const Delimiter : Char) : SmallIntArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToLongIntArray (const S : String; const Delimiter : Char) : LongIntArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToIntegerArray (const S : String; const Delimiter : Char) : IntegerArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToInt64Array (const S : String; const Delimiter : Char) : Int64Array; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0 else Result [L - 1] := StrToInt64 (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToSingleArray (const S : String; const Delimiter : Char) : SingleArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0.0 else Result [L - 1] := StrToFloat (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToDoubleArray (const S : String; const Delimiter : Char) : DoubleArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0.0 else Result [L - 1] := StrToFloat (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToExtendedArray (const S : String; const Delimiter : Char) : ExtendedArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := 0.0 else Result [L - 1] := StrToFloat (Copy (S, F, G)); Inc (F, G + 1); end; End; Function StrToStringArray (const S : String; const Delimiter : Char) : StringArray; var F, G, L, C : Integer; Begin L := 0; F := 1; C := Length (S); While F <= C do begin G := 0; While (F + G <= C) and (S [F + G] <> Delimiter) do Inc (G); Inc (L); SetLength (Result, L); if G = 0 then Result [L - 1] := '' else Result [L - 1] := UnquoteText (Copy (S, F, G)); Inc (F, G + 1); end; End; { } { Self testing code } { } Procedure Test_Copy; Begin { CopyRange } Assert (CopyRange ('', 1, 2) = '', 'CopyRange'); Assert (CopyRange ('', -1, -2) = '', 'CopyRange'); Assert (CopyRange ('1234567890', 5, 7) = '567', 'CopyRange'); Assert (CopyRange ('1234567890', 1, 1) = '1', 'CopyRange'); Assert (CopyRange ('1234567890', 0, 11) = '1234567890', 'CopyRange'); Assert (CopyRange ('1234567890', 7, 4) = '', 'CopyRange'); Assert (CopyRange ('1234567890', 1, 0) = '', 'CopyRange'); Assert (CopyRange ('1234567890', -2, 3) = '123', 'CopyRange'); Assert (CopyRange ('1234567890', 2, -1) = '', 'CopyRange'); Assert (CopyRange ('1234567890', -4, -2) = '', 'CopyRange'); { CopyFrom } Assert (CopyFrom ('a', 0) = 'a', 'CopyFrom'); Assert (CopyFrom ('a', -1) = 'a', 'CopyFrom'); Assert (CopyFrom ('', 1) = '', 'CopyFrom'); Assert (CopyFrom ('', -2) = '', 'CopyFrom'); Assert (CopyFrom ('1234567890', 8) = '890', 'CopyFrom'); Assert (CopyFrom ('1234567890', 11) = '', 'CopyFrom'); Assert (CopyFrom ('1234567890', 0) = '1234567890', 'CopyFrom'); Assert (CopyFrom ('1234567890', -2) = '1234567890', 'CopyFrom'); { CopyLeft } Assert (CopyLeft ('a', 0) = '', 'CopyLeft'); Assert (CopyLeft ('a', -1) = '', 'CopyLeft'); Assert (CopyLeft ('', 1) = '', 'CopyLeft'); Assert (CopyLeft ('b', 1) = 'b', 'CopyLeft'); Assert (CopyLeft ('', -1) = '', 'CopyLeft'); Assert (CopyLeft ('1234567890', 3) = '123', 'CopyLeft'); Assert (CopyLeft ('1234567890', 11) = '1234567890', 'CopyLeft'); Assert (CopyLeft ('1234567890', 0) = '', 'CopyLeft'); Assert (CopyLeft ('1234567890', -2) = '', 'CopyLeft'); { CopyRight } Assert (CopyRight ('a', 0) = '', 'CopyRight'); Assert (CopyRight ('a', -1) = '', 'CopyRight'); Assert (CopyRight ('', 1) = '', 'CopyRight'); Assert (CopyRight ('', -2) = '', 'CopyRight'); Assert (CopyRight ('1234567890', 3) = '890', 'CopyRight'); Assert (CopyRight ('1234567890', 11) = '1234567890', 'CopyRight'); Assert (CopyRight ('1234567890', 0) = '', 'CopyRight'); Assert (CopyRight ('1234567890', -2) = '', 'CopyRight'); End; Procedure Test_CopyEx; Begin { CopyEx } Assert (CopyEx ('', 1, 1) = ''); Assert (CopyEx ('', -2, -1) = ''); Assert (CopyEx ('12345', -2, 2) = '45'); Assert (CopyEx ('12345', -1, 2) = '5'); Assert (CopyEx ('12345', -7, 2) = '12'); Assert (CopyEx ('12345', -5, 2) = '12'); Assert (CopyEx ('12345', 2, -2) = ''); Assert (CopyEx ('12345', -4, 0) = ''); Assert (CopyEx ('12345', -4, 7) = '2345'); Assert (CopyEx ('12345', 2, 2) = '23'); Assert (CopyEx ('12345', -7, -6) = ''); Assert (CopyEx ('12345', 0, 2) = '12'); Assert (CopyEx ('12345', 0, 7) = '12345'); { CopyRangeEx } Assert (CopyRangeEx ('', -2, -1) = ''); Assert (CopyRangeEx ('', 0, 0) = ''); Assert (CopyRangeEx ('12345', -2, -1) = '45'); Assert (CopyRangeEx ('12345', -2, -1) = '45'); Assert (CopyRangeEx ('12345', -2, 5) = '45'); Assert (CopyRangeEx ('12345', 2, -2) = '234'); Assert (CopyRangeEx ('12345', 0, -2) = '1234'); Assert (CopyRangeEx ('12345', 1, -7) = ''); Assert (CopyRangeEx ('12345', 7, -1) = ''); Assert (CopyRangeEx ('12345', -10, 2) = '12'); Assert (CopyRangeEx ('12345', -10, -7) = ''); Assert (CopyRangeEx ('12345', 2, -6) = ''); Assert (CopyRangeEx ('12345', 0, -2) = '1234'); Assert (CopyRangeEx ('12345', 2, 0) = ''); Assert (CopyRangeEx ('', -1, 2) = ''); { CopyFromEx } Assert (CopyFromEx ('', 0) = ''); Assert (CopyFromEx ('', -1) = ''); Assert (CopyFromEx ('12345', 0) = '12345'); Assert (CopyFromEx ('12345', 1) = '12345'); Assert (CopyFromEx ('12345', -5) = '12345'); Assert (CopyFromEx ('12345', -6) = '12345'); Assert (CopyFromEx ('12345', 2) = '2345'); Assert (CopyFromEx ('12345', -4) = '2345'); Assert (CopyFromEx ('12345', 6) = ''); End; Procedure Test_Match; Begin { Match } Assert (not Match ([], '')); Assert (not Match ([], 'a')); Assert (not Match (['a'], '')); Assert (not Match ('', '')); Assert (not Match ('a', '')); Assert (not Match ('', 'a')); Assert (not Match ('A', 'a')); Assert (MatchChars ('A', ['a', 'A']) = 1); Assert (Match ('A', 'A')); Assert (Match ('A', 'a', False)); Assert (Match ('a', 'A', False)); Assert (Match ('A', 'A', False)); Assert (Match (['a'..'z'], 'abcd', 2, 2)); Assert (not Match (['a'..'z'], 'ab', 2, 2)); Assert (not Match (['a'..'z'], 'abcd', 0, 2)); Assert (Match (['a'..'z'], 'abcd', 1, 1)); Assert (Match (['a'..'z'], 'abcd', 1, 0)); Assert (Match (['y'..'z'], 'abcd', 1, 0)); Assert (Match (['y'..'z'], 'abcd', 1, -1)); Assert (not Match ('xx', 'abcdef', 1)); Assert (Match ('x', 'xbcdef', 1)); Assert (Match ('xxxxx', 'abcdxxxxx', 5)); Assert (Match ('abcdef', 'abcdef', 1)); Assert (not Match ('xx', 'abcdef', 1, False)); Assert (Match ('xBCd', 'xbCDef', 1, False)); Assert (Match ('Xxx-xX', 'abcdxxX-xx', 5, False)); Assert (Match ('abcd', 'abcde', 1, True)); Assert (Match ('abc', 'abcde', 1, True)); Assert (Match ('ab', 'abcde', 1, True)); Assert (Match ('a', 'abcde', 1, True)); Assert (Match (' abC-Def{', ' AbC-def{', 1, False)); Assert (Match (['a'..'z'], 'a', False)); Assert (Match (['a'..'z'], 'A', False)); Assert (not Match (['a'..'z'], '-', False)); Assert (not Match (['a'..'z'], 'A', True)); Assert (not Match ([], 'A')); Assert (MatchLeft ('aBc1', 'ABC1D', False), 'MatchLeft'); Assert (MatchLeft ('aBc1', 'aBc1D', True), 'MatchLeft'); Assert (not MatchLeft ('ABc1', 'AB1D', False), 'MatchLeft'); Assert (not MatchLeft ('aBc1', 'aBC1D', True), 'MatchLeft'); Assert (MatchCount ('a', 'baaab', 2, 5) = 3, 'MatchCount'); Assert (MatchCount ('a', 'baaab', 2, 2) = 2, 'MatchCount'); Assert (MatchCount ('a', 'baaab', 2, 0) = 0, 'MatchCount'); Assert (MatchCount ('a', 'baaab', 1, 5) = 0, 'MatchCount'); Assert (MatchCount ('a', 'aaab', -1, 5) = 3, 'MatchCount'); Assert (MatchCount ('a', 'aaab', -1, 1) = 1, 'MatchCount'); { MatchFileMask } Assert (MatchFileMask ('*', 'A'), 'MatchFileMask.1'); Assert (MatchFileMask ('?', 'A'), 'MatchFileMask.2'); Assert (MatchFileMask ('', 'A'), 'MatchFileMask.3'); Assert (MatchFileMask ('', ''), 'MatchFileMask.4'); Assert (not MatchFileMask ('X', ''), 'MatchFileMask.5'); Assert (MatchFileMask ('A?', 'A'), 'MatchFileMask.6'); Assert (MatchFileMask ('A?', 'AB'), 'MatchFileMask.7'); Assert (MatchFileMask ('A*B*C', 'ACBDC'), 'MatchFileMask.8'); Assert (MatchFileMask ('A*B*?', 'ACBDC'), 'MatchFileMask.9'); End; Procedure Test_Pos; var ChS : CharSet; Begin { Pos } ChS := []; Assert (Pos ('', 'a') = 0); Assert (Pos (ChS, 'a') = 0); Assert (PosSeq (AsCharSetArray (ChS), 'a') = 0); ChS := ['a']; Assert (Pos ('a', 'a') = 1); Assert (Pos (ChS, 'a') = 1); Assert (Pos ('a', '') = 0); Assert (Pos (ChS, '') = 0); Assert (Pos ('a', 'aa') = 1); Assert (Pos (ChS, 'aa') = 1); Assert (Pos ('a', 'ba') = 2); Assert (Pos (ChS, 'ba') = 2); Assert (Pos ('a', 'zx') = 0); Assert (Pos (ChS, 'zx') = 0); Assert (PosSeq (AsCharSetArray (ChS), 'a') = 1); Assert (PosSeq (AsCharSetArray (ChS), 'a') = 1); Assert (PosSeq (AsCharSetArray (ChS), 'ba') = 2); Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'a') = 0); Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'aa') = 1); Assert (PosSeq (AsCharSetArray ([ChS, ['a'..'z']]), 'ak') = 1); Assert (Pos ('ab', 'a') = 0); Assert (Pos ('ab', 'ab') = 1); Assert (Pos ('ab', 'zxab') = 3); Assert (Pos ('ab', '') = 0); Assert (Pos ('ab', 'axdba') = 0); ChS := ['a'..'z']; Assert (Pos ('a', 'abac', [foReverse]) = 3); Assert (Pos (ChS, 'abac', [foReverse]) = 4); Assert (Pos ('ab', 'abacabac', [foReverse]) = 5); Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'aa-b-cc', [foReverse]) = 6); Assert (Pos ('a', 'abac', [foNonMatch]) = 2); Assert (Pos (ChS, 'abac1a', [foNonMatch]) = 5); Assert (Pos ('ab', 'abacabac', [foNonMatch]) = 3); Assert (Pos ('aa', 'aaacabac', [foNonMatch]) = 3); Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'aa-b-cc', [foNonMatch]) = 3); Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'aabc-cc', [foNonMatch]) = 5); Assert (Pos ('a', 'AbAc', [foCaseInsensitive]) = 1); Assert (Pos (ChS, 'AbAc', [foCaseInsensitive]) = 1); Assert (Pos ('ba', 'ABAcabac', [foCaseInsensitive]) = 2); Assert (PosSeq (AsCharSetArray ([ChS, ChS]), '-AC-b-cc', [foCaseInsensitive]) = 2); Assert (Pos ('aa', 'aab', [foOverlapping, foNonMatch]) = 2); Assert (Pos ('aa', 'aab', [foNonMatch]) = 0); Assert (PosSeq ([['a'], ['a']], 'aab', [foOverlapping, foNonMatch]) = 2); Assert (PosSeq ([['a'], ['a']], 'aab', [foNonMatch]) = 0); ChS := ['a']; Assert (Pos ('a', 'abac', [], 2) = 3); Assert (Pos (ChS, 'abac', [], 2) = 3); Assert (Pos ('ab', 'abacabac', [], 2) = 5); Assert (PosSeq ([ChS, ChS], 'aa-b-aa', [], 2) = 6); Assert (Pos ('a', 'accca', [], 2, 4) = 0); Assert (Pos (ChS, 'accca', [], 2, 4) = 0); Assert (Pos ('ab', 'abbbab', [], 2, 5) = 0); Assert (PosSeq ([ChS, ChS], 'aabbbaa', [], 2, 5) = 0); { PosBMH } Assert (PosBMH ('', 'ABCD012012345') = 0, 'PosBMH'); Assert (PosBMH ('123', '') = 0, 'PosBMH'); Assert (PosBMH ('', '') = 0, 'PosBMH'); Assert (PosBMH ('123', 'ABCD012012345') = 9, 'PosBMH'); Assert (PosBMH ('12', 'ABCD012012345') = 6, 'PosBMH'); Assert (PosBMH ('123', 'ABCD012012345', 0, 0) = 9, 'PosBMH'); Assert (PosBMH ('123', 'ABCD012012345', 9) = 9, 'PosBMH'); Assert (PosBMH ('123', 'ABCD012012345', 10) = 0, 'PosBMH'); Assert (PosBMH ('123', 'ABCD012012345', 9, 9) = 9, 'PosBMH'); Assert (PosBMH ('123', 'ABCD012012345', 15, 20) = 0, 'PosBMH'); End; Procedure Test_CopyDelim; Begin { CopyBefore } Assert (CopyBefore ('1234543210', '4', True) = '123'); Assert (CopyBefore ('1234543210', '4', False) = '123'); Assert (CopyBefore ('1234543210', '6', True) = '1234543210'); Assert (CopyBefore ('1234543210', '6', False) = ''); Assert (CopyBefore ('1234543210', ['2', '4'], True) = '1'); Assert (CopyBefore ('1234543210', ['2', '4'], False) = '1'); Assert (CopyBefore ('1234543210', ['6', 'a'], True) = '1234543210'); Assert (CopyBefore ('1234543210', ['6', 'a'], False) = ''); { CopyAfter } Assert (CopyAfter ('1234543210', '4', True) = '543210'); Assert (CopyAfter ('1234543210', '4', False) = '543210'); Assert (CopyAfter ('1234543210', '6', True) = '1234543210'); Assert (CopyAfter ('1234543210', '6', False) = ''); Assert (CopyAfter ('1234543210', ['4', '5'], True) = '543210'); Assert (CopyAfter ('1234543210', ['4', '5'], False) = '543210'); Assert (CopyAfter ('1234543210', ['6', 'a'], True) = '1234543210'); Assert (CopyAfter ('1234543210', ['6', 'a'], False) = ''); { CopyFrom } Assert (CopyFrom ('1234543210', '4') = '4543210'); Assert (CopyFrom ('1234543210', '6') = ''); Assert (CopyFrom ('1234543210', '9', True) = '1234543210'); Assert (CopyFrom ('1234543210', '4', True, [], 5) = '43210'); Assert (CopyFrom ('1234543210', '6', True, [], 5) = '543210'); Assert (CopyFrom ('1234543210', ['4', '5']) = '4543210'); { CopyTo } Assert (CopyTo ('1234543210', '4', True) = '1234'); Assert (CopyTo ('1234543210', '4', False) = '1234'); Assert (CopyTo ('1234543210', '6', True) = '1234543210'); Assert (CopyTo ('1234543210', '6', False) = ''); { CopyBetween } Assert (CopyBetween ('1234543210', '3', '3', False, False) = '454'); Assert (CopyBetween ('1234543210', '3', '4', False, False) = ''); Assert (CopyBetween ('1234543210', '4', '3', False, False) = '54'); Assert (CopyBetween ('1234543210', '4', '6', False, False) = ''); Assert (CopyBetween ('1234543210', '4', '6', False, True) = '543210'); Assert (CopyBetween ('1234543210', '3', ['2', '3'], False, False) = '454'); Assert (CopyBetween ('1234543210', '3', ['4', '5'], False, False) = ''); Assert (CopyBetween ('1234543210', '4', ['2', '3'], False, False) = '54'); Assert (CopyBetween ('1234543210', '4', ['6', '7'], False, False) = ''); Assert (CopyBetween ('1234543210', '4', ['6'], False, True) = '543210'); End; Procedure Test_Replace; Begin { Replace } Assert (Replace ('a', 'b', 'bababa') = 'bbbbbb'); Assert (Replace ('a', '', 'bababa') = 'bbb'); Assert (Replace ('a', '', 'aaa') = ''); Assert (Replace ('aba', 'x', 'bababa') = 'bxba'); Assert (Replace ('b', 'bb', 'bababa') = 'bbabbabba'); Assert (Replace ('c', 'aa', 'bababa') = 'bababa'); Assert (Replace ('ba', '', 'bababa') = ''); Assert (Replace ('BA', '', 'bababa', [foCaseInsensitive]) = ''); Assert (Replace ('BA', 'X', 'bababa', [foCaseInsensitive]) = 'XXX'); Assert (Replace ('BA', 'X', 'bababa', [foCaseInsensitive], 2) = 'aXX'); Assert (Replace ('aa', '12', 'aaaaa') = '1212a'); Assert (Replace ('aa', 'a', 'aaaaa') = 'aaa'); Assert (Replace (['b'], 'z', 'bababa') = 'zazaza'); Assert (Replace (['b', 'a'], 'z', 'bababa') = 'zzzzzz'); Assert (QuoteText ('Abe''s', '''') = '''Abe''''s''', 'QuoteText'); Assert (RemoveAll (['a', 'z'], 'bazabazza') = 'bb', 'Remove'); Assert (RemoveDup ('a', 'azaazzel') = 'azazzel', 'RemoveDup'); Assert (Replace ('a', 'b', 'bababaa', [foReverse], 1, -2, 2) = 'babbbba'); End; Procedure Test_Count; const C : CharSet = ['a'..'z']; Begin { Count } Assert (Count ('xyz', 'abcxyzdexxyxyz') = 2); Assert (Count ('xx', 'axxbxxxx') = 3); Assert (Count ('xx', 'axxbxxx') = 2); Assert (Count ('x', 'abcxyzdexxyxyz') = 4); Assert (Count ('q', 'abcxyzdexxyxyz') = 0); Assert (Count (C, 'abcxyzdexxyxyz') = 14); End; Procedure Test_PosEx; Begin { PosEx } Assert (PosEx ('A', 'XAXAAXAAA', [], 1, -1, 1) = 2); Assert (PosEx ('A', 'XAXAAXAAA', [], 1, -1, 3) = 5); Assert (PosEx ('A', 'XAXAAXAAA', [], 1, -1, 6) = 9); Assert (PosEx ('A', 'XAXAAXAAA', [], 1, -1, 7) = 0); End; Procedure Test_Case; var S : String; Ch : Char; Begin { Case functions } For Ch := #0 to #255 do begin Assert (UpCase (Ch) = UpperCase (Ch), 'UpCase = UpperCase'); Assert (LowCase (Ch) = LowerCase (Ch), 'UpCase = UpperCase'); end; For Ch := 'A' to 'Z' do begin Assert (LowCase (Ch) <> Ch, 'LowCase'); Assert (UpCase (Ch) = Ch, 'UpCase'); end; For Ch := 'a' to 'z' do begin Assert (UpCase (Ch) <> Ch, 'LowCase'); Assert (LowCase (Ch) = Ch, 'UpCase'); end; Assert (['a'..'c', 'A'..'C'] = CaseInsensitiveCharSet (['A', 'b', 'C']), 'InsensitiveCharSet'); Assert (FirstUp ('abra') = 'Abra', 'FirstUp'); Assert (FirstUp ('') = '', 'FirstUp'); Assert (LowCase ('A') = 'a', 'LowCase'); Assert (UpCase ('a') = 'A', 'UpCase'); Assert (LowCase ('-') = '-', 'LowCase'); Assert (UpCase ('}') = '}', 'UpCase'); S := 'aBcDEfg-123'; ConvertUpper (S); Assert (S = 'ABCDEFG-123', 'ConvertUpper'); S := 'aBcDEfg-123'; ConvertLower (S); Assert (S = 'abcdefg-123', 'ConvertLower'); S := ''; ConvertLower (S); Assert (S = '', 'ConvertLower'); S := 'abc'; ConvertLower (S); Assert (S = 'abc', 'ConvertLower'); Assert (IsEqualNoCase ('@ABCDEFGHIJKLMNOPQRSTUVWXYZ` ', '@abcdefghijklmnopqrstuvwxyz` '), 'IsEqualNoCase'); End; Procedure Test_Misc; var S : String; Ch : Char; I : Integer; Begin { Dup } Assert (Dup ('xy', 3) = 'xyxyxy', 'Dup'); Assert (Dup ('', 3) = '', 'Dup'); Assert (Dup ('a', 0) = '', 'Dup'); Assert (Dup ('a', -1) = '', 'Dup'); Ch := 'x'; Assert (Dup (Ch, 6) = 'xxxxxx', 'Dup'); Assert (Dup (Ch, 0) = '', 'Dup'); Assert (Dup (Ch, -1) = '', 'Dup'); { Trim } Assert (TrimLeft (' 123 ') = '123 ', 'TrimLeft'); Assert (TrimLeftStr (' 123 ', ' ') = ' 123 ', 'TrimLeft'); Assert (TrimRight (' 123 ') = ' 123', 'TrimRight'); Assert (TrimRightStr (' 123 ', ' ') = ' 123 ', 'TrimRight'); Assert (Trim (' 123 ', [' ']) = '123', 'Trim'); Assert (Trim ('', [' ']) = '', 'Trim'); Assert (Trim ('X', [' ']) = 'X', 'Trim'); Assert (TrimQuotes ('"123"') = '123', 'TrimQuotes'); Assert (TrimQuotes ('"1""23"') = '1""23', 'TrimQuotes'); { Pad } Assert (PadLeft ('xxx', 'y', 6) = 'yyyxxx', 'PadLeft'); Assert (PadLeft ('xxx', 'y', 2, True) = 'xx', 'PadLeft'); Assert (PadRight ('xxx', 'y', 6) = 'xxxyyy', 'PadRight'); Assert (PadRight ('xxx', 'y', 2, True) = 'xx', 'PadRight'); Assert (Pad ('xxx', 'y', 7) = 'yyxxxyy', 'Pad'); Assert (Pad (123, 8) = '00000123', 'Pad'); Assert (Pad (0, 1) = '0', 'Pad'); Assert (Pad (0, 0, True) = '', 'Pad'); Assert (Pad (0, 0) = '0', 'Pad'); Assert (PadLeft ('x', ' ', 3, True) = ' x', 'PadLeft'); Assert (PadLeft ('xabc', ' ', 3, True) = 'xab', 'PadLeft'); { Paste } S := '1234567890'; I := 1; Paste ('2', S, I, False); Assert (S = '2234567890', 'Paste'); Paste ('012', S, I, False, 2, 3); Assert (S = '2124567890', 'Paste'); Paste ('0', S, I, True); Assert (S = '2120567890', 'Paste'); Paste ('0', S, I, True); Assert (S = '2100567890', 'Paste'); Paste ('12', S, I, False); Assert (S = '2120567890', 'Paste'); { Type checking } Assert (IsNumber ('1234567890'), 'IsNumber'); Assert (IsInteger ('-1234567890'), 'IsInteger'); Assert (IsReal ('-1234.567890'), 'IsReal'); Assert (IsQuotedString ('"ABC""D"'), 'IsQuotedString'); Assert (IsQuotedString ('"A"'), 'IsQuotedString'); Assert (not IsQuotedString ('"ABC""D'''), 'IsQuotedString'); Assert (not IsQuotedString ('"ABC""D'), 'IsQuotedString'); Assert (not IsQuotedString ('"'), 'IsQuotedString'); Assert (not IsQuotedString (''), 'IsQuotedString'); Assert (IsQuotedString (''''''), 'IsQuotedString'); Assert (not IsQuotedString ('''a'''''), 'IsQuotedString'); Assert (UnQuoteText ('"123"') = '123', 'UnQuoteText'); Assert (UnQuoteText ('"1""23"') = '1"23', 'UnQuoteText'); { Reverse } Assert (Reversed ('12345') = '54321', 'Reverse'); Assert (Reversed ('1234') = '4321', 'Reverse'); { Join / Split } Assert (Join (Split ('x yy zzz', ' ')) = 'x yy zzz', 'Join/Split'); Assert (Join (Split (' x yy zzz ', ' ')) = ' x yy zzz ', 'Join/Split'); { CharClassStr } Assert (CharSetToCharClassStr (['a'..'z']) = '[a-z]', 'CharClassStr'); Assert (CharSetToCharClassStr (CompleteCharSet) = '.', 'CharClassStr'); Assert (CharSetToCharClassStr ([#0..#31]) = '[\x0-\x1F]', 'CharClassStr'); Assert (CharSetToCharClassStr ([#0..#32]) = '[\x0- ]', 'CharClassStr'); Assert (CharSetToCharClassStr (CompleteCharSet - ['a']) = '[^a]', 'CharClassStr'); Assert (CharSetToCharClassStr (CompleteCharSet - ['a'..'z']) = '[^a-z]', 'CharClassStr'); Assert (CharSetToCharClassStr (['a'..'b']) = '[ab]', 'CharClassStr'); Assert (CharSetToCharClassStr ([]) = '[]', 'CharClassStr'); Assert (CharClassStrToCharSet ('[a]') = ['a'], 'CharClassStr'); Assert (CharClassStrToCharSet ('[]') = [], 'CharClassStr'); Assert (CharClassStrToCharSet ('.') = CompleteCharSet, 'CharClassStr'); Assert (CharClassStrToCharSet ('') = [], 'CharClassStr'); Assert (CharClassStrToCharSet ('[a-z]') = ['a'..'z'], 'CharClassStr'); Assert (CharClassStrToCharSet ('[^a-z]') = CompleteCharSet - ['a'..'z'], 'CharClassStr'); Assert (CharClassStrToCharSet ('[-]') = ['-'], 'CharClassStr'); Assert (CharClassStrToCharSet ('[a-]') = ['a', '-'], 'CharClassStr'); Assert (CharClassStrToCharSet ('[\x5]') = [#$5], 'CharClassStr'); Assert (CharClassStrToCharSet ('[\x1f]') = [#$1f], 'CharClassStr'); Assert (CharClassStrToCharSet ('[\x10-]') = [#$10, '-'], 'CharClassStr'); Assert (CharClassStrToCharSet ('[\x10-\x1f]') = [#$10..#$1f], 'CharClassStr'); Assert (CharClassStrToCharSet ('[\x10-\xf]') = [], 'CharClassStr'); { Ensure } S := 'ABC'; EnsurePrefix (S, '\'); Assert (S = '\ABC', 'EnsurePrefix'); EnsureSuffix (S, '\'); Assert (S = '\ABC\', 'EnsureSuffix'); EnsureNoPrefix (S, '\'); Assert (S = 'ABC\', 'EnsureNoPrefix'); EnsureNoSuffix (S, '\'); Assert (S = 'ABC', 'EnsureNoSuffix'); End; Procedure Test_RegEx; var I : Integer; Begin { MatchPattern } Assert (MatchPattern ('a*b', 'ab'), 'MatchPattern'); Assert (MatchPattern ('a*b', 'aab'), 'MatchPattern'); Assert (MatchPattern ('a*b', 'accb'), 'MatchPattern'); Assert (not MatchPattern ('a*b', 'a'), 'MatchPattern'); Assert (MatchPattern ('a?b', 'acb'), 'MatchPattern'); Assert (not MatchPattern ('a?b', 'ab'), 'MatchPattern'); Assert (MatchPattern ('a[^a]', 'ab'), 'MatchPattern'); Assert (MatchPattern ('a[0-9a-z]', 'ab'), 'MatchPattern'); Assert (MatchPattern ('', ''), 'MatchPattern'); Assert (not MatchPattern ('', 'a'), 'MatchPattern'); Assert (not MatchPattern ('a', ''), 'MatchPattern'); Assert (not MatchPattern ('?', ''), 'MatchPattern'); { MatchQuantSeq } Assert (MatchQuantSeq (I, [cs_Alpha], [mqOnce], 'a', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha], [mqAny], 'a', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha], [mqLeastOnce], 'a', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha], [mqOptional], 'a', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha], [mqOnce], 'ab', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha], [mqAny], 'ab', [])); Assert (I = 2); Assert (MatchQuantSeq (I, [cs_Alpha], [mqLeastOnce], 'ab', [])); Assert (I = 2); Assert (MatchQuantSeq (I, [cs_Alpha], [mqOptional], 'ab', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha], [mqOnce], 'abc', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha], [mqAny], 'abc', [])); Assert (I = 3); Assert (MatchQuantSeq (I, [cs_Alpha], [mqLeastOnce], 'abc', [])); Assert (I = 3); Assert (MatchQuantSeq (I, [cs_Alpha], [mqOptional], 'abc', [])); Assert (I = 1); Assert (not MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOnce, mqOnce], 'ab12', [])); Assert (I = 0); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqAny, mqOnce], 'abc123', [])); Assert (I = 4); Assert (not MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqLeastOnce, mqAny], '123', [])); Assert (I = 0); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOptional, mqAny], '123abc', [])); Assert (I = 3); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOnce, mqAny], 'a123', [])); Assert (I = 4); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqAny, mqAny], 'abc123', [])); Assert (I = 6); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqLeastOnce, mqOnce], 'ab123', [])); Assert (I = 3); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOptional, mqOptional], '1', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOptional, mqOptional], 'a', [])); Assert (I = 1); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOnce, mqOptional], 'ab', [])); Assert (I = 1); Assert (not MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOptional, mqOnce], 'ab', [])); Assert (I = 0); Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric, cs_Alpha, cs_Numeric], [mqLeastOnce, mqAny, mqOptional, mqOnce], 'a1b2', [])); Assert (I = 4); Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric, cs_Alpha, cs_Numeric], [mqAny, mqOnce, mqOptional, mqOnce], 'a1b2cd3efg4', [])); Assert (I = 4); Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqOptional], 'a1', [moDeterministic])); Assert (I = 2); Assert (not MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqOnce], 'a1', [moDeterministic])); Assert (I = 0); Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric, cs_Alpha, cs_AlphaNumeric], [mqAny, mqOnce, mqAny, mqLeastOnce], 'a1b2cd3efg4', [moDeterministic])); Assert (I = 11); Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqOptional], 'a1', [moNonGreedy])); Assert (I = 0); Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqLeastOnce], 'a1', [moNonGreedy])); Assert (I = 2); Assert (not MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqOnce], 'abc', [moNonGreedy])); Assert (I = 0); Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric, cs_Alpha, cs_Numeric], [mqAny, mqOnce, mqOnce, mqLeastOnce], 'a1bc2de3g4', [moNonGreedy])); Assert (I = 10); End; Procedure SelfTest; Begin Test_Case; Test_Replace; Test_Copy; Test_Match; Test_CopyEx; Test_CopyDelim; Test_Pos; Test_PosEx; Test_Count; Test_Misc; Test_RegEx; End; end.