strutils.pp 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207
  1. {
  2. Delphi/Kylix compatibility unit: String handling routines.
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2005 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$h+}
  13. {$inline on}
  14. unit StrUtils;
  15. interface
  16. uses
  17. SysUtils{, Types};
  18. { ---------------------------------------------------------------------
  19. Case insensitive search/replace
  20. ---------------------------------------------------------------------}
  21. Function AnsiResemblesText(const AText, AOther: string): Boolean;
  22. Function AnsiContainsText(const AText, ASubText: string): Boolean;
  23. Function AnsiStartsText(const ASubText, AText: string): Boolean;
  24. Function AnsiEndsText(const ASubText, AText: string): Boolean;
  25. Function AnsiReplaceText(const AText, AFromText, AToText: string): string;inline;
  26. Function AnsiMatchText(const AText: string; const AValues: array of string): Boolean;inline;
  27. Function AnsiIndexText(const AText: string; const AValues: array of string): Integer;
  28. Function StartsText(const ASubText, AText: string): Boolean; inline;
  29. Function EndsText(const ASubText, AText: string): Boolean; inline;
  30. { ---------------------------------------------------------------------
  31. Case sensitive search/replace
  32. ---------------------------------------------------------------------}
  33. Function AnsiContainsStr(const AText, ASubText: string): Boolean;inline;
  34. Function AnsiStartsStr(const ASubText, AText: string): Boolean;
  35. Function AnsiEndsStr(const ASubText, AText: string): Boolean;
  36. Function AnsiReplaceStr(const AText, AFromText, AToText: string): string;inline;
  37. Function AnsiMatchStr(const AText: string; const AValues: array of string): Boolean;inline;
  38. Function AnsiIndexStr(const AText: string; const AValues: array of string): Integer;
  39. Function StartsStr(const ASubText, AText: string): Boolean;
  40. Function EndsStr(const ASubText, AText: string): Boolean;
  41. Function MatchStr(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  42. Function MatchText(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  43. Function IndexStr(const AText: UnicodeString; const AValues: array of UnicodeString): Integer;
  44. Function IndexText(const AText: UnicodeString; const AValues: array of UnicodeString): Integer;
  45. Operator in (const AText: string; const AValues: array of string):Boolean;inline;
  46. Operator in (const AText: UnicodeString; const AValues: array of UnicodeString):Boolean;inline;
  47. { ---------------------------------------------------------------------
  48. Miscellaneous
  49. ---------------------------------------------------------------------}
  50. Function DupeString(const AText: string; ACount: Integer): string;
  51. Function ReverseString(const AText: string): string;
  52. Function AnsiReverseString(const AText: AnsiString): AnsiString;inline;
  53. Function StuffString(const AText: string; AStart, ALength: Cardinal; const ASubText: string): string;
  54. Function RandomFrom(const AValues: array of string): string; overload;
  55. Function IfThen(AValue: Boolean; const ATrue: string; const AFalse: string = ''): string; overload;
  56. function NaturalCompareText (const S1 , S2 : string ): Integer ;
  57. function NaturalCompareText(const Str1, Str2: string; const ADecSeparator, AThousandSeparator: Char): Integer;
  58. { ---------------------------------------------------------------------
  59. VB emulations.
  60. ---------------------------------------------------------------------}
  61. Function LeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;inline;
  62. Function RightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  63. Function MidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;inline;
  64. Function RightBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;inline;
  65. Function MidBStr(const AText: AnsiString; const AByteStart, AByteCount: SizeInt): AnsiString;inline;
  66. Function AnsiLeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;inline;
  67. Function AnsiRightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;inline;
  68. Function AnsiMidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;inline;
  69. Function LeftBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;inline;
  70. Function LeftStr(const AText: WideString; const ACount: SizeInt): WideString;inline;
  71. Function RightStr(const AText: WideString; const ACount: SizeInt): WideString;
  72. Function MidStr(const AText: WideString; const AStart, ACount: SizeInt): WideString;inline;
  73. { ---------------------------------------------------------------------
  74. Extended search and replace
  75. ---------------------------------------------------------------------}
  76. const
  77. { Default word delimiters are any character except the core alphanumerics. }
  78. WordDelimiters: set of Char = [#0..#255] - ['a'..'z','A'..'Z','1'..'9','0'];
  79. resourcestring
  80. SErrAmountStrings = 'Amount of search and replace strings don''t match';
  81. type
  82. TStringSearchOption = (soDown, soMatchCase, soWholeWord);
  83. TStringSearchOptions = set of TStringSearchOption;
  84. TStringSeachOption = TStringSearchOption;
  85. Function SearchBuf(Buf: PChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String; Options: TStringSearchOptions): PChar;
  86. Function SearchBuf(Buf: PChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String): PChar;inline; // ; Options: TStringSearchOptions = [soDown]
  87. Function PosEx(const SubStr, S: string; Offset: SizeUint): SizeInt;
  88. Function PosEx(const SubStr, S: string): SizeInt;inline; // Offset: Cardinal = 1
  89. Function PosEx(c:char; const S: string; Offset: SizeUint): SizeInt;
  90. Function PosEx(const SubStr, S: UnicodeString; Offset: SizeUint): SizeInt;
  91. Function PosEx(c: WideChar; const S: UnicodeString; Offset: SizeUint): SizeInt;
  92. Function PosEx(const SubStr, S: UnicodeString): Sizeint;inline; // Offset: Cardinal = 1
  93. function StringsReplace(const S: string; OldPattern, NewPattern: array of string; Flags: TReplaceFlags): string;
  94. { ---------------------------------------------------------------------
  95. Delphi compat
  96. ---------------------------------------------------------------------}
  97. Function ReplaceStr(const AText, AFromText, AToText: string): string;inline;
  98. Function ReplaceText(const AText, AFromText, AToText: string): string;inline;
  99. { ---------------------------------------------------------------------
  100. Soundex Functions.
  101. ---------------------------------------------------------------------}
  102. type
  103. TSoundexLength = 1..MaxInt;
  104. Function Soundex(const AText: string; ALength: TSoundexLength): string;
  105. Function Soundex(const AText: string): string;inline; // ; ALength: TSoundexLength = 4
  106. type
  107. TSoundexIntLength = 1..8;
  108. Function SoundexInt(const AText: string; ALength: TSoundexIntLength): Integer;
  109. Function SoundexInt(const AText: string): Integer;inline; //; ALength: TSoundexIntLength = 4
  110. Function DecodeSoundexInt(AValue: Integer): string;
  111. Function SoundexWord(const AText: string): Word;
  112. Function DecodeSoundexWord(AValue: Word): string;
  113. Function SoundexSimilar(const AText, AOther: string; ALength: TSoundexLength): Boolean;inline;
  114. Function SoundexSimilar(const AText, AOther: string): Boolean;inline; //; ALength: TSoundexLength = 4
  115. Function SoundexCompare(const AText, AOther: string; ALength: TSoundexLength): Integer;inline;
  116. Function SoundexCompare(const AText, AOther: string): Integer;inline; //; ALength: TSoundexLength = 4
  117. Function SoundexProc(const AText, AOther: string): Boolean;
  118. type
  119. TCompareTextProc = Function(const AText, AOther: string): Boolean;
  120. Const
  121. AnsiResemblesProc: TCompareTextProc = @SoundexProc;
  122. { ---------------------------------------------------------------------
  123. Other functions, based on RxStrUtils.
  124. ---------------------------------------------------------------------}
  125. type
  126. TRomanConversionStrictness = (rcsStrict, rcsRelaxed, rcsDontCare);
  127. resourcestring
  128. SInvalidRomanNumeral = '%s is not a valid Roman numeral';
  129. function IsEmptyStr(const S: string; const EmptyChars: TSysCharSet): Boolean;
  130. function DelSpace(const S: string): string;
  131. function DelChars(const S: string; Chr: Char): string;
  132. function DelSpace1(const S: string): string;
  133. function Tab2Space(const S: string; Numb: Byte): string;
  134. function NPos(const C: string; S: string; N: Integer): SizeInt;
  135. Function RPosEX(C:char;const S : AnsiString;offs:cardinal):SizeInt; overload;
  136. Function RPosex (Const Substr : AnsiString; Const Source : AnsiString;offs:cardinal) : SizeInt; overload;
  137. Function RPos(c:char;const S : AnsiString):SizeInt; overload;
  138. Function RPos (Const Substr : AnsiString; Const Source : AnsiString) : SizeInt; overload;
  139. function AddChar(C: Char; const S: string; N: Integer): string;
  140. function AddCharR(C: Char; const S: string; N: Integer): string;
  141. function PadLeft(const S: string; N: Integer): string;inline;
  142. function PadRight(const S: string; N: Integer): string;inline;
  143. function PadCenter(const S: string; Len: SizeInt): string;
  144. function Copy2Symb(const S: string; Symb: Char): string;
  145. function Copy2SymbDel(var S: string; Symb: Char): string;
  146. function Copy2Space(const S: string): string;inline;
  147. function Copy2SpaceDel(var S: string): string;inline;
  148. function AnsiProperCase(const S: string; const WordDelims: TSysCharSet): string;
  149. function WordCount(const S: string; const WordDelims: TSysCharSet): SizeInt;
  150. function WordPosition(const N: Integer; const S: string; const WordDelims: TSysCharSet): SizeInt;
  151. function ExtractWord(N: Integer; const S: string; const WordDelims: TSysCharSet): string;inline;
  152. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  153. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; out Pos: SizeInt): string;
  154. {$ENDIF}
  155. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; out Pos: Integer): string;
  156. function ExtractDelimited(N: Integer; const S: string; const Delims: TSysCharSet): string;
  157. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  158. function ExtractSubstr(const S: string; var Pos: SizeInt; const Delims: TSysCharSet): string;
  159. {$ENDIF}
  160. function ExtractSubstr(const S: string; var Pos: Integer; const Delims: TSysCharSet): string;
  161. function IsWordPresent(const W, S: string; const WordDelims: TSysCharSet): Boolean;
  162. function FindPart(const HelpWilds, InputStr: string): SizeInt;
  163. function IsWild(InputStr, Wilds: string; IgnoreCase: Boolean): Boolean;
  164. function XorString(const Key, Src: ShortString): ShortString;
  165. function XorEncode(const Key, Source: string): string;
  166. function XorDecode(const Key, Source: string): string;
  167. function GetCmdLineArg(const Switch: string; SwitchChars: TSysCharSet): string;
  168. function Numb2USA(const S: string): string;
  169. function Hex2Dec(const S: string): Longint;
  170. function Hex2Dec64(const S: string): int64;
  171. function Dec2Numb(N: Longint; Len, Base: Byte): string;
  172. function Numb2Dec(S: string; Base: Byte): Longint;
  173. function IntToBin(Value: Longint; Digits, Spaces: Integer): string;
  174. function IntToBin(Value: Longint; Digits: Integer): string;
  175. function intToBin(Value: int64; Digits:integer): string;
  176. function IntToRoman(Value: Longint): string;
  177. function TryRomanToInt(S: String; out N: LongInt; Strictness: TRomanConversionStrictness = rcsRelaxed): Boolean;
  178. function RomanToInt(const S: string; Strictness: TRomanConversionStrictness = rcsRelaxed): Longint;
  179. function RomanToIntDef(Const S : String; const ADefault: Longint = 0; Strictness: TRomanConversionStrictness = rcsRelaxed): Longint;
  180. procedure BinToHex(BinValue, HexValue: PChar; BinBufSize: Integer);
  181. function HexToBin(HexValue, BinValue: PChar; BinBufSize: Integer): Integer;
  182. const
  183. DigitChars = ['0'..'9'];
  184. Brackets = ['(',')','[',']','{','}'];
  185. StdWordDelims = [#0..' ',',','.',';','/','\',':','''','"','`'] + Brackets;
  186. StdSwitchChars = ['-','/'];
  187. function PosSet (const c:TSysCharSet;const s : ansistring ):SizeInt;
  188. function PosSet (const c:string;const s : ansistring ):SizeInt;
  189. function PosSetEx (const c:TSysCharSet;const s : ansistring;count:Integer ):SizeInt;
  190. function PosSetEx (const c:string;const s : ansistring;count:Integer ):SizeInt;
  191. Procedure Removeleadingchars(VAR S : AnsiString; Const CSet:TSysCharset);
  192. Procedure RemoveTrailingChars(VAR S : AnsiString;Const CSet:TSysCharset);
  193. Procedure RemovePadChars(VAR S : AnsiString;Const CSet:TSysCharset);
  194. function TrimLeftSet(const S: String;const CSet:TSysCharSet): String;
  195. Function TrimRightSet(const S: String;const CSet:TSysCharSet): String;
  196. function TrimSet(const S: String;const CSet:TSysCharSet): String;
  197. type
  198. SizeIntArray = array of SizeInt;
  199. Function FindMatchesBoyerMooreCaseSensitive(const S,OldPattern: PChar; const SSize, OldPatternSize: SizeInt; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean;
  200. Function FindMatchesBoyerMooreCaseSensitive(const S,OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean;
  201. Function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: PChar; const SSize, OldPatternSize: SizeInt; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean;
  202. Function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean;
  203. Type
  204. TStringReplaceAlgorithm = (sraDefault, // Default algoritm as used in StringUtils.
  205. sraManySmall, // Algorithm optimized for many small replacements.
  206. sraBoyerMoore // Algorithm optimized for long replacements.
  207. );
  208. Function StringReplace(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags; Algorithm : TStringReplaceAlgorithm = sraDefault): string; overload;
  209. { We need these for backwards compatibility:
  210. The compiler will stop searching and convert to ansistring if the widestring version of stringreplace is used.
  211. They currently simply refer to sysutils, till the new mechanisms are proven to work with unicode.}
  212. Function StringReplace(const S, OldPattern, NewPattern: unicodestring; Flags: TReplaceFlags): unicodestring; overload;
  213. Function StringReplace(const S, OldPattern, NewPattern: widestring; Flags: TReplaceFlags): widestring; overload;
  214. implementation
  215. (*
  216. FindMatchesBoyerMooreCaseSensitive
  217. Finds one or many ocurrences of an ansistring in another ansistring.
  218. It is case sensitive.
  219. * Parameters:
  220. S: The PChar to be searched in. (Read only).
  221. OldPattern: The PChar to be searched. (Read only).
  222. SSize: The size of S in Chars. (Read only).
  223. OldPatternSize: The size of OldPatter in chars. (Read only).
  224. aMatches: SizeInt array where match indexes are returned (zero based) (write only).
  225. aMatchAll: Finds all matches, not just the first one. (Read only).
  226. * Returns:
  227. Nothing, information returned in aMatches parameter.
  228. The function is based in the Boyer-Moore algorithm.
  229. *)
  230. function FindMatchesBoyerMooreCaseSensitive(const S, OldPattern: PChar;
  231. const SSize, OldPatternSize: SizeInt; out aMatches: SizeIntArray;
  232. const aMatchAll: Boolean) : Boolean;
  233. const
  234. ALPHABET_LENGHT=256;
  235. MATCHESCOUNTRESIZER=100; //Arbitrary value. Memory used = MATCHESCOUNTRESIZER * sizeof(SizeInt)
  236. var
  237. //Stores the amount of replaces that will take place
  238. MatchesCount: SizeInt;
  239. //Currently allocated space for matches.
  240. MatchesAllocatedLimit: SizeInt;
  241. type
  242. AlphabetArray=array [0..ALPHABET_LENGHT-1] of SizeInt;
  243. function Max(const a1,a2: SizeInt): SizeInt;
  244. begin
  245. if a1>a2 then Result:=a1 else Result:=a2;
  246. end;
  247. procedure MakeDeltaJumpTable1(out DeltaJumpTable1: AlphabetArray; const aPattern: PChar; const aPatternSize: SizeInt);
  248. var
  249. i: SizeInt;
  250. begin
  251. for i := 0 to ALPHABET_LENGHT-1 do begin
  252. DeltaJumpTable1[i]:=aPatternSize;
  253. end;
  254. //Last char do not enter in the equation
  255. for i := 0 to aPatternSize - 1 - 1 do begin
  256. DeltaJumpTable1[Ord(aPattern[i])]:=aPatternSize -1 - i;
  257. end;
  258. end;
  259. function IsPrefix(const aPattern: PChar; const aPatternSize, aPos: SizeInt): Boolean;
  260. var
  261. i: SizeInt;
  262. SuffixLength: SizeInt;
  263. begin
  264. SuffixLength:=aPatternSize-aPos;
  265. for i := 0 to SuffixLength-1 do begin
  266. if (aPattern[i] <> aPattern[aPos+i]) then begin
  267. exit(false);
  268. end;
  269. end;
  270. Result:=true;
  271. end;
  272. function SuffixLength(const aPattern: PChar; const aPatternSize, aPos: SizeInt): SizeInt;
  273. var
  274. i: SizeInt;
  275. begin
  276. i:=0;
  277. while (aPattern[aPos-i] = aPattern[aPatternSize-1-i]) and (i < aPos) do begin
  278. inc(i);
  279. end;
  280. Result:=i;
  281. end;
  282. procedure MakeDeltaJumpTable2(var DeltaJumpTable2: SizeIntArray; const aPattern: PChar; const aPatternSize: SizeInt);
  283. var
  284. Position: SizeInt;
  285. LastPrefixIndex: SizeInt;
  286. SuffixLengthValue: SizeInt;
  287. begin
  288. LastPrefixIndex:=aPatternSize-1;
  289. Position:=aPatternSize-1;
  290. while Position>=0 do begin
  291. if IsPrefix(aPattern,aPatternSize,Position+1) then begin
  292. LastPrefixIndex := Position+1;
  293. end;
  294. DeltaJumpTable2[Position] := LastPrefixIndex + (aPatternSize-1 - Position);
  295. Dec(Position);
  296. end;
  297. Position:=0;
  298. while Position<aPatternSize-1 do begin
  299. SuffixLengthValue:=SuffixLength(aPattern,aPatternSize,Position);
  300. if aPattern[Position-SuffixLengthValue] <> aPattern[aPatternSize-1 - SuffixLengthValue] then begin
  301. DeltaJumpTable2[aPatternSize - 1 - SuffixLengthValue] := aPatternSize - 1 - Position + SuffixLengthValue;
  302. end;
  303. Inc(Position);
  304. end;
  305. end;
  306. //Resizes the allocated space for replacement index
  307. procedure ResizeAllocatedMatches;
  308. begin
  309. MatchesAllocatedLimit:=MatchesCount+MATCHESCOUNTRESIZER;
  310. SetLength(aMatches,MatchesAllocatedLimit);
  311. end;
  312. //Add a match to be replaced
  313. procedure AddMatch(const aPosition: SizeInt); inline;
  314. begin
  315. if MatchesCount = MatchesAllocatedLimit then begin
  316. ResizeAllocatedMatches;
  317. end;
  318. aMatches[MatchesCount]:=aPosition;
  319. inc(MatchesCount);
  320. end;
  321. var
  322. i,j: SizeInt;
  323. DeltaJumpTable1: array [0..ALPHABET_LENGHT-1] of SizeInt;
  324. DeltaJumpTable2: SizeIntArray;
  325. begin
  326. MatchesCount:=0;
  327. MatchesAllocatedLimit:=0;
  328. SetLength(aMatches,MatchesCount);
  329. if OldPatternSize=0 then begin
  330. Exit;
  331. end;
  332. SetLength(DeltaJumpTable2,OldPatternSize);
  333. MakeDeltaJumpTable1(DeltaJumpTable1,OldPattern,OldPatternSize);
  334. MakeDeltaJumpTable2(DeltaJumpTable2,OldPattern,OldPatternSize);
  335. i:=OldPatternSize-1;
  336. while i < SSize do begin
  337. j:=OldPatternSize-1;
  338. while (j>=0) and (S[i] = OldPattern[j]) do begin
  339. dec(i);
  340. dec(j);
  341. end;
  342. if (j<0) then begin
  343. AddMatch(i+1);
  344. //Only first match ?
  345. if not aMatchAll then break;
  346. inc(i,OldPatternSize);
  347. inc(i,OldPatternSize);
  348. end else begin
  349. i:=i + Max(DeltaJumpTable1[ord(s[i])],DeltaJumpTable2[j]);
  350. end;
  351. end;
  352. SetLength(aMatches,MatchesCount);
  353. Result:=MatchesCount>0;
  354. end;
  355. function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: PChar; const SSize, OldPatternSize: SizeInt; out
  356. aMatches: SizeIntArray; const aMatchAll: Boolean): Boolean;
  357. const
  358. ALPHABET_LENGHT=256;
  359. MATCHESCOUNTRESIZER=100; //Arbitrary value. Memory used = MATCHESCOUNTRESIZER * sizeof(SizeInt)
  360. var
  361. //Lowercased OldPattern
  362. lPattern: string;
  363. //Array of lowercased alphabet
  364. lCaseArray: array [0..ALPHABET_LENGHT-1] of char;
  365. //Stores the amount of replaces that will take place
  366. MatchesCount: SizeInt;
  367. //Currently allocated space for matches.
  368. MatchesAllocatedLimit: SizeInt;
  369. type
  370. AlphabetArray=array [0..ALPHABET_LENGHT-1] of SizeInt;
  371. function Max(const a1,a2: SizeInt): SizeInt;
  372. begin
  373. if a1>a2 then Result:=a1 else Result:=a2;
  374. end;
  375. procedure MakeDeltaJumpTable1(out DeltaJumpTable1: AlphabetArray; const aPattern: PChar; const aPatternSize: SizeInt);
  376. var
  377. i: SizeInt;
  378. begin
  379. for i := 0 to ALPHABET_LENGHT-1 do begin
  380. DeltaJumpTable1[i]:=aPatternSize;
  381. end;
  382. //Last char do not enter in the equation
  383. for i := 0 to aPatternSize - 1 - 1 do begin
  384. DeltaJumpTable1[Ord(aPattern[i])]:=aPatternSize - 1 - i;
  385. end;
  386. end;
  387. function IsPrefix(const aPattern: PChar; const aPatternSize, aPos: SizeInt): Boolean; inline;
  388. var
  389. i: SizeInt;
  390. SuffixLength: SizeInt;
  391. begin
  392. SuffixLength:=aPatternSize-aPos;
  393. for i := 0 to SuffixLength-1 do begin
  394. if (aPattern[i+1] <> aPattern[aPos+i]) then begin
  395. exit(false);
  396. end;
  397. end;
  398. Result:=true;
  399. end;
  400. function SuffixLength(const aPattern: PChar; const aPatternSize, aPos: SizeInt): SizeInt; inline;
  401. var
  402. i: SizeInt;
  403. begin
  404. i:=0;
  405. while (aPattern[aPos-i] = aPattern[aPatternSize-1-i]) and (i < aPos) do begin
  406. inc(i);
  407. end;
  408. Result:=i;
  409. end;
  410. procedure MakeDeltaJumpTable2(var DeltaJumpTable2: SizeIntArray; const aPattern: PChar; const aPatternSize: SizeInt);
  411. var
  412. Position: SizeInt;
  413. LastPrefixIndex: SizeInt;
  414. SuffixLengthValue: SizeInt;
  415. begin
  416. LastPrefixIndex:=aPatternSize-1;
  417. Position:=aPatternSize-1;
  418. while Position>=0 do begin
  419. if IsPrefix(aPattern,aPatternSize,Position+1) then begin
  420. LastPrefixIndex := Position+1;
  421. end;
  422. DeltaJumpTable2[Position] := LastPrefixIndex + (aPatternSize-1 - Position);
  423. Dec(Position);
  424. end;
  425. Position:=0;
  426. while Position<aPatternSize-1 do begin
  427. SuffixLengthValue:=SuffixLength(aPattern,aPatternSize,Position);
  428. if aPattern[Position-SuffixLengthValue] <> aPattern[aPatternSize-1 - SuffixLengthValue] then begin
  429. DeltaJumpTable2[aPatternSize - 1 - SuffixLengthValue] := aPatternSize - 1 - Position + SuffixLengthValue;
  430. end;
  431. Inc(Position);
  432. end;
  433. end;
  434. //Resizes the allocated space for replacement index
  435. procedure ResizeAllocatedMatches;
  436. begin
  437. MatchesAllocatedLimit:=MatchesCount+MATCHESCOUNTRESIZER;
  438. SetLength(aMatches,MatchesAllocatedLimit);
  439. end;
  440. //Add a match to be replaced
  441. procedure AddMatch(const aPosition: SizeInt); inline;
  442. begin
  443. if MatchesCount = MatchesAllocatedLimit then begin
  444. ResizeAllocatedMatches;
  445. end;
  446. aMatches[MatchesCount]:=aPosition;
  447. inc(MatchesCount);
  448. end;
  449. var
  450. i,j: SizeInt;
  451. DeltaJumpTable1: array [0..ALPHABET_LENGHT-1] of SizeInt;
  452. DeltaJumpTable2: SizeIntArray;
  453. //Pointer to lowered OldPattern
  454. plPattern: PChar;
  455. begin
  456. MatchesCount:=0;
  457. MatchesAllocatedLimit:=0;
  458. SetLength(aMatches,MatchesCount);
  459. if OldPatternSize=0 then begin
  460. Exit;
  461. end;
  462. //Build an internal array of lowercase version of every possible char.
  463. for j := 0 to Pred(ALPHABET_LENGHT) do begin
  464. lCaseArray[j]:=AnsiLowerCase(char(j))[1];
  465. end;
  466. //Create the new lowercased pattern
  467. SetLength(lPattern,OldPatternSize);
  468. for j := 0 to Pred(OldPatternSize) do begin
  469. lPattern[j+1]:=lCaseArray[ord(OldPattern[j])];
  470. end;
  471. SetLength(DeltaJumpTable2,OldPatternSize);
  472. MakeDeltaJumpTable1(DeltaJumpTable1,@lPattern[1],OldPatternSize);
  473. MakeDeltaJumpTable2(DeltaJumpTable2,@lPattern[1],OldPatternSize);
  474. plPattern:=@lPattern[1];
  475. i:=OldPatternSize-1;
  476. while i < SSize do begin
  477. j:=OldPatternSize-1;
  478. while (j>=0) and (lCaseArray[Ord(S[i])] = plPattern[j]) do begin
  479. dec(i);
  480. dec(j);
  481. end;
  482. if (j<0) then begin
  483. AddMatch(i+1);
  484. //Only first match ?
  485. if not aMatchAll then break;
  486. inc(i,OldPatternSize);
  487. inc(i,OldPatternSize);
  488. end else begin
  489. i:=i + Max(DeltaJumpTable1[Ord(lCaseArray[Ord(s[i])])],DeltaJumpTable2[j]);
  490. end;
  491. end;
  492. SetLength(aMatches,MatchesCount);
  493. Result:=MatchesCount>0;
  494. end;
  495. function StringReplaceFast(const S, OldPattern, NewPattern: string;
  496. Flags: TReplaceFlags): string;
  497. const
  498. MATCHESCOUNTRESIZER=100; //Arbitrary value. Memory used = MATCHESCOUNTRESIZER * sizeof(SizeInt)
  499. var
  500. //Stores where a replace will take place
  501. Matches: array of SizeInt;
  502. //Stores the amount of replaces that will take place
  503. MatchesCount: SizeInt;
  504. //Currently allocated space for matches.
  505. MatchesAllocatedLimit: SizeInt;
  506. //Uppercase version of pattern
  507. PatternUppercase: string;
  508. //Lowercase version of pattern
  509. PatternLowerCase: string;
  510. //Index
  511. MatchIndex: SizeInt;
  512. MatchLimit: SizeInt;
  513. MatchInternal: SizeInt;
  514. MatchTarget: SizeInt;
  515. AdvanceIndex: SizeInt;
  516. //Miscelanous variables
  517. OldPatternSize: SizeInt;
  518. NewPatternSize: SizeInt;
  519. //Resizes the allocated space for replacement index
  520. procedure ResizeAllocatedMatches;
  521. begin
  522. MatchesAllocatedLimit:=MatchesCount+MATCHESCOUNTRESIZER;
  523. SetLength(Matches,MatchesAllocatedLimit);
  524. end;
  525. //Add a match to be replaced
  526. procedure AddMatch(const aPosition: SizeInt); inline;
  527. begin
  528. if MatchesCount = MatchesAllocatedLimit then begin
  529. ResizeAllocatedMatches;
  530. end;
  531. Matches[MatchesCount]:=aPosition;
  532. inc(MatchesCount);
  533. end;
  534. begin
  535. if (OldPattern='') or (Length(OldPattern)>Length(S)) then begin
  536. //This cases will never match nothing.
  537. Result:=S;
  538. exit;
  539. end;
  540. Result:='';
  541. OldPatternSize:=Length(OldPattern);
  542. MatchesCount:=0;
  543. MatchesAllocatedLimit:=0;
  544. if rfIgnoreCase in Flags then begin
  545. //Different algorithm for case sensitive and insensitive
  546. //This is insensitive, so 2 new ansistrings are created for search pattern, one upper and one lower case.
  547. //It is easy, usually, to create 2 versions of the match pattern than uppercased and lowered case each
  548. //character in the "to be matched" string.
  549. PatternUppercase:=AnsiUpperCase(OldPattern);
  550. PatternLowerCase:=AnsiLowerCase(OldPattern);
  551. MatchIndex:=Length(OldPattern);
  552. MatchLimit:=Length(S);
  553. NewPatternSize:=Length(NewPattern);
  554. while MatchIndex <= MatchLimit do begin
  555. if (S[MatchIndex]=PatternLowerCase[OldPatternSize]) or (S[MatchIndex]=PatternUppercase[OldPatternSize]) then begin
  556. //Match backwards...
  557. MatchInternal:=OldPatternSize-1;
  558. MatchTarget:=MatchIndex-1;
  559. while MatchInternal>=1 do begin
  560. if (S[MatchTarget]=PatternLowerCase[MatchInternal]) or (S[MatchTarget]=PatternUppercase[MatchInternal]) then begin
  561. dec(MatchInternal);
  562. dec(MatchTarget);
  563. end else begin
  564. break;
  565. end;
  566. end;
  567. if MatchInternal=0 then begin
  568. //Match found, all char meet the sequence
  569. //MatchTarget points to char before, so matching is +1
  570. AddMatch(MatchTarget+1);
  571. inc(MatchIndex,OldPatternSize);
  572. if not (rfReplaceAll in Flags) then begin
  573. break;
  574. end;
  575. end else begin
  576. //Match not found
  577. inc(MatchIndex);
  578. end;
  579. end else begin
  580. inc(MatchIndex);
  581. end;
  582. end;
  583. end else begin
  584. //Different algorithm for case sensitive and insensitive
  585. //This is sensitive, so just 1 binary comprare
  586. MatchIndex:=Length(OldPattern);
  587. MatchLimit:=Length(S);
  588. NewPatternSize:=Length(NewPattern);
  589. while MatchIndex <= MatchLimit do begin
  590. if (S[MatchIndex]=OldPattern[OldPatternSize]) then begin
  591. //Match backwards...
  592. MatchInternal:=OldPatternSize-1;
  593. MatchTarget:=MatchIndex-1;
  594. while MatchInternal>=1 do begin
  595. if (S[MatchTarget]=OldPattern[MatchInternal]) then begin
  596. dec(MatchInternal);
  597. dec(MatchTarget);
  598. end else begin
  599. break;
  600. end;
  601. end;
  602. if MatchInternal=0 then begin
  603. //Match found, all char meet the sequence
  604. //MatchTarget points to char before, so matching is +1
  605. AddMatch(MatchTarget+1);
  606. inc(MatchIndex,OldPatternSize);
  607. if not (rfReplaceAll in Flags) then begin
  608. break;
  609. end;
  610. end else begin
  611. //Match not found
  612. inc(MatchIndex);
  613. end;
  614. end else begin
  615. inc(MatchIndex);
  616. end;
  617. end;
  618. end;
  619. //Create room enougth for the result string
  620. SetLength(Result,Length(S)-OldPatternSize*MatchesCount+NewPatternSize*MatchesCount);
  621. MatchIndex:=1;
  622. MatchTarget:=1;
  623. //Matches[x] are 1 based offsets
  624. for MatchInternal := 0 to Pred(MatchesCount) do begin
  625. //Copy information up to next match
  626. AdvanceIndex:=Matches[MatchInternal]-MatchIndex;
  627. if AdvanceIndex>0 then begin
  628. move(S[MatchIndex],Result[MatchTarget],AdvanceIndex);
  629. inc(MatchTarget,AdvanceIndex);
  630. inc(MatchIndex,AdvanceIndex);
  631. end;
  632. //Copy the new replace information string
  633. if NewPatternSize>0 then begin
  634. move(NewPattern[1],Result[MatchTarget],NewPatternSize);
  635. inc(MatchTarget,NewPatternSize);
  636. end;
  637. inc(MatchIndex,OldPatternSize);
  638. end;
  639. if MatchTarget<=Length(Result) then begin
  640. //Add remain data at the end of source.
  641. move(S[MatchIndex],Result[MatchTarget],Length(Result)-MatchTarget+1);
  642. end;
  643. end;
  644. (*
  645. StringReplaceBoyerMoore
  646. Replaces one or many ocurrences of an ansistring in another ansistring by a new one.
  647. It can perform the compare ignoring case (ansi).
  648. * Parameters (Read only):
  649. S: The string to be searched in.
  650. OldPattern: The string to be searched.
  651. NewPattern: The string to replace OldPattern matches.
  652. Flags:
  653. rfReplaceAll: Replace all occurrences.
  654. rfIgnoreCase: Ignore case in OldPattern matching.
  655. * Returns:
  656. The modified string (if needed).
  657. It is memory conservative, just sizeof(SizeInt) per match in blocks off 100 matches
  658. plus Length(OldPattern)*2 in the case of ignoring case.
  659. Memory copies are the minimun necessary.
  660. Algorithm based in the Boyer-Moore string search algorithm.
  661. It is faster when the "S" string is very long and the OldPattern is also
  662. very big. As much big the OldPattern is, faster the search is too.
  663. It uses 2 different helper versions of Boyer-Moore algorithm, one for case
  664. sensitive and one for case INsensitive for speed reasons.
  665. *)
  666. function StringReplaceBoyerMoore(const S, OldPattern, NewPattern: string;Flags: TReplaceFlags): string;
  667. var
  668. Matches: SizeIntArray;
  669. OldPatternSize: SizeInt;
  670. NewPatternSize: SizeInt;
  671. MatchesCount: SizeInt;
  672. MatchIndex: SizeInt;
  673. MatchTarget: SizeInt;
  674. MatchInternal: SizeInt;
  675. AdvanceIndex: SizeInt;
  676. begin
  677. OldPatternSize:=Length(OldPattern);
  678. NewPatternSize:=Length(NewPattern);
  679. if (OldPattern='') or (Length(OldPattern)>Length(S)) then begin
  680. Result:=S;
  681. exit;
  682. end;
  683. if rfIgnoreCase in Flags then begin
  684. FindMatchesBoyerMooreCaseINSensitive(@s[1],@OldPattern[1],Length(S),Length(OldPattern),Matches, rfReplaceAll in Flags);
  685. end else begin
  686. FindMatchesBoyerMooreCaseSensitive(@s[1],@OldPattern[1],Length(S),Length(OldPattern),Matches, rfReplaceAll in Flags);
  687. end;
  688. MatchesCount:=Length(Matches);
  689. //Create room enougth for the result string
  690. SetLength(Result,Length(S)-OldPatternSize*MatchesCount+NewPatternSize*MatchesCount);
  691. MatchIndex:=1;
  692. MatchTarget:=1;
  693. //Matches[x] are 0 based offsets
  694. for MatchInternal := 0 to Pred(MatchesCount) do begin
  695. //Copy information up to next match
  696. AdvanceIndex:=Matches[MatchInternal]+1-MatchIndex;
  697. if AdvanceIndex>0 then begin
  698. move(S[MatchIndex],Result[MatchTarget],AdvanceIndex);
  699. inc(MatchTarget,AdvanceIndex);
  700. inc(MatchIndex,AdvanceIndex);
  701. end;
  702. //Copy the new replace information string
  703. if NewPatternSize>0 then begin
  704. move(NewPattern[1],Result[MatchTarget],NewPatternSize);
  705. inc(MatchTarget,NewPatternSize);
  706. end;
  707. inc(MatchIndex,OldPatternSize);
  708. end;
  709. if MatchTarget<=Length(Result) then begin
  710. //Add remain data at the end of source.
  711. move(S[MatchIndex],Result[MatchTarget],Length(Result)-MatchTarget+1);
  712. end;
  713. end;
  714. function StringReplace(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags; Algorithm: TStringReplaceAlgorithm): string;
  715. begin
  716. Case Algorithm of
  717. sraDefault : Result:=sysutils.StringReplace(S,OldPattern,NewPattern,Flags);
  718. sraManySmall : Result:=StringReplaceFast(S,OldPattern,NewPattern,Flags);
  719. sraBoyerMoore : Result:=StringReplaceBoyerMoore(S,OldPattern,NewPattern,Flags);
  720. end;
  721. end;
  722. function StringReplace(const S, OldPattern, NewPattern: unicodestring; Flags: TReplaceFlags): unicodestring;
  723. begin
  724. Result:=sysutils.StringReplace(S,OldPattern,NewPattern,Flags);
  725. end;
  726. function StringReplace(const S, OldPattern, NewPattern: widestring; Flags: TReplaceFlags): widestring;
  727. begin
  728. Result:=sysutils.StringReplace(S,OldPattern,NewPattern,Flags);
  729. end;
  730. function FindMatchesBoyerMooreCaseSensitive(const S, OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean
  731. ): Boolean;
  732. Var
  733. I : SizeInt;
  734. begin
  735. Result:=FindMatchesBoyerMooreCaseSensitive(PChar(S),Pchar(OldPattern),Length(S),Length(OldPattern),aMatches,aMatchAll);
  736. For I:=0 to pred(Length(AMatches)) do
  737. Inc(AMatches[i]);
  738. end;
  739. function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean
  740. ): Boolean;
  741. Var
  742. I : SizeInt;
  743. begin
  744. Result:=FindMatchesBoyerMooreCaseInSensitive(PChar(S),Pchar(OldPattern),Length(S),Length(OldPattern),aMatches,aMatchAll);
  745. For I:=0 to pred(Length(AMatches)) do
  746. Inc(AMatches[i]);
  747. end;
  748. { ---------------------------------------------------------------------
  749. Possibly Exception raising functions
  750. ---------------------------------------------------------------------}
  751. function Hex2Dec(const S: string): Longint;
  752. var
  753. HexStr: string;
  754. begin
  755. if Pos('$',S)=0 then
  756. HexStr:='$'+ S
  757. else
  758. HexStr:=S;
  759. Result:=StrToInt(HexStr);
  760. end;
  761. function Hex2Dec64(const S: string): int64;
  762. var
  763. HexStr: string;
  764. begin
  765. if Pos('$',S)=0 then
  766. HexStr:='$'+ S
  767. else
  768. HexStr:=S;
  769. Result:=StrToInt64(HexStr);
  770. end;
  771. {
  772. We turn off implicit exceptions, since these routines are tested, and it
  773. saves 20% codesize (and some speed) and don't throw exceptions, except maybe
  774. heap related. If they don't, that is consider a bug.
  775. In the future, be wary with routines that use strtoint, floating point
  776. and/or format() derivatives. And check every divisor for 0.
  777. }
  778. {$IMPLICITEXCEPTIONS OFF}
  779. { ---------------------------------------------------------------------
  780. Case insensitive search/replace
  781. ---------------------------------------------------------------------}
  782. function AnsiResemblesText(const AText, AOther: string): Boolean;
  783. begin
  784. if Assigned(AnsiResemblesProc) then
  785. Result:=AnsiResemblesProc(AText,AOther)
  786. else
  787. Result:=False;
  788. end;
  789. function AnsiContainsText(const AText, ASubText: string): Boolean;
  790. begin
  791. AnsiContainsText:=AnsiPos(AnsiUppercase(ASubText),AnsiUppercase(AText))>0;
  792. end;
  793. function AnsiStartsText(const ASubText, AText: string): Boolean;
  794. begin
  795. Result := (ASubText = '') or AnsiSameText(LeftStr(AText, Length(ASubText)), ASubText);
  796. end;
  797. function AnsiEndsText(const ASubText, AText: string): Boolean;
  798. begin
  799. Result := (ASubText = '') or AnsiSameText(RightStr(AText, Length(ASubText)), ASubText);
  800. end;
  801. function StartsText(const ASubText, AText: string): Boolean; inline;
  802. begin
  803. Result := AnsiStartsText(ASubText, AText);
  804. end;
  805. function EndsText(const ASubText, AText: string): Boolean;
  806. begin
  807. Result := AnsiEndsText(ASubText, AText);
  808. end;
  809. function AnsiReplaceText(const AText, AFromText, AToText: string): string;
  810. begin
  811. Result := StringReplace(AText,AFromText,AToText,[rfReplaceAll,rfIgnoreCase]);
  812. end;
  813. function AnsiMatchText(const AText: string; const AValues: array of string): Boolean;
  814. begin
  815. Result:=(AnsiIndexText(AText,AValues)<>-1)
  816. end;
  817. function AnsiIndexText(const AText: string; const AValues: array of string): Integer;
  818. begin
  819. for Result := Low(AValues) to High(AValues) do
  820. if AnsiSameText(AValues[Result], AText) then
  821. Exit;
  822. Result := -1;
  823. end;
  824. { ---------------------------------------------------------------------
  825. Case sensitive search/replace
  826. ---------------------------------------------------------------------}
  827. function AnsiContainsStr(const AText, ASubText: string): Boolean;
  828. begin
  829. Result := AnsiPos(ASubText,AText)>0;
  830. end;
  831. function AnsiStartsStr(const ASubText, AText: string): Boolean;
  832. begin
  833. Result := (ASubText = '') or (LeftStr(AText, Length(ASubText)) = ASubText);
  834. end;
  835. function AnsiEndsStr(const ASubText, AText: string): Boolean;
  836. begin
  837. Result := (ASubText = '') or (RightStr(AText, Length(ASubText)) = ASubText);
  838. end;
  839. function StartsStr(const ASubText, AText: string): Boolean;
  840. begin
  841. if (Length(AText) >= Length(ASubText)) and (ASubText <> '') then
  842. Result := StrLComp(PChar(ASubText), PChar(AText), Length(ASubText)) = 0
  843. else
  844. Result := False;
  845. end;
  846. function EndsStr(const ASubText, AText: string): Boolean;
  847. begin
  848. if Length(AText) >= Length(ASubText) then
  849. Result := StrLComp(PChar(ASubText),
  850. PChar(AText) + Length(AText) - Length(ASubText), Length(ASubText)) = 0
  851. else
  852. Result := False;
  853. end;
  854. function AnsiReplaceStr(const AText, AFromText, AToText: string): string;
  855. begin
  856. Result := StringReplace(AText,AFromText,AToText,[rfReplaceAll]);
  857. end;
  858. function AnsiMatchStr(const AText: string; const AValues: array of string): Boolean;
  859. begin
  860. Result:=AnsiIndexStr(AText,Avalues)<>-1;
  861. end;
  862. function AnsiIndexStr(const AText: string; const AValues: array of string): Integer;
  863. var
  864. i : longint;
  865. begin
  866. result:=-1;
  867. if (high(AValues)=-1) or (High(AValues)>MaxInt) Then
  868. Exit;
  869. for i:=low(AValues) to High(Avalues) do
  870. if (avalues[i]=AText) Then
  871. exit(i); // make sure it is the first val.
  872. end;
  873. function MatchStr(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  874. begin
  875. Result := IndexStr(AText,AValues) <> -1;
  876. end;
  877. function MatchText(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  878. begin
  879. Result := IndexText(AText,AValues) <> -1;
  880. end;
  881. function IndexStr(const AText: UnicodeString; const AValues: array of UnicodeString): Integer;
  882. var
  883. i: longint;
  884. begin
  885. Result := -1;
  886. if (high(AValues) = -1) or (High(AValues) > MaxInt) Then
  887. Exit;
  888. for i := low(AValues) to High(Avalues) do
  889. if (avalues[i] = AText) Then
  890. exit(i); // make sure it is the first val.
  891. end;
  892. function IndexText(const AText: UnicodeString; const AValues: array of UnicodeString): Integer;
  893. var
  894. i : Integer;
  895. begin
  896. Result:=-1;
  897. if (high(AValues)=-1) or (High(AValues)>MaxInt) Then
  898. Exit;
  899. for i:=low(AValues) to High(Avalues) do
  900. if UnicodeCompareText(avalues[i],atext)=0 Then
  901. exit(i); // make sure it is the first val.
  902. end;
  903. operator in(const AText: string; const AValues: array of string): Boolean;
  904. begin
  905. Result := AnsiIndexStr(AText,AValues) <>-1;
  906. end;
  907. operator in(const AText: UnicodeString; const AValues: array of UnicodeString): Boolean;
  908. begin
  909. Result := IndexStr(AText,AValues) <> -1;
  910. end;
  911. { ---------------------------------------------------------------------
  912. Playthingies
  913. ---------------------------------------------------------------------}
  914. function DupeString(const AText: string; ACount: Integer): string;
  915. var
  916. Len: SizeInt;
  917. Source, Target: PByte;
  918. begin
  919. Len := Length(AText);
  920. SetLength(Result, ACount * Len);
  921. // Use PByte to skip implicit UniqueString, because SetLength always unique
  922. Target := PByte(Result);
  923. if Target = nil then // ACount = 0 or AText = ''
  924. Exit;
  925. // Now ACount > 0 and Len > 0
  926. Source := PByte(AText);
  927. repeat
  928. Move(Source[0], Target[0], Len * SizeOf(Char));
  929. Inc(Target, Len * SizeOf(Char));
  930. Dec(ACount);
  931. until ACount = 0;
  932. end;
  933. function ReverseString(const AText: string): string;
  934. var
  935. i,j : SizeInt;
  936. begin
  937. setlength(result,length(atext));
  938. i:=1; j:=length(atext);
  939. while (i<=j) do
  940. begin
  941. result[i]:=atext[j-i+1];
  942. inc(i);
  943. end;
  944. end;
  945. function AnsiReverseString(const AText: AnsiString): AnsiString;
  946. begin
  947. Result:=ReverseString(AText);
  948. end;
  949. function StuffString(const AText: string; AStart, ALength: Cardinal; const ASubText: string): string;
  950. var i,j,k : SizeUInt;
  951. begin
  952. j:=length(ASubText);
  953. i:=length(AText);
  954. if AStart>i then
  955. aStart:=i+1;
  956. k:=i+1-AStart;
  957. if ALength> k then
  958. ALength:=k;
  959. SetLength(Result,i+j-ALength);
  960. move (AText[1],result[1],AStart-1);
  961. move (ASubText[1],result[AStart],j);
  962. move (AText[AStart+ALength], Result[AStart+j],i+1-AStart-ALength);
  963. end;
  964. function RandomFrom(const AValues: array of string): string;
  965. begin
  966. if high(AValues)=-1 then exit('');
  967. result:=Avalues[random(High(AValues)+1)];
  968. end;
  969. function IfThen(AValue: Boolean; const ATrue: string; const AFalse: string): string;
  970. begin
  971. if avalue then
  972. result:=atrue
  973. else
  974. result:=afalse;
  975. end;
  976. function NaturalCompareText(const Str1, Str2: string; const ADecSeparator, AThousandSeparator: Char): Integer;
  977. {
  978. NaturalCompareBase compares strings in a collated order and
  979. so numbers are sorted too. It sorts like this:
  980. 01
  981. 001
  982. 0001
  983. and
  984. 0
  985. 00
  986. 000
  987. 000_A
  988. 000_B
  989. in a intuitive order.
  990. }
  991. var
  992. Num1, Num2: double;
  993. pStr1, pStr2: PChar;
  994. Len1, Len2: SizeInt;
  995. TextLen1, TextLen2: SizeInt;
  996. TextStr1: string = '';
  997. TextStr2: string = '';
  998. i: SizeInt;
  999. j: SizeInt;
  1000. function Sign(const AValue: sizeint): integer;inline;
  1001. begin
  1002. If Avalue<0 then
  1003. Result:=-1
  1004. else If Avalue>0 then
  1005. Result:=1
  1006. else
  1007. Result:=0;
  1008. end;
  1009. function IsNumber(ch: char): boolean;
  1010. begin
  1011. Result := ch in ['0'..'9'];
  1012. end;
  1013. function GetInteger(var pch: PChar; var Len: sizeint): double;
  1014. begin
  1015. Result := 0;
  1016. while (pch^ <> #0) and IsNumber(pch^) do
  1017. begin
  1018. Result := Result * 10 + Ord(pch^) - Ord('0');
  1019. Inc(Len);
  1020. Inc(pch);
  1021. end;
  1022. end;
  1023. procedure GetChars;
  1024. begin
  1025. TextLen1 := 0;
  1026. while not ((pStr1 + TextLen1)^ in ['0'..'9']) and ((pStr1 + TextLen1)^ <> #0) do
  1027. Inc(TextLen1);
  1028. SetLength(TextStr1, TextLen1);
  1029. i := 1;
  1030. j := 0;
  1031. while i <= TextLen1 do
  1032. begin
  1033. TextStr1[i] := (pStr1 + j)^;
  1034. Inc(i);
  1035. Inc(j);
  1036. end;
  1037. TextLen2 := 0;
  1038. while not ((pStr2 + TextLen2)^ in ['0'..'9']) and ((pStr2 + TextLen2)^ <> #0) do
  1039. Inc(TextLen2);
  1040. SetLength(TextStr2, TextLen2);
  1041. i := 1;
  1042. j := 0;
  1043. while i <= TextLen2 do
  1044. begin
  1045. TextStr2[i] := (pStr2 + j)^;
  1046. Inc(i);
  1047. Inc(j);
  1048. end;
  1049. end;
  1050. begin
  1051. if (Str1 <> '') and (Str2 <> '') then
  1052. begin
  1053. pStr1 := PChar(Str1);
  1054. pStr2 := PChar(Str2);
  1055. Result := 0;
  1056. while not ((pStr1^ = #0) or (pStr2^ = #0)) do
  1057. begin
  1058. TextLen1 := 1;
  1059. TextLen2 := 1;
  1060. Len1 := 0;
  1061. Len2 := 0;
  1062. while (pStr1^ = ' ') do
  1063. begin
  1064. Inc(pStr1);
  1065. Inc(Len1);
  1066. end;
  1067. while (pStr2^ = ' ') do
  1068. begin
  1069. Inc(pStr2);
  1070. Inc(Len2);
  1071. end;
  1072. if IsNumber(pStr1^) and IsNumber(pStr2^) then
  1073. begin
  1074. Num1 := GetInteger(pStr1, Len1);
  1075. Num2 := GetInteger(pStr2, Len2);
  1076. if Num1 < Num2 then
  1077. Result := -1
  1078. else if Num1 > Num2 then
  1079. Result := 1
  1080. else
  1081. begin
  1082. Result := Sign(Len1 - Len2);
  1083. end;
  1084. Dec(pStr1);
  1085. Dec(pStr2);
  1086. end
  1087. else
  1088. begin
  1089. GetChars;
  1090. if TextStr1 <> TextStr2 then
  1091. Result := WideCompareText(UTF8Decode(TextStr1), UTF8Decode(TextStr2))
  1092. else
  1093. Result := 0;
  1094. end;
  1095. if Result <> 0 then
  1096. Break;
  1097. Inc(pStr1, TextLen1);
  1098. Inc(pStr2, TextLen2);
  1099. end;
  1100. end;
  1101. Num1 := Length(Str1);
  1102. Num2 := Length(Str2);
  1103. if (Result = 0) and (Num1 <> Num2) then
  1104. begin
  1105. if Num1 < Num2 then
  1106. Result := -1
  1107. else
  1108. Result := 1;
  1109. end;
  1110. end;
  1111. function NaturalCompareText (const S1 , S2 : string ): Integer ;
  1112. begin
  1113. Result := NaturalCompareText(S1, S2,
  1114. DefaultFormatSettings.DecimalSeparator,
  1115. DefaultFormatSettings.ThousandSeparator);
  1116. end;
  1117. { ---------------------------------------------------------------------
  1118. VB emulations.
  1119. ---------------------------------------------------------------------}
  1120. function LeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1121. begin
  1122. Result:=Copy(AText,1,ACount);
  1123. end;
  1124. function RightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1125. var j,l:SizeInt;
  1126. begin
  1127. l:=length(atext);
  1128. j:=ACount;
  1129. if j>l then j:=l;
  1130. Result:=Copy(AText,l-j+1,j);
  1131. end;
  1132. function MidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;
  1133. begin
  1134. if (ACount=0) or (AStart>length(atext)) then
  1135. exit('');
  1136. Result:=Copy(AText,AStart,ACount);
  1137. end;
  1138. function LeftBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;
  1139. begin
  1140. Result:=LeftStr(AText,AByteCount);
  1141. end;
  1142. function RightBStr(const AText: AnsiString; const AByteCount: SizeInt): AnsiString;
  1143. begin
  1144. Result:=RightStr(Atext,AByteCount);
  1145. end;
  1146. function MidBStr(const AText: AnsiString; const AByteStart, AByteCount: SizeInt): AnsiString;
  1147. begin
  1148. Result:=MidStr(AText,AByteStart,AByteCount);
  1149. end;
  1150. function AnsiLeftStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1151. begin
  1152. Result := copy(AText,1,ACount);
  1153. end;
  1154. function AnsiRightStr(const AText: AnsiString; const ACount: SizeInt): AnsiString;
  1155. begin
  1156. Result := copy(AText,length(AText)-ACount+1,ACount);
  1157. end;
  1158. function AnsiMidStr(const AText: AnsiString; const AStart, ACount: SizeInt): AnsiString;
  1159. begin
  1160. Result:=Copy(AText,AStart,ACount);
  1161. end;
  1162. function LeftStr(const AText: WideString; const ACount: SizeInt): WideString;
  1163. begin
  1164. Result:=Copy(AText,1,ACount);
  1165. end;
  1166. function RightStr(const AText: WideString; const ACount: SizeInt): WideString;
  1167. var
  1168. j,l:SizeInt;
  1169. begin
  1170. l:=length(atext);
  1171. j:=ACount;
  1172. if j>l then j:=l;
  1173. Result:=Copy(AText,l-j+1,j);
  1174. end;
  1175. function MidStr(const AText: WideString; const AStart, ACount: SizeInt): WideString;
  1176. begin
  1177. Result:=Copy(AText,AStart,ACount);
  1178. end;
  1179. { ---------------------------------------------------------------------
  1180. Extended search and replace
  1181. ---------------------------------------------------------------------}
  1182. type
  1183. TEqualFunction = function (const a,b : char) : boolean;
  1184. function EqualWithCase (const a,b : char) : boolean;
  1185. begin
  1186. result := (a = b);
  1187. end;
  1188. function EqualWithoutCase (const a,b : char) : boolean;
  1189. begin
  1190. result := (lowerCase(a) = lowerCase(b));
  1191. end;
  1192. function IsWholeWord (bufstart, bufend, wordstart, wordend : pchar) : boolean;
  1193. begin
  1194. // Check start
  1195. result := ((wordstart = bufstart) or ((wordstart-1)^ in worddelimiters)) and
  1196. // Check end
  1197. ((wordend = bufend) or ((wordend+1)^ in worddelimiters));
  1198. end;
  1199. function SearchDown(buf,aStart,endchar:pchar; SearchString:string;
  1200. Equals : TEqualFunction; WholeWords:boolean) : pchar;
  1201. var Found : boolean;
  1202. s, c : pchar;
  1203. begin
  1204. result := aStart;
  1205. Found := false;
  1206. while not Found and (result <= endchar) do
  1207. begin
  1208. // Search first letter
  1209. while (result <= endchar) and not Equals(result^,SearchString[1]) do
  1210. inc (result);
  1211. // Check if following is searchstring
  1212. c := result;
  1213. s := @(Searchstring[1]);
  1214. Found := true;
  1215. while (c <= endchar) and (s^ <> #0) and Found do
  1216. begin
  1217. Found := Equals(c^, s^);
  1218. inc (c);
  1219. inc (s);
  1220. end;
  1221. if s^ <> #0 then
  1222. Found := false;
  1223. // Check if it is a word
  1224. if Found and WholeWords then
  1225. Found := IsWholeWord(buf,endchar,result,c-1);
  1226. if not found then
  1227. inc (result);
  1228. end;
  1229. if not Found then
  1230. result := nil;
  1231. end;
  1232. function SearchUp(buf,aStart,endchar:pchar; SearchString:string;
  1233. equals : TEqualFunction; WholeWords:boolean) : pchar;
  1234. var Found : boolean;
  1235. s, c, l : pchar;
  1236. begin
  1237. result := aStart;
  1238. Found := false;
  1239. l := @(SearchString[length(SearchString)]);
  1240. while not Found and (result >= buf) do
  1241. begin
  1242. // Search last letter
  1243. while (result >= buf) and not Equals(result^,l^) do
  1244. dec (result);
  1245. // Check if before is searchstring
  1246. c := result;
  1247. s := l;
  1248. Found := true;
  1249. while (c >= buf) and (s >= @SearchString[1]) and Found do
  1250. begin
  1251. Found := Equals(c^, s^);
  1252. dec (c);
  1253. dec (s);
  1254. end;
  1255. if (s >= @(SearchString[1])) then
  1256. Found := false;
  1257. // Check if it is a word
  1258. if Found and WholeWords then
  1259. Found := IsWholeWord(buf,endchar,c+1,result);
  1260. if found then
  1261. result := c+1
  1262. else
  1263. dec (result);
  1264. end;
  1265. if not Found then
  1266. result := nil;
  1267. end;
  1268. //function SearchDown(buf,aStart,endchar:pchar; SearchString:string; equal : TEqualFunction; WholeWords:boolean) : pchar;
  1269. function SearchBuf(Buf: PChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String; Options: TStringSearchOptions
  1270. ): PChar;
  1271. var
  1272. equal : TEqualFunction;
  1273. begin
  1274. SelStart := SelStart + SelLength;
  1275. if (SearchString = '') or (SelStart > BufLen) or (SelStart < 0) then
  1276. result := nil
  1277. else
  1278. begin
  1279. if soMatchCase in Options then
  1280. Equal := @EqualWithCase
  1281. else
  1282. Equal := @EqualWithoutCase;
  1283. if soDown in Options then
  1284. result := SearchDown(buf,buf+SelStart,Buf+(BufLen-1), SearchString, Equal, (soWholeWord in Options))
  1285. else
  1286. result := SearchUp(buf,buf+SelStart,Buf+(Buflen-1), SearchString, Equal, (soWholeWord in Options));
  1287. end;
  1288. end;
  1289. function SearchBuf(Buf: PChar; BufLen: SizeInt; SelStart, SelLength: SizeInt; SearchString: String): PChar; // ; Options: TStringSearchOptions = [soDown]
  1290. begin
  1291. Result:=SearchBuf(Buf,BufLen,SelStart,SelLength,SearchString,[soDown]);
  1292. end;
  1293. function PosEx(const SubStr, S: string; Offset: SizeUint): SizeInt;
  1294. var
  1295. i,MaxLen, SubLen : SizeInt;
  1296. SubFirst: Char;
  1297. pc : pchar;
  1298. begin
  1299. PosEx:=0;
  1300. SubLen := Length(SubStr);
  1301. if (SubLen > 0) and (Offset > 0) and (Offset <= Cardinal(Length(S))) then
  1302. begin
  1303. MaxLen := Length(S)- SubLen;
  1304. SubFirst := SubStr[1];
  1305. i := indexbyte(S[Offset],Length(S) - Offset + 1, Byte(SubFirst));
  1306. while (i >= 0) and ((i + sizeint(Offset) - 1) <= MaxLen) do
  1307. begin
  1308. pc := @S[i+SizeInt(Offset)];
  1309. //we know now that pc^ = SubFirst, because indexbyte returned a value > -1
  1310. if (CompareByte(Substr[1],pc^,SubLen) = 0) then
  1311. begin
  1312. PosEx := i + SizeInt(Offset);
  1313. Exit;
  1314. end;
  1315. //point Offset to next char in S
  1316. Offset := sizeuint(i) + Offset + 1;
  1317. i := indexbyte(S[Offset],Length(S) - Offset + 1, Byte(SubFirst));
  1318. end;
  1319. end;
  1320. end;
  1321. function PosEx(c: char; const S: string; Offset: SizeUint): SizeInt;
  1322. var
  1323. p,Len : SizeInt;
  1324. begin
  1325. Len := length(S);
  1326. if (Offset < 1) or (Offset > SizeUInt(Length(S))) then exit(0);
  1327. Len := length(S);
  1328. p := indexbyte(S[Offset],Len-offset+1,Byte(c));
  1329. if (p < 0) then
  1330. PosEx := 0
  1331. else
  1332. PosEx := p + sizeint(Offset);
  1333. end;
  1334. function PosEx(const SubStr, S: string): SizeInt; // Offset: Cardinal = 1
  1335. begin
  1336. posex:=posex(substr,s,1);
  1337. end;
  1338. function PosEx(const SubStr, S: UnicodeString; Offset: SizeUint): SizeInt;
  1339. var
  1340. i,MaxLen, SubLen : SizeInt;
  1341. SubFirst: WideChar;
  1342. pc : pwidechar;
  1343. begin
  1344. PosEx:=0;
  1345. SubLen := Length(SubStr);
  1346. if (SubLen > 0) and (Offset > 0) and (Offset <= Cardinal(Length(S))) then
  1347. begin
  1348. MaxLen := Length(S)- SubLen;
  1349. SubFirst := SubStr[1];
  1350. i := indexword(S[Offset],Length(S) - Offset + 1, Word(SubFirst));
  1351. while (i >= 0) and ((i + sizeint(Offset) - 1) <= MaxLen) do
  1352. begin
  1353. pc := @S[i+SizeInt(Offset)];
  1354. //we know now that pc^ = SubFirst, because indexbyte returned a value > -1
  1355. if (CompareWord(Substr[1],pc^,SubLen) = 0) then
  1356. begin
  1357. PosEx := i + SizeInt(Offset);
  1358. Exit;
  1359. end;
  1360. //point Offset to next char in S
  1361. Offset := sizeuint(i) + Offset + 1;
  1362. i := indexword(S[Offset],Length(S) - Offset + 1, Word(SubFirst));
  1363. end;
  1364. end;
  1365. end;
  1366. function PosEx(c: WideChar; const S: UnicodeString; Offset: SizeUint): SizeInt;
  1367. var
  1368. Len,p : SizeInt;
  1369. begin
  1370. Len := length(S);
  1371. if (Offset < 1) or (Offset > SizeUInt(Length(S))) then exit(0);
  1372. Len := length(S);
  1373. p := indexword(S[Offset],Len-offset+1,Word(c));
  1374. if (p < 0) then
  1375. PosEx := 0
  1376. else
  1377. PosEx := p + sizeint(Offset);
  1378. end;
  1379. function PosEx(const SubStr, S: UnicodeString): Sizeint; // Offset: Cardinal = 1
  1380. begin
  1381. PosEx:=PosEx(SubStr,S,1);
  1382. end;
  1383. function StringsReplace(const S: string; OldPattern, NewPattern: array of string; Flags: TReplaceFlags): string;
  1384. var pc,pcc,lastpc : pchar;
  1385. strcount : integer;
  1386. ResStr,
  1387. CompStr : string;
  1388. Found : Boolean;
  1389. sc : sizeint;
  1390. begin
  1391. sc := length(OldPattern);
  1392. if sc <> length(NewPattern) then
  1393. raise exception.Create(SErrAmountStrings);
  1394. dec(sc);
  1395. if rfIgnoreCase in Flags then
  1396. begin
  1397. CompStr:=AnsiUpperCase(S);
  1398. for strcount := 0 to sc do
  1399. OldPattern[strcount] := AnsiUpperCase(OldPattern[strcount]);
  1400. end
  1401. else
  1402. CompStr := s;
  1403. ResStr := '';
  1404. pc := @CompStr[1];
  1405. pcc := @s[1];
  1406. lastpc := pc+Length(S);
  1407. while pc < lastpc do
  1408. begin
  1409. Found := False;
  1410. for strcount := 0 to sc do
  1411. begin
  1412. if (length(OldPattern[strcount])>0) and
  1413. (OldPattern[strcount][1]=pc^) and
  1414. (Length(OldPattern[strcount]) <= (lastpc-pc)) and
  1415. (CompareByte(OldPattern[strcount][1],pc^,Length(OldPattern[strcount]))=0) then
  1416. begin
  1417. ResStr := ResStr + NewPattern[strcount];
  1418. pc := pc+Length(OldPattern[strcount]);
  1419. pcc := pcc+Length(OldPattern[strcount]);
  1420. Found := true;
  1421. end
  1422. end;
  1423. if not found then
  1424. begin
  1425. ResStr := ResStr + pcc^;
  1426. inc(pc);
  1427. inc(pcc);
  1428. end
  1429. else if not (rfReplaceAll in Flags) then
  1430. begin
  1431. ResStr := ResStr + StrPas(pcc);
  1432. break;
  1433. end;
  1434. end;
  1435. Result := ResStr;
  1436. end;
  1437. { ---------------------------------------------------------------------
  1438. Delphi compat
  1439. ---------------------------------------------------------------------}
  1440. function ReplaceStr(const AText, AFromText, AToText: string): string;
  1441. begin
  1442. result:=AnsiReplaceStr(AText, AFromText, AToText);
  1443. end;
  1444. function ReplaceText(const AText, AFromText, AToText: string): string;
  1445. begin
  1446. result:=AnsiReplaceText(AText, AFromText, AToText);
  1447. end;
  1448. { ---------------------------------------------------------------------
  1449. Soundex Functions.
  1450. ---------------------------------------------------------------------}
  1451. Const
  1452. SScore : array[1..255] of Char =
  1453. ('0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 1..32
  1454. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 33..64
  1455. '0','1','2','3','0','1','2','i','0','2','2','4','5','5','0','1','2','6','2','3','0','1','i','2','i','2', // 65..90
  1456. '0','0','0','0','0','0', // 91..96
  1457. '0','1','2','3','0','1','2','i','0','2','2','4','5','5','0','1','2','6','2','3','0','1','i','2','i','2', // 97..122
  1458. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 123..154
  1459. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 155..186
  1460. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 187..218
  1461. '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0', // 219..250
  1462. '0','0','0','0','0'); // 251..255
  1463. function Soundex(const AText: string; ALength: TSoundexLength): string;
  1464. Var
  1465. S,PS : Char;
  1466. I,L : SizeInt;
  1467. begin
  1468. Result:='';
  1469. PS:=#0;
  1470. If Length(AText)>0 then
  1471. begin
  1472. Result:=Upcase(AText[1]);
  1473. I:=2;
  1474. L:=Length(AText);
  1475. While (I<=L) and (Length(Result)<ALength) do
  1476. begin
  1477. S:=SScore[Ord(AText[i])];
  1478. If Not (S in ['0','i',PS]) then
  1479. Result:=Result+S;
  1480. If (S<>'i') then
  1481. PS:=S;
  1482. Inc(I);
  1483. end;
  1484. end;
  1485. L:=Length(Result);
  1486. If (L<ALength) then
  1487. Result:=Result+StringOfChar('0',Alength-L);
  1488. end;
  1489. function Soundex(const AText: string): string; // ; ALength: TSoundexLength = 4
  1490. begin
  1491. Result:=Soundex(AText,4);
  1492. end;
  1493. Const
  1494. Ord0 = Ord('0');
  1495. OrdA = Ord('A');
  1496. function SoundexInt(const AText: string; ALength: TSoundexIntLength): Integer;
  1497. var
  1498. SE: string;
  1499. I: SizeInt;
  1500. begin
  1501. Result:=-1;
  1502. SE:=Soundex(AText,ALength);
  1503. If Length(SE)>0 then
  1504. begin
  1505. Result:=Ord(SE[1])-OrdA;
  1506. if ALength > 1 then
  1507. begin
  1508. Result:=Result*26+(Ord(SE[2])-Ord0);
  1509. for I:=3 to ALength do
  1510. Result:=(Ord(SE[I])-Ord0)+Result*7;
  1511. end;
  1512. Result:=ALength+Result*9;
  1513. end;
  1514. end;
  1515. function SoundexInt(const AText: string): Integer; //; ALength: TSoundexIntLength = 4
  1516. begin
  1517. Result:=SoundexInt(AText,4);
  1518. end;
  1519. function DecodeSoundexInt(AValue: Integer): string;
  1520. var
  1521. I, Len: Integer;
  1522. begin
  1523. Result := '';
  1524. Len := AValue mod 9;
  1525. AValue := AValue div 9;
  1526. for I:=Len downto 3 do
  1527. begin
  1528. Result:=Chr(Ord0+(AValue mod 7))+Result;
  1529. AValue:=AValue div 7;
  1530. end;
  1531. if Len>1 then
  1532. begin
  1533. Result:=Chr(Ord0+(AValue mod 26))+Result;
  1534. AValue:=AValue div 26;
  1535. end;
  1536. Result:=Chr(OrdA+AValue)+Result;
  1537. end;
  1538. function SoundexWord(const AText: string): Word;
  1539. Var
  1540. S : String;
  1541. begin
  1542. S:=SoundEx(Atext,4);
  1543. Result:=Ord(S[1])-OrdA;
  1544. Result:=Result*26+ord(S[2])-48;
  1545. Result:=Result*7+ord(S[3])-48;
  1546. Result:=Result*7+ord(S[4])-48;
  1547. end;
  1548. function DecodeSoundexWord(AValue: Word): string;
  1549. begin
  1550. Result := Chr(Ord0+ (AValue mod 7));
  1551. AValue := AValue div 7;
  1552. Result := Chr(Ord0+ (AValue mod 7)) + Result;
  1553. AValue := AValue div 7;
  1554. Result := IntToStr(AValue mod 26) + Result;
  1555. AValue := AValue div 26;
  1556. Result := Chr(OrdA+AValue) + Result;
  1557. end;
  1558. function SoundexSimilar(const AText, AOther: string; ALength: TSoundexLength): Boolean;
  1559. begin
  1560. Result:=Soundex(AText,ALength)=Soundex(AOther,ALength);
  1561. end;
  1562. function SoundexSimilar(const AText, AOther: string): Boolean; //; ALength: TSoundexLength = 4
  1563. begin
  1564. Result:=SoundexSimilar(AText,AOther,4);
  1565. end;
  1566. function SoundexCompare(const AText, AOther: string; ALength: TSoundexLength): Integer;
  1567. begin
  1568. Result:=AnsiCompareStr(Soundex(AText,ALength),Soundex(AOther,ALength));
  1569. end;
  1570. function SoundexCompare(const AText, AOther: string): Integer; //; ALength: TSoundexLength = 4
  1571. begin
  1572. Result:=SoundexCompare(AText,AOther,4);
  1573. end;
  1574. function SoundexProc(const AText, AOther: string): Boolean;
  1575. begin
  1576. Result:=SoundexSimilar(AText,AOther);
  1577. end;
  1578. { ---------------------------------------------------------------------
  1579. RxStrUtils-like functions.
  1580. ---------------------------------------------------------------------}
  1581. function IsEmptyStr(const S: string; const EmptyChars: TSysCharSet): Boolean;
  1582. var
  1583. i,l: SizeInt;
  1584. begin
  1585. l:=Length(S);
  1586. i:=1;
  1587. Result:=True;
  1588. while Result and (i<=l) do
  1589. begin
  1590. Result:=(S[i] in EmptyChars);
  1591. Inc(i);
  1592. end;
  1593. end;
  1594. function DelSpace(const S: string): string;
  1595. begin
  1596. Result:=DelChars(S,' ');
  1597. end;
  1598. function DelChars(const S: string; Chr: Char): string;
  1599. var
  1600. I,J: SizeInt;
  1601. begin
  1602. Result:=S;
  1603. I:=Length(Result);
  1604. While I>0 do
  1605. begin
  1606. if Result[I]=Chr then
  1607. begin
  1608. J:=I-1;
  1609. While (J>0) and (Result[J]=Chr) do
  1610. Dec(j);
  1611. Delete(Result,J+1,I-J);
  1612. I:=J+1;
  1613. end;
  1614. dec(I);
  1615. end;
  1616. end;
  1617. function DelSpace1(const S: string): string;
  1618. var
  1619. I : SizeInt;
  1620. begin
  1621. Result:=S;
  1622. for i:=Length(Result) downto 2 do
  1623. if (Result[i]=' ') and (Result[I-1]=' ') then
  1624. Delete(Result,I,1);
  1625. end;
  1626. function Tab2Space(const S: string; Numb: Byte): string;
  1627. var
  1628. I: SizeInt;
  1629. begin
  1630. I:=1;
  1631. Result:=S;
  1632. while I <= Length(Result) do
  1633. if Result[I]<>Chr(9) then
  1634. inc(I)
  1635. else
  1636. begin
  1637. Result[I]:=' ';
  1638. If (Numb>1) then
  1639. Insert(StringOfChar(' ',Numb-1),Result,I);
  1640. Inc(I,Numb);
  1641. end;
  1642. end;
  1643. function NPos(const C: string; S: string; N: Integer): SizeInt;
  1644. var
  1645. i,p,k: SizeInt;
  1646. begin
  1647. Result:=0;
  1648. if N<1 then
  1649. Exit;
  1650. k:=0;
  1651. i:=1;
  1652. Repeat
  1653. p:=pos(C,S);
  1654. Inc(k,p);
  1655. if p>0 then
  1656. delete(S,1,p);
  1657. Inc(i);
  1658. Until (i>n) or (p=0);
  1659. If (P>0) then
  1660. Result:=K;
  1661. end;
  1662. function AddChar(C: Char; const S: string; N: Integer): string;
  1663. Var
  1664. l : SizeInt;
  1665. begin
  1666. Result:=S;
  1667. l:=Length(Result);
  1668. if l<N then
  1669. Result:=StringOfChar(C,N-l)+Result;
  1670. end;
  1671. function AddCharR(C: Char; const S: string; N: Integer): string;
  1672. Var
  1673. l : SizeInt;
  1674. begin
  1675. Result:=S;
  1676. l:=Length(Result);
  1677. if l<N then
  1678. Result:=Result+StringOfChar(C,N-l);
  1679. end;
  1680. function PadRight(const S: string; N: Integer): string;inline;
  1681. begin
  1682. Result:=AddCharR(' ',S,N);
  1683. end;
  1684. function PadLeft(const S: string; N: Integer): string;inline;
  1685. begin
  1686. Result:=AddChar(' ',S,N);
  1687. end;
  1688. function Copy2Symb(const S: string; Symb: Char): string;
  1689. var
  1690. p: SizeInt;
  1691. begin
  1692. p:=Pos(Symb,S);
  1693. if p=0 then
  1694. p:=Length(S)+1;
  1695. Result:=Copy(S,1,p-1);
  1696. end;
  1697. function Copy2SymbDel(var S: string; Symb: Char): string;
  1698. var
  1699. p: SizeInt;
  1700. begin
  1701. p:=Pos(Symb,S);
  1702. if p=0 then
  1703. begin
  1704. result:=s;
  1705. s:='';
  1706. end
  1707. else
  1708. begin
  1709. Result:=Copy(S,1,p-1);
  1710. delete(s,1,p);
  1711. end;
  1712. end;
  1713. function Copy2Space(const S: string): string;inline;
  1714. begin
  1715. Result:=Copy2Symb(S,' ');
  1716. end;
  1717. function Copy2SpaceDel(var S: string): string;inline;
  1718. begin
  1719. Result:=Copy2SymbDel(S,' ');
  1720. end;
  1721. function AnsiProperCase(const S: string; const WordDelims: TSysCharSet): string;
  1722. var
  1723. P,PE : PChar;
  1724. begin
  1725. Result:=AnsiLowerCase(S);
  1726. P:=PChar(pointer(Result));
  1727. PE:=P+Length(Result);
  1728. while (P<PE) do
  1729. begin
  1730. while (P<PE) and (P^ in WordDelims) do
  1731. inc(P);
  1732. if (P<PE) then
  1733. P^:=UpCase(P^);
  1734. while (P<PE) and not (P^ in WordDelims) do
  1735. inc(P);
  1736. end;
  1737. end;
  1738. function WordCount(const S: string; const WordDelims: TSysCharSet): SizeInt;
  1739. var
  1740. P,PE : PChar;
  1741. begin
  1742. Result:=0;
  1743. P:=Pchar(pointer(S));
  1744. PE:=P+Length(S);
  1745. while (P<PE) do
  1746. begin
  1747. while (P<PE) and (P^ in WordDelims) do
  1748. Inc(P);
  1749. if (P<PE) then
  1750. inc(Result);
  1751. while (P<PE) and not (P^ in WordDelims) do
  1752. inc(P);
  1753. end;
  1754. end;
  1755. function WordPosition(const N: Integer; const S: string; const WordDelims: TSysCharSet): SizeInt;
  1756. var
  1757. PS,P,PE : PChar;
  1758. Count: Integer;
  1759. begin
  1760. Result:=0;
  1761. Count:=0;
  1762. PS:=PChar(pointer(S));
  1763. PE:=PS+Length(S);
  1764. P:=PS;
  1765. while (P<PE) and (Count<>N) do
  1766. begin
  1767. while (P<PE) and (P^ in WordDelims) do
  1768. inc(P);
  1769. if (P<PE) then
  1770. inc(Count);
  1771. if (Count<>N) then
  1772. while (P<PE) and not (P^ in WordDelims) do
  1773. inc(P)
  1774. else
  1775. Result:=(P-PS)+1;
  1776. end;
  1777. end;
  1778. function ExtractWord(N: Integer; const S: string; const WordDelims: TSysCharSet): string;inline;
  1779. var
  1780. i: SizeInt;
  1781. begin
  1782. Result:=ExtractWordPos(N,S,WordDelims,i);
  1783. end;
  1784. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; out Pos: Integer): string;
  1785. var
  1786. i,j,l: SizeInt;
  1787. begin
  1788. j:=0;
  1789. i:=WordPosition(N, S, WordDelims);
  1790. if (I>High(Integer)) then
  1791. begin
  1792. Result:='';
  1793. Pos:=-1;
  1794. Exit;
  1795. end;
  1796. Pos:=i;
  1797. if (i<>0) then
  1798. begin
  1799. j:=i;
  1800. l:=Length(S);
  1801. while (j<=L) and not (S[j] in WordDelims) do
  1802. inc(j);
  1803. end;
  1804. SetLength(Result,j-i);
  1805. If ((j-i)>0) then
  1806. Move(S[i],Result[1],j-i);
  1807. end;
  1808. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  1809. function ExtractWordPos(N: Integer; const S: string; const WordDelims: TSysCharSet; Out Pos: SizeInt): string;
  1810. var
  1811. i,j,l: SizeInt;
  1812. begin
  1813. j:=0;
  1814. i:=WordPosition(N, S, WordDelims);
  1815. Pos:=i;
  1816. if (i<>0) then
  1817. begin
  1818. j:=i;
  1819. l:=Length(S);
  1820. while (j<=L) and not (S[j] in WordDelims) do
  1821. inc(j);
  1822. end;
  1823. SetLength(Result,j-i);
  1824. If ((j-i)>0) then
  1825. Move(S[i],Result[1],j-i);
  1826. end;
  1827. {$ENDIF}
  1828. function ExtractDelimited(N: Integer; const S: string; const Delims: TSysCharSet): string;
  1829. var
  1830. w,i,l,len: SizeInt;
  1831. begin
  1832. w:=0;
  1833. i:=1;
  1834. l:=0;
  1835. len:=Length(S);
  1836. SetLength(Result, 0);
  1837. while (i<=len) and (w<>N) do
  1838. begin
  1839. if s[i] in Delims then
  1840. inc(w)
  1841. else
  1842. begin
  1843. if (N-1)=w then
  1844. begin
  1845. inc(l);
  1846. SetLength(Result,l);
  1847. Result[L]:=S[i];
  1848. end;
  1849. end;
  1850. inc(i);
  1851. end;
  1852. end;
  1853. {$IF SIZEOF(SIZEINT)<>SIZEOF(INTEGER)}
  1854. function ExtractSubstr(const S: string; var Pos: SizeInt; const Delims: TSysCharSet): string;
  1855. var
  1856. i,l: SizeInt;
  1857. begin
  1858. i:=Pos;
  1859. l:=Length(S);
  1860. while (i<=l) and not (S[i] in Delims) do
  1861. inc(i);
  1862. Result:=Copy(S,Pos,i-Pos);
  1863. while (i<=l) and (S[i] in Delims) do
  1864. inc(i);
  1865. Pos:=i;
  1866. end;
  1867. {$ENDIF}
  1868. function ExtractSubstr(const S: string; var Pos: Integer; const Delims: TSysCharSet): string;
  1869. var
  1870. i,l: SizeInt;
  1871. begin
  1872. i:=Pos;
  1873. l:=Length(S);
  1874. while (i<=l) and not (S[i] in Delims) do
  1875. inc(i);
  1876. Result:=Copy(S,Pos,i-Pos);
  1877. while (i<=l) and (S[i] in Delims) do
  1878. inc(i);
  1879. if I>MaxInt then
  1880. Pos:=MaxInt
  1881. else
  1882. Pos:=i;
  1883. end;
  1884. function IsWordPresent(const W, S: string; const WordDelims: TSysCharSet): Boolean;
  1885. var
  1886. i,Count : SizeInt;
  1887. begin
  1888. Result:=False;
  1889. Count:=WordCount(S, WordDelims);
  1890. I:=1;
  1891. While (Not Result) and (I<=Count) do
  1892. begin
  1893. Result:=ExtractWord(i,S,WordDelims)=W;
  1894. Inc(i);
  1895. end;
  1896. end;
  1897. function Numb2USA(const S: string): string;
  1898. var
  1899. i, NA: Integer;
  1900. begin
  1901. i:=Length(S);
  1902. Result:=S;
  1903. NA:=0;
  1904. while (i > 0) do begin
  1905. if ((Length(Result) - i + 1 - NA) mod 3 = 0) and (i <> 1) then
  1906. begin
  1907. insert(',', Result, i);
  1908. inc(NA);
  1909. end;
  1910. Dec(i);
  1911. end;
  1912. end;
  1913. function PadCenter(const S: string; Len: SizeInt): string;
  1914. begin
  1915. if Length(S)<Len then
  1916. begin
  1917. Result:=StringOfChar(' ',(Len div 2) -(Length(S) div 2))+S;
  1918. Result:=Result+StringOfChar(' ',Len-Length(Result));
  1919. end
  1920. else
  1921. Result:=S;
  1922. end;
  1923. function Dec2Numb(N: Longint; Len, Base: Byte): string;
  1924. var
  1925. C: Integer;
  1926. Number: Longint;
  1927. begin
  1928. if N=0 then
  1929. Result:='0'
  1930. else
  1931. begin
  1932. Number:=N;
  1933. Result:='';
  1934. while Number>0 do
  1935. begin
  1936. C:=Number mod Base;
  1937. if C>9 then
  1938. C:=C+55
  1939. else
  1940. C:=C+48;
  1941. Result:=Chr(C)+Result;
  1942. Number:=Number div Base;
  1943. end;
  1944. end;
  1945. if (Result<>'') then
  1946. Result:=AddChar('0',Result,Len);
  1947. end;
  1948. function Numb2Dec(S: string; Base: Byte): Longint;
  1949. var
  1950. i, P: sizeint;
  1951. begin
  1952. i:=Length(S);
  1953. Result:=0;
  1954. S:=UpperCase(S);
  1955. P:=1;
  1956. while (i>=1) do
  1957. begin
  1958. if (S[i]>'@') then
  1959. Result:=Result+(Ord(S[i])-55)*P
  1960. else
  1961. Result:=Result+(Ord(S[i])-48)*P;
  1962. Dec(i);
  1963. P:=P*Base;
  1964. end;
  1965. end;
  1966. function RomanToIntDontCare(const S: String): Longint;
  1967. {This was the original implementation of RomanToInt,
  1968. it is internally used in TryRomanToInt when Strictness = rcsDontCare}
  1969. const
  1970. RomanChars = ['C','D','I','L','M','V','X'];
  1971. RomanValues : array['C'..'X'] of Word
  1972. = (100,500,0,0,0,0,1,0,0,50,1000,0,0,0,0,0,0,0,0,5,0,10);
  1973. var
  1974. index, Next: Char;
  1975. i,l: SizeInt;
  1976. Negative: Boolean;
  1977. begin
  1978. Result:=0;
  1979. i:=0;
  1980. Negative:=(Length(S)>0) and (S[1]='-');
  1981. if Negative then
  1982. inc(i);
  1983. l:=Length(S);
  1984. while (i<l) do
  1985. begin
  1986. inc(i);
  1987. index:=UpCase(S[i]);
  1988. if index in RomanChars then
  1989. begin
  1990. if Succ(i)<=l then
  1991. Next:=UpCase(S[i+1])
  1992. else
  1993. Next:=#0;
  1994. if (Next in RomanChars) and (RomanValues[index]<RomanValues[Next]) then
  1995. begin
  1996. inc(Result, RomanValues[Next]);
  1997. Dec(Result, RomanValues[index]);
  1998. inc(i);
  1999. end
  2000. else
  2001. inc(Result, RomanValues[index]);
  2002. end
  2003. else
  2004. begin
  2005. Result:=0;
  2006. Exit;
  2007. end;
  2008. end;
  2009. if Negative then
  2010. Result:=-Result;
  2011. end;
  2012. { TryRomanToInt: try to convert a roman numeral to an integer
  2013. Parameters:
  2014. S: Roman numeral (like: 'MCMXXII')
  2015. N: Integer value of S (only meaningfull if the function succeeds)
  2016. Stricness: controls how strict the parsing of S is
  2017. - rcsStrict:
  2018. * Follow common subtraction rules
  2019. - only 1 preceding subtraction character allowed: IX = 9, but IIX <> 8
  2020. - from M you can only subtract C
  2021. - from D you can only subtract C
  2022. - from C you can only subtract X
  2023. - from L you can only subtract X
  2024. - from X you can only subtract I
  2025. - from V you can only subtract I
  2026. * The numeral is parsed in "groups" (first M's, then D's etc.), the next group to be parsed
  2027. must always be of a lower denomination than the previous one.
  2028. Example: 'MMDCCXX' is allowed but 'MMCCXXDD' is not
  2029. * There can only ever be 3 consecutive M's, C's, X's or I's
  2030. * There can only ever be 1 D, 1 L and 1 V
  2031. * After IX or IV there can be no more characters
  2032. * Negative numbers are not supported
  2033. // As a consequence the maximum allowed Roman numeral is MMMCMXCIX = 3999, also N can never become 0 (zero)
  2034. - rcsRelaxed: Like rcsStrict but with the following exceptions:
  2035. * An infinite number of (leading) M's is allowed
  2036. * Up to 4 consecutive M's, C's, X's and I's are allowed
  2037. // So this is allowed: 'MMMMMMCXIIII' = 6124
  2038. - rcsDontCare:
  2039. * no checking on the order of "groups" is done
  2040. * there are no restrictions on the number of consecutive chars
  2041. * negative numbers are supported
  2042. * an empty string as input will return True and N will be 0
  2043. * invalid input will return false
  2044. // for backwards comatibility: it supports rather ludicrous input like '-IIIMIII' -> -(2+(1000-1)+3)=-1004
  2045. }
  2046. function TryRomanToInt(S: String; out N: LongInt; Strictness: TRomanConversionStrictness = rcsRelaxed): Boolean;
  2047. var
  2048. i, Len: SizeInt;
  2049. Terminated: Boolean;
  2050. begin
  2051. Result := (False);
  2052. S := UpperCase(S); //don't use AnsiUpperCase please
  2053. Len := Length(S);
  2054. if (Strictness = rcsDontCare) then
  2055. begin
  2056. N := RomanToIntDontCare(S);
  2057. if (N = 0) then
  2058. begin
  2059. Result := (Len = 0);
  2060. end
  2061. else
  2062. Result := True;
  2063. Exit;
  2064. end;
  2065. if (Len = 0) then
  2066. begin
  2067. Result:=true;
  2068. N:=0;
  2069. Exit;
  2070. end;
  2071. i := 1;
  2072. N := 0;
  2073. Terminated := False;
  2074. //leading M's
  2075. while (i <= Len) and ((Strictness <> rcsStrict) or (i < 4)) and (S[i] = 'M') do
  2076. begin
  2077. //writeln('TryRomanToInt: Found 1000');
  2078. Inc(i);
  2079. N := N + 1000;
  2080. end;
  2081. //then CM or or CD or D or (C, CC, CCC, CCCC)
  2082. if (i <= Len) and (S[i] = 'D') then
  2083. begin
  2084. //writeln('TryRomanToInt: Found 500');
  2085. Inc(i);
  2086. N := N + 500;
  2087. end
  2088. else if (i + 1 <= Len) and (S[i] = 'C') then
  2089. begin
  2090. if (S[i+1] = 'M') then
  2091. begin
  2092. //writeln('TryRomanToInt: Found 900');
  2093. Inc(i,2);
  2094. N := N + 900;
  2095. end
  2096. else if (S[i+1] = 'D') then
  2097. begin
  2098. //writeln('TryRomanToInt: Found 400');
  2099. Inc(i,2);
  2100. N := N + 400;
  2101. end;
  2102. end ;
  2103. //next max 4 or 3 C's, depending on Strictness
  2104. if (i <= Len) and (S[i] = 'C') then
  2105. begin
  2106. //find max 4 C's
  2107. //writeln('TryRomanToInt: Found 100');
  2108. Inc(i);
  2109. N := N + 100;
  2110. if (i <= Len) and (S[i] = 'C') then
  2111. begin
  2112. //writeln('TryRomanToInt: Found 100');
  2113. Inc(i);
  2114. N := N + 100;
  2115. end;
  2116. if (i <= Len) and (S[i] = 'C') then
  2117. begin
  2118. //writeln('TryRomanToInt: Found 100');
  2119. Inc(i);
  2120. N := N + 100;
  2121. end;
  2122. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'C') then
  2123. begin
  2124. //writeln('TryRomanToInt: Found 100');
  2125. Inc(i);
  2126. N := N + 100;
  2127. end;
  2128. end;
  2129. //then XC or XL
  2130. if (i + 1 <= Len) and (S[i] = 'X') then
  2131. begin
  2132. if (S[i+1] = 'C') then
  2133. begin
  2134. //writeln('TryRomanToInt: Found 90');
  2135. Inc(i,2);
  2136. N := N + 90;
  2137. end
  2138. else if (S[i+1] = 'L') then
  2139. begin
  2140. //writeln('TryRomanToInt: Found 40');
  2141. Inc(i,2);
  2142. N := N + 40;
  2143. end;
  2144. end;
  2145. //then L
  2146. if (i <= Len) and (S[i] = 'L') then
  2147. begin
  2148. //writeln('TryRomanToInt: Found 50');
  2149. Inc(i);
  2150. N := N + 50;
  2151. end;
  2152. //then (X, xx, xxx, xxxx)
  2153. if (i <= Len) and (S[i] = 'X') then
  2154. begin
  2155. //find max 3 or 4 X's, depending on Strictness
  2156. //writeln('TryRomanToInt: Found 10');
  2157. Inc(i);
  2158. N := N + 10;
  2159. if (i <= Len) and (S[i] = 'X') then
  2160. begin
  2161. //writeln('TryRomanToInt: Found 10');
  2162. Inc(i);
  2163. N := N + 10;
  2164. end;
  2165. if (i <= Len) and (S[i] = 'X') then
  2166. begin
  2167. //writeln('TryRomanToInt: Found 10');
  2168. Inc(i);
  2169. N := N + 10;
  2170. end;
  2171. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'X') then
  2172. begin
  2173. //writeln('TryRomanToInt: Found 10');
  2174. Inc(i);
  2175. N := N + 10;
  2176. end;
  2177. end;
  2178. //then IX or IV
  2179. if (i + 1 <= Len) and (S[i] = 'I') then
  2180. begin
  2181. if (S[i+1] = 'X') then
  2182. begin
  2183. Terminated := (True);
  2184. //writeln('TryRomanToInt: Found 9');
  2185. Inc(i,2);
  2186. N := N + 9;
  2187. end
  2188. else if (S[i+1] = 'V') then
  2189. begin
  2190. Terminated := (True);
  2191. //writeln('TryRomanToInt: Found 4');
  2192. Inc(i,2);
  2193. N := N + 4;
  2194. end;
  2195. end;
  2196. //then V
  2197. if (not Terminated) and (i <= Len) and (S[i] = 'V') then
  2198. begin
  2199. //writeln('TryRomanToInt: Found 5');
  2200. Inc(i);
  2201. N := N + 5;
  2202. end;
  2203. //then I
  2204. if (not Terminated) and (i <= Len) and (S[i] = 'I') then
  2205. begin
  2206. Terminated := (True);
  2207. //writeln('TryRomanToInt: Found 1');
  2208. Inc(i);
  2209. N := N + 1;
  2210. //Find max 2 or 3 closing I's, depending on strictness
  2211. if (i <= Len) and (S[i] = 'I') then
  2212. begin
  2213. //writeln('TryRomanToInt: Found 1');
  2214. Inc(i);
  2215. N := N + 1;
  2216. end;
  2217. if (i <= Len) and (S[i] = 'I') then
  2218. begin
  2219. //writeln('TryRomanToInt: Found 1');
  2220. Inc(i);
  2221. N := N + 1;
  2222. end;
  2223. if (Strictness <> rcsStrict) and (i <= Len) and (S[i] = 'I') then
  2224. begin
  2225. //writeln('TryRomanToInt: Found 1');
  2226. Inc(i);
  2227. N := N + 1;
  2228. end;
  2229. end;
  2230. //writeln('TryRomanToInt: Len = ',Len,' i = ',i);
  2231. Result := (i > Len);
  2232. //if Result then writeln('TryRomanToInt: N = ',N);
  2233. end;
  2234. function RomanToInt(const S: string; Strictness: TRomanConversionStrictness = rcsRelaxed): Longint;
  2235. begin
  2236. if not TryRomanToInt(S, Result, Strictness) then
  2237. raise EConvertError.CreateFmt(SInvalidRomanNumeral,[S]);
  2238. end;
  2239. function RomanToIntDef(const S: String; const ADefault: Longint;
  2240. Strictness: TRomanConversionStrictness): Longint;
  2241. begin
  2242. if not TryRomanToInt(S, Result, Strictness) then
  2243. Result := ADefault;
  2244. end;
  2245. function IntToRoman(Value: Longint): string;
  2246. const
  2247. Arabics : Array[1..13] of Integer
  2248. = (1,4,5,9,10,40,50,90,100,400,500,900,1000);
  2249. Romans : Array[1..13] of String
  2250. = ('I','IV','V','IX','X','XL','L','XC','C','CD','D','CM','M');
  2251. var
  2252. i: Integer;
  2253. begin
  2254. Result:='';
  2255. for i:=13 downto 1 do
  2256. while (Value >= Arabics[i]) do
  2257. begin
  2258. Value:=Value-Arabics[i];
  2259. Result:=Result+Romans[i];
  2260. end;
  2261. end;
  2262. function IntToBin(Value: Longint; Digits, Spaces: Integer): string;
  2263. var endpos : integer;
  2264. p,p2:pchar;
  2265. k: integer;
  2266. begin
  2267. Result:='';
  2268. if (Digits>32) then
  2269. Digits:=32;
  2270. if (spaces=0) then
  2271. begin
  2272. result:=inttobin(value,digits);
  2273. exit;
  2274. end;
  2275. endpos:=digits+ (digits-1) div spaces;
  2276. setlength(result,endpos);
  2277. p:=@result[endpos];
  2278. p2:=@result[1];
  2279. k:=spaces;
  2280. while (p>=p2) do
  2281. begin
  2282. if k=0 then
  2283. begin
  2284. p^:=' ';
  2285. dec(p);
  2286. k:=spaces;
  2287. end;
  2288. p^:=chr(48+(cardinal(value) and 1));
  2289. value:=cardinal(value) shr 1;
  2290. dec(p);
  2291. dec(k);
  2292. end;
  2293. end;
  2294. function IntToBin(Value: Longint; Digits: Integer): string;
  2295. var p,p2 : pchar;
  2296. begin
  2297. result:='';
  2298. if digits<=0 then exit;
  2299. setlength(result,digits);
  2300. p:=pchar(pointer(@result[digits]));
  2301. p2:=pchar(pointer(@result[1]));
  2302. // typecasts because we want to keep intto* delphi compat and take an integer
  2303. while (p>=p2) and (cardinal(value)>0) do
  2304. begin
  2305. p^:=chr(48+(cardinal(value) and 1));
  2306. value:=cardinal(value) shr 1;
  2307. dec(p);
  2308. end;
  2309. digits:=p-p2+1;
  2310. if digits>0 then
  2311. fillchar(result[1],digits,#48);
  2312. end;
  2313. function intToBin(Value: int64; Digits:integer): string;
  2314. var p,p2 : pchar;
  2315. begin
  2316. result:='';
  2317. if digits<=0 then exit;
  2318. setlength(result,digits);
  2319. p:=pchar(pointer(@result[digits]));
  2320. p2:=pchar(pointer(@result[1]));
  2321. // typecasts because we want to keep intto* delphi compat and take a signed val
  2322. // and avoid warnings
  2323. while (p>=p2) and (qword(value)>0) do
  2324. begin
  2325. p^:=chr(48+(cardinal(value) and 1));
  2326. value:=qword(value) shr 1;
  2327. dec(p);
  2328. end;
  2329. digits:=p-p2+1;
  2330. if digits>0 then
  2331. fillchar(result[1],digits,#48);
  2332. end;
  2333. function FindPart(const HelpWilds, InputStr: string): SizeInt;
  2334. var
  2335. Diff, i, J: SizeInt;
  2336. begin
  2337. Result:=0;
  2338. i:=Pos('?',HelpWilds);
  2339. if (i=0) then
  2340. Result:=Pos(HelpWilds, inputStr)
  2341. else
  2342. begin
  2343. Diff:=Length(inputStr) - Length(HelpWilds);
  2344. for i:=0 to Diff do
  2345. begin
  2346. for J:=1 to Length(HelpWilds) do
  2347. if (inputStr[i + J] = HelpWilds[J]) or (HelpWilds[J] = '?') then
  2348. begin
  2349. if (J=Length(HelpWilds)) then
  2350. begin
  2351. Result:=i+1;
  2352. Exit;
  2353. end;
  2354. end
  2355. else
  2356. Break;
  2357. end;
  2358. end;
  2359. end;
  2360. Function isMatch(level : integer;inputstr,wilds : string; CWild, CinputWord: SizeInt;MaxInputword,maxwilds : SizeInt; Out EOS : Boolean) : Boolean;
  2361. function WildisQuestionmark : boolean;
  2362. begin
  2363. Result:=CWild <= MaxWilds;
  2364. if Result then
  2365. Result:= Wilds[CWild]='?';
  2366. end;
  2367. function WildisStar : boolean;
  2368. begin
  2369. Result:=CWild <= MaxWilds;
  2370. if Result then
  2371. Result:= Wilds[CWild]='*';
  2372. end;
  2373. begin
  2374. EOS:=False;
  2375. Result:=True;
  2376. repeat
  2377. if WildisStar then { handling of '*' }
  2378. begin
  2379. inc(CWild);
  2380. if CWild>MaxWilds then
  2381. begin
  2382. EOS:=true;
  2383. exit;
  2384. end;
  2385. while WildisQuestionmark do { equal to '?' }
  2386. begin
  2387. { goto next letter }
  2388. inc(CWild);
  2389. inc(CinputWord);
  2390. end;
  2391. { increase until a match }
  2392. Repeat
  2393. while (CinputWord <= MaxinputWord) and (CWild <= MaxWilds) and (inputStr[CinputWord]<>Wilds[CWild]) do
  2394. inc(CinputWord);
  2395. Result:=isMatch(Level+1,inputstr,wilds,CWild, CinputWord,MaxInputword,maxwilds,EOS);
  2396. if not Result then
  2397. Inc(cInputWord);
  2398. Until Result or (CinputWord>=MaxinputWord);
  2399. if Result and EOS then
  2400. Exit;
  2401. Continue;
  2402. end;
  2403. if WildisQuestionmark then { equal to '?' }
  2404. begin
  2405. { goto next letter }
  2406. inc(CWild);
  2407. inc(CinputWord);
  2408. Continue;
  2409. end;
  2410. if (CinputWord>MaxinputWord) or (CWild > MaxWilds) or (inputStr[CinputWord] = Wilds[CWild]) then { equal letters }
  2411. begin
  2412. { goto next letter }
  2413. inc(CWild);
  2414. inc(CinputWord);
  2415. Continue;
  2416. end;
  2417. Result:=false;
  2418. Exit;
  2419. until (CinputWord > MaxinputWord) or (CWild > MaxWilds);
  2420. { no completed evaluation, we need to check what happened }
  2421. if (CinputWord <= MaxinputWord) or (CWild < MaxWilds) then
  2422. Result:=false
  2423. else if (CWild>Maxwilds) then
  2424. EOS:=False
  2425. else
  2426. begin
  2427. EOS:=Wilds[CWild]='*';
  2428. if not EOS then
  2429. Result:=False;
  2430. end
  2431. end;
  2432. function IsWild(InputStr, Wilds: string; IgnoreCase: Boolean): Boolean;
  2433. var
  2434. i: SizeInt;
  2435. MaxinputWord, MaxWilds: SizeInt; { Length of inputStr and Wilds }
  2436. eos : Boolean;
  2437. begin
  2438. Result:=true;
  2439. if Wilds = inputStr then
  2440. Exit;
  2441. { delete '**', because '**' = '*' }
  2442. i:=Pos('**', Wilds);
  2443. while i > 0 do
  2444. begin
  2445. Delete(Wilds, i, 1);
  2446. i:=Pos('**', Wilds);
  2447. end;
  2448. if Wilds = '*' then { for fast end, if Wilds only '*' }
  2449. Exit;
  2450. MaxinputWord:=Length(inputStr);
  2451. MaxWilds:=Length(Wilds);
  2452. if (MaxWilds = 0) or (MaxinputWord = 0) then
  2453. begin
  2454. Result:=false;
  2455. Exit;
  2456. end;
  2457. if ignoreCase then { upcase all letters }
  2458. begin
  2459. inputStr:=AnsiUpperCase(inputStr);
  2460. Wilds:=AnsiUpperCase(Wilds);
  2461. end;
  2462. Result:=isMatch(1,inputStr,wilds,1,1,MaxinputWord, MaxWilds,EOS);
  2463. end;
  2464. function XorString(const Key, Src: ShortString): ShortString;
  2465. var
  2466. i: SizeInt;
  2467. begin
  2468. Result:=Src;
  2469. if Length(Key) > 0 then
  2470. for i:=1 to Length(Src) do
  2471. Result[i]:=Chr(Byte(Key[1 + ((i - 1) mod Length(Key))]) xor Ord(Src[i]));
  2472. end;
  2473. function XorEncode(const Key, Source: string): string;
  2474. var
  2475. i: Integer;
  2476. C: Byte;
  2477. begin
  2478. Result:='';
  2479. for i:=1 to Length(Source) do
  2480. begin
  2481. if Length(Key) > 0 then
  2482. C:=Byte(Key[1 + ((i - 1) mod Length(Key))]) xor Byte(Source[i])
  2483. else
  2484. C:=Byte(Source[i]);
  2485. Result:=Result+AnsiLowerCase(intToHex(C, 2));
  2486. end;
  2487. end;
  2488. function XorDecode(const Key, Source: string): string;
  2489. var
  2490. i: Integer;
  2491. C: Char;
  2492. begin
  2493. Result:='';
  2494. for i:=0 to Length(Source) div 2 - 1 do
  2495. begin
  2496. C:=Chr(StrTointDef('$' + Copy(Source, (i * 2) + 1, 2), Ord(' ')));
  2497. if Length(Key) > 0 then
  2498. C:=Chr(Byte(Key[1 + (i mod Length(Key))]) xor Byte(C));
  2499. Result:=Result + C;
  2500. end;
  2501. end;
  2502. function GetCmdLineArg(const Switch: string; SwitchChars: TSysCharSet): string;
  2503. var
  2504. i: Integer;
  2505. S: string;
  2506. begin
  2507. i:=1;
  2508. Result:='';
  2509. while (Result='') and (i<=ParamCount) do
  2510. begin
  2511. S:=ParamStr(i);
  2512. if (SwitchChars=[]) or ((S[1] in SwitchChars) and (Length(S) > 1)) and
  2513. (AnsiCompareText(Copy(S,2,Length(S)-1),Switch)=0) then
  2514. begin
  2515. inc(i);
  2516. if i<=ParamCount then
  2517. Result:=ParamStr(i);
  2518. end;
  2519. inc(i);
  2520. end;
  2521. end;
  2522. function RPosEX(C: char; const S: AnsiString; offs: cardinal): SizeInt;
  2523. var I : SizeUInt;
  2524. p,p2: pChar;
  2525. Begin
  2526. I:=Length(S);
  2527. If (I<>0) and (offs<=i) Then
  2528. begin
  2529. p:=@s[offs];
  2530. p2:=@s[1];
  2531. while (p2<=p) and (p^<>c) do dec(p);
  2532. RPosEx:=(p-p2)+1;
  2533. end
  2534. else
  2535. RPosEX:=0;
  2536. End;
  2537. function RPos(c: char; const S: AnsiString): SizeInt;
  2538. var I : SizeInt;
  2539. p,p2: pChar;
  2540. Begin
  2541. I:=Length(S);
  2542. If I<>0 Then
  2543. begin
  2544. p:=@s[i];
  2545. p2:=@s[1];
  2546. while (p2<=p) and (p^<>c) do dec(p);
  2547. i:=p-p2+1;
  2548. end;
  2549. RPos:=i;
  2550. End;
  2551. function RPos(const Substr: AnsiString; const Source: AnsiString): SizeInt;
  2552. var
  2553. MaxLen,llen : SizeInt;
  2554. c : char;
  2555. pc,pc2 : pchar;
  2556. begin
  2557. rPos:=0;
  2558. llen:=Length(SubStr);
  2559. maxlen:=length(source);
  2560. if (llen>0) and (maxlen>0) and ( llen<=maxlen) then
  2561. begin
  2562. // i:=maxlen;
  2563. pc:=@source[maxlen];
  2564. pc2:=@source[llen-1];
  2565. c:=substr[llen];
  2566. while pc>=pc2 do
  2567. begin
  2568. if (c=pc^) and
  2569. (CompareChar(Substr[1],pchar(pc-llen+1)^,Length(SubStr))=0) then
  2570. begin
  2571. rPos:=pchar(pc-llen+1)-pchar(@source[1])+1;
  2572. exit;
  2573. end;
  2574. dec(pc);
  2575. end;
  2576. end;
  2577. end;
  2578. function RPosex(const Substr: AnsiString; const Source: AnsiString; offs: cardinal): SizeInt;
  2579. var
  2580. MaxLen,llen : SizeInt;
  2581. c : char;
  2582. pc,pc2 : pchar;
  2583. begin
  2584. rPosex:=0;
  2585. llen:=Length(SubStr);
  2586. maxlen:=length(source);
  2587. if SizeInt(offs)<maxlen then maxlen:=offs;
  2588. if (llen>0) and (maxlen>0) and ( llen<=maxlen) then
  2589. begin
  2590. // i:=maxlen;
  2591. pc:=@source[maxlen];
  2592. pc2:=@source[llen-1];
  2593. c:=substr[llen];
  2594. while pc>=pc2 do
  2595. begin
  2596. if (c=pc^) and
  2597. (CompareChar(Substr[1],pchar(pc-llen+1)^,Length(SubStr))=0) then
  2598. begin
  2599. rPosex:=pchar(pc-llen+1)-pchar(@source[1])+1;
  2600. exit;
  2601. end;
  2602. dec(pc);
  2603. end;
  2604. end;
  2605. end;
  2606. // def from delphi.about.com:
  2607. procedure BinToHex(BinValue, HexValue: PChar; BinBufSize: Integer);
  2608. Const
  2609. HexDigits='0123456789ABCDEF';
  2610. var
  2611. i : longint;
  2612. begin
  2613. for i:=0 to binbufsize-1 do
  2614. begin
  2615. HexValue[0]:=hexdigits[1+((ord(binvalue^) shr 4))];
  2616. HexValue[1]:=hexdigits[1+((ord(binvalue^) and 15))];
  2617. inc(hexvalue,2);
  2618. inc(binvalue);
  2619. end;
  2620. end;
  2621. function HexToBin(HexValue, BinValue: PChar; BinBufSize: Integer): Integer;
  2622. // more complex, have to accept more than bintohex
  2623. // A..F    1000001
  2624. // a..f    1100001
  2625. // 0..9     110000
  2626. var i,j,h,l : integer;
  2627. begin
  2628. i:=binbufsize;
  2629. while (i>0) do
  2630. begin
  2631. if hexvalue^ IN ['A'..'F','a'..'f'] then
  2632. h:=((ord(hexvalue^)+9) and 15)
  2633. else if hexvalue^ IN ['0'..'9'] then
  2634. h:=((ord(hexvalue^)) and 15)
  2635. else
  2636. break;
  2637. inc(hexvalue);
  2638. if hexvalue^ IN ['A'..'F','a'..'f'] then
  2639. l:=(ord(hexvalue^)+9) and 15
  2640. else if hexvalue^ IN ['0'..'9'] then
  2641. l:=(ord(hexvalue^)) and 15
  2642. else
  2643. break;
  2644. j := l + (h shl 4);
  2645. inc(hexvalue);
  2646. binvalue^:=chr(j);
  2647. inc(binvalue);
  2648. dec(i);
  2649. end;
  2650. result:=binbufsize-i;
  2651. end;
  2652. function PosSetEx(const c: TSysCharSet; const s: ansistring; count: Integer): SizeInt;
  2653. var i,j:SizeInt;
  2654. begin
  2655. if pchar(pointer(s))=nil then
  2656. j:=0
  2657. else
  2658. begin
  2659. i:=length(s);
  2660. j:=count;
  2661. if j>i then
  2662. begin
  2663. result:=0;
  2664. exit;
  2665. end;
  2666. while (j<=i) and (not (s[j] in c)) do inc(j);
  2667. if (j>i) then
  2668. j:=0; // not found.
  2669. end;
  2670. result:=j;
  2671. end;
  2672. function PosSet(const c: TSysCharSet; const s: ansistring): SizeInt;
  2673. begin
  2674. result:=possetex(c,s,1);
  2675. end;
  2676. function PosSetEx(const c: string; const s: ansistring; count: Integer): SizeInt;
  2677. var cset : TSysCharSet;
  2678. i : SizeInt;
  2679. begin
  2680. cset:=[];
  2681. if length(c)>0 then
  2682. for i:=1 to length(c) do
  2683. include(cset,c[i]);
  2684. result:=possetex(cset,s,count);
  2685. end;
  2686. function PosSet(const c: string; const s: ansistring): SizeInt;
  2687. var cset : TSysCharSet;
  2688. i : SizeInt;
  2689. begin
  2690. cset:=[];
  2691. if length(c)>0 then
  2692. for i:=1 to length(c) do
  2693. include(cset,c[i]);
  2694. result:=possetex(cset,s,1);
  2695. end;
  2696. procedure Removeleadingchars(VAR S: AnsiString; const CSet: TSysCharset);
  2697. VAR I,J : Longint;
  2698. Begin
  2699. I:=Length(S);
  2700. IF (I>0) Then
  2701. Begin
  2702. J:=1;
  2703. While (J<=I) And (S[J] IN CSet) DO
  2704. INC(J);
  2705. IF J>1 Then
  2706. Delete(S,1,J-1);
  2707. End;
  2708. End;
  2709. function TrimLeftSet(const S: String;const CSet:TSysCharSet): String;
  2710. begin
  2711. result:=s;
  2712. removeleadingchars(result,cset);
  2713. end;
  2714. procedure RemoveTrailingChars(VAR S: AnsiString; const CSet: TSysCharset);
  2715. VAR I,J: LONGINT;
  2716. Begin
  2717. I:=Length(S);
  2718. IF (I>0) Then
  2719. Begin
  2720. J:=I;
  2721. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2722. IF J<>I Then
  2723. SetLength(S,J);
  2724. End;
  2725. End;
  2726. function TrimRightSet(const S: String; const CSet: TSysCharSet): String;
  2727. begin
  2728. result:=s;
  2729. RemoveTrailingchars(result,cset);
  2730. end;
  2731. procedure RemovePadChars(VAR S: AnsiString; const CSet: TSysCharset);
  2732. VAR I,J,K: LONGINT;
  2733. Begin
  2734. I:=Length(S);
  2735. IF (I>0) Then
  2736. Begin
  2737. J:=I;
  2738. While (j>0) and (S[J] IN CSet) DO DEC(J);
  2739. if j=0 Then
  2740. begin
  2741. s:='';
  2742. exit;
  2743. end;
  2744. k:=1;
  2745. While (k<=I) And (S[k] IN CSet) DO
  2746. INC(k);
  2747. IF k>1 Then
  2748. begin
  2749. move(s[k],s[1],j-k+1);
  2750. setlength(s,j-k+1);
  2751. end
  2752. else
  2753. setlength(s,j);
  2754. End;
  2755. End;
  2756. function TrimSet(const S: String;const CSet:TSysCharSet): String;
  2757. begin
  2758. result:=s;
  2759. RemovePadChars(result,cset);
  2760. end;
  2761. end.