strutils.pp 80 KB

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