/
YakuninAV
/
Convertors
Обзор
Документация
Войти
/
YakuninAV
/
Convertors
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Core/dbdutf8utils.pas
2 079 строк
95 KB
YakuninAV
begin with gitflick
31 июл 2026, 15:21
31 июл 2026, 15:21
1dc0fc6
Код
Авторство
О чём код?
unit DBDUtf8Utils; {$include mormot.defines.inc} {.$mode objfpc}{$H+} interface uses Classes, SysUtils, PUCU, mormot.core.base, mormot.core.unicode, mormot.core.data, mormot.core.buffers, mormot.core.text ,DBDCommons ; {$IFNDEF DELPHIXE2} type Int32 = Integer; type UInt32 = LongWord; {$ENDIF} type TdbdPUCUCategories = set of byte; const dbdWordSymbolsCategory = [PUCUUnicodeCategoryLu, PUCUUnicodeCategoryLl, PUCUUnicodeCategoryLt, PUCUUnicodeCategoryLm, PUCUUnicodeCategoryLo, PUCUUnicodeCategoryNd, PUCUUnicodeCategoryNl, PUCUUnicodeCategoryNo, PUCUUnicodeCategoryPc]; dbdLetterCategory = [PUCUUnicodeCategoryLu, PUCUUnicodeCategoryLl, PUCUUnicodeCategoryLt, PUCUUnicodeCategoryLm, PUCUUnicodeCategoryLo]; dbdNumberCategory = [PUCUUnicodeCategoryNd, PUCUUnicodeCategoryNl, PUCUUnicodeCategoryNo]; dbdNumberPunctuation = [PUCUUnicodeCategoryPd, PUCUUnicodeCategoryPs, PUCUUnicodeCategoryPe, PUCUUnicodeCategoryPi, PUCUUnicodeCategoryPf, PUCUUnicodeCategoryPc, PUCUUnicodeCategoryPo, PUCUUnicodeCategorySm, PUCUUnicodeCategorySc ]; dbdExtraCharCategory = [PUCUUnicodeCategoryCn, PUCUUnicodeCategoryZs, PUCUUnicodeCategoryZl, PUCUUnicodeCategoryZp, PUCUUnicodeCategoryCc, PUCUUnicodeCategoryCf, PUCUUnicodeCategoryCo, PUCUUnicodeCategoryCs, {PUCUUnicodeCategoryPd, PUCUUnicodeCategoryPs, PUCUUnicodeCategoryPe, PUCUUnicodeCategoryPi,} {PUCUUnicodeCategoryPf, PUCUUnicodeCategoryPc, PUCUUnicodeCategoryPo,} {PUCUUnicodeCategorySm, PUCUUnicodeCategorySc,} PUCUUnicodeCategorySk{, PUCUUnicodeCategorySo} ]; dbdClassUnicodeCategoryC: TdbdPUCUCategories = [PUCUUnicodeCategoryCn,PUCUUnicodeCategoryCc, PUCUUnicodeCategoryCf, PUCUUnicodeCategoryCo, PUCUUnicodeCategoryCs]; type /// Массив символов Unicode (кодовых точек) TUcs4DynArray = array of Ucs4CodePoint; /// Функция конвертирует UTF8-строку в массив символов Unicode. Если флаг ToUpper установлен, // то символы переводятся в верхний регистр function DBDRawUtf8ToUcs4Array(const txt: RawUtf8; const ToUpper: Boolean=False): TUcs4DynArray; /// Функция преобразует строку UTF8-символов представляющую собой запись числа в шестнацатеричной форме // в десятичное число. Символ "$" в качестве признака шестнацатеричного числа необязателен и может быть опущен. // Если строка пуста, то функция возвращает 0, если строка содержит недопустимый символ то функция возвращает -1 function HexToInteger(const Hex: PUtf8Char): Int32; overload; /// Функция преобразует строку UTF8-символов представляющую собой запись числа в шестнацатеричной форме // в десятичное число. Символ "$" в качестве признака шестнацатеричного числа необязателен и может быть опущен. // Если строка пуста, то функция возвращает 0, если строка содержит недопустимый символ то функция возвращает -1 function HexToInteger(const Hex: RawUTF8): Int32; inline; overload; /// Функция преобразует строку UTF8-символов представляющую собой запись числа в шестнацатеричной форме // в десятичное число. Символ "$" в качестве признака шестнацатеричного числа необязателен и может быть опущен // Если строка пуста, то функция возвращает 0, если строка содержит недопустимый символ то функция возвращает -1 function HexToInteger(const Hex: ShortString): Int32; overload; /// Функция сравнивает строку в кодировке Utf8 с образцом, заданным строкой символов function DBDCompareStringI(const txt: PUtf8Char; const S: string): Boolean; overload; /// Функция сравнивает строку с образцом, заданным строкой символов в верхнем регистре function DBDCompareUtf8U(const txt: PUtf8Char; const UpperTxt: RawUtf8): Boolean; overload; /// Функция сравнивает строку с образцом, заданным масивом символов Unicode в верхнем регистре function DBDCompareUtf8U(const txt: PUtf8Char; const UpperUcs4Array: TUcs4DynArray): Boolean; overload; /// Функция возвращает true, если Unicode-символ (кодовая точка) принадлежит одной из заданных категорий function DBDIsCategory(const USC4: Ucs4CodePoint; const Categories: TdbdPUCUCategories): Boolean; overload; /// Функция возвращает true, если символ Utf8 принадлежит одной из заданных категорий function DBDIsCategory(const Utf8Char: PUtf8Char; const Categories: TdbdPUCUCategories): Boolean; overload; /// Функция возвращает true, если Unicode-символ (кодовая точка) представляет латинскую букву в верхнем регистре function DBDisLatinUpper(const USC4: Ucs4CodePoint): Boolean; inline; /// Функция возвращает true, если Unicode-символ (кодовая точка) представляет латинскую букву в нижнем регистре function DBDisLatinLow(const USC4: Ucs4CodePoint): Boolean; inline; /// Функция возвращает true, если Unicode-символ (кодовая точка) представляет русскую букву в верхнем регистре function DBDisRussianUpper(const USC4: Ucs4CodePoint): Boolean; inline; /// Функция возвращает true, если Unicode-символ (кодовая точка) представляет русскую букву в нижнем регистре function DBDisRussianLow(const USC4: Ucs4CodePoint): Boolean; inline; /// Функция возвращает true, если Unicode-символ (кодовая точка) представляет цифру function DBDisDigit(const USC4: Ucs4CodePoint): Boolean; inline; /// Функциф возвращает длину Utf8-символа в байтах или -1, если символ не является Utf8 function DBDCharSize(const P: PUtf8Char): PtrInt; /// Функция возвращает true, если строка пуста или содержит только пробельные символы function DBDisEmptyString(const txt: PUtf8Char): Boolean; overload; /// Функция возвращает true, если строка пуста или содержит только пробельные символы function DBDisEmptyString(const txt: RawUtf8): Boolean; inline; overload; /// function DBDisExtraChar(const USC4: Ucs4CodePoint): Boolean; /// Функция проверяет, что Unicode-символ (кодовая точка) может быть символом входящим запись числа (целого или вещественного) function DBDisNumberChar(const USC4: Ucs4CodePoint): Boolean; /// Функция проверяет, что Unicode-символ (кодовая точка) является пробельным символом function DBDisWhiteSpace(const USC4: Ucs4CodePoint): Boolean; inline; overload; /// Функция проверяет, что параметр Р указывает на символ UTF-8, являющийся пробелом (пробельным символом) function DBDisWhiteSpace(var p: PUtf8Char; const MoveNext: Boolean=True): Boolean; inline; overload; function DBDisWordSymbol(const USC4: Ucs4CodePoint): Boolean; inline; overload; function DBDisWordSymbol(var p: PUtf8Char): Boolean; overload; /// Функция копирует из строки заданное количество символов function DBDCopyUtf8(const txt: PUtf8Char; const Count: Integer): RawUtf8; overload; /// Функция копирует из строки заданное количество символов function DBDCopyUtf8(const txt: RawUtf8; const Start, Count: Integer): RawUtf8; overload; /// Функция возвращает True, если указатель установлен на конец строки (#00) или строка пуста (nil) function DBDisEndOfUtf8(const txt: PUtf8Char): Boolean; inline; /// Функция возвращает true, если строка состоит только из символов C. При установленном флаге Trimmed // игнорируются пробелы в начале и конце строки function DBDisDupeString(const txt: PUtf8Char; const C: AnsiChar; Trimmed: Boolean=False): Boolean; overload; /// Функция возвращает true, если строка состоит только из символов C. При установленном флаге Trimmed // игнорируются пробелы в начале и конце строки function DBDisDupeString(const txt: RawUtf8; const C: AnsiChar; Trimmed: Boolean=False): Boolean; inline; overload; /// Функция возвращает true, если строка состоит только из подстрок sub. При установленном флаге Trimmed // игнорируются пробелы в начале и конце строки function DBDisDupeString(const txt, sub: PUtf8Char; Trimmed: Boolean=False): Boolean; overload; /// Функция возвращает true, если строка состоит только из подстрок sub. При установленном флаге Trimmed // игнорируются пробелы в начале и конце строки function DBDisDupeString(const txt, sub: RawUtf8; Trimmed: Boolean=False): Boolean; inline; overload; /// Функция возвращает позицию ASCI-символа, либо nil, если символ не найден function DBDFindChar(const txt: PUTF8Char; const C: AnsiChar): PUtf8Char; overload; inline; /// Функция возвращает позицию символа, представленного кодовой точкой, либо nil, если символ не найден function DBDFindChar(const txt: PUTF8Char; const USC4: Ucs4CodePoint): PUtf8Char; overload; /// Функция возвращает номер символа, представленного кодовой точкой, в заданной строке либо 0, если символ не найден. // Параметр Start задаёт позицию символа, начиная с которого будет осуществляться поиск. // Если задан Offset, то по этому адресу сохраняется позиция найденного символа function DBDFindChar(const txt: RawUtf8; const USC4: Ucs4CodePoint; const Start: Integer=1; const CharPtr: PPUtf8Char=nil): Integer; overload; /// Функция возвращает номер символа-цифры, либо 0, если строка не содержит цифр, или меньше 0 при ошибках в параметрах function DBDFindDigit(const txt: RawUtf8; const Start: Integer=1; const CharPtr: PPUtf8Char=nil): Integer; overload; /// Функция возвращает адрес конца строки, заданной указателем function FindEndStr(const txt: PUTF8Char; const MaxLen: Cardinal=1024): PUtf8Char; /// Функция возвращает позицию первого не пробельного символа function DBDFindNonSpaceChar(const p: PUtf8Char): PUtf8Char; overload; /// Функция возвращает позицию первого пробельного символа function DBDFindSpaceChar(const p: PUtf8Char): PUTF8Char; overload; /// Функция находит первую позицию пробельного символа справа от заданной позиции // в случае успеха функция возвращает true // предполагается что vPos указывает на первый байт исходного или найденного символа function DBDFindSpaceChar(const txt: RawUTF8; var vPos: integer): Boolean; overload; /// Функция ищет в строке стоп-символ из заданного массива символов и, в случае успеха, возвращает указатель на найденный символ, // а также может быть возвращён индекс стоп-символа в массиве function DBDFindStopChar(const txt: PUtf8Char; const Stops: array of AnsiChar; const Idx: PPtrUInt=nil): PUtf8Char; overload; function DBDFindStopChar(const txt: PUtf8Char; const Stops: PAnsiChar; const Idx: PPtrUInt=nil): PUtf8Char; overload; /// Функция находит в строке подстроку, расположенную после символа заданного позицией Start и возвращает указатель на стмвол, // следующий за найденной подстрокой function DBDFindSubstr(const txt, sub: RawUtf8; const Start: Integer=1; const After: Boolean=True): PUtf8Char; overload; function DBDFindSubstr(const txt: PUtf8Char; const sub: RawUtf8; const Start: Integer=1; const After: Boolean=True): PUtf8Char; overload; /// Функция находит в строке подстроку заданную символами в верхнема регистре. Поиск начинается с позиции символа Start // Если установлен флаг After, то функция возвращает указатель на символ после найденной подстроки, иначе - на первый символ // найденной подстроки function DBDFindSubstrU(const txt, sub: RawUtf8; const Start: Integer=1; const After: Boolean=False): PUtf8Char; overload; function DBDFindSubstrUCase(const txt, sub: RawUtf8; const Start: Integer=1; const After: Boolean=False; const PP: PPUtf8Char=nil): Integer; /// Функция ищет подстроку aSub в строке aText, начиная с байтовой позиции aFromPos, // двигаясь К НАЧАЛУ строки. // Возвращает 1-based байтовую позицию начала подстроки, или 0, если ничего не найдено. function DBDFindSubstrBackward(const aText, aSub: RawUtf8; aFromPos: Integer): Integer; function DBDFindSubStrBackwardAnsi(const aText, aSub: AnsiString; aFromPos: Integer): Integer; /// Функция возвращает позицию последнего Unicode-символа строки не являющегося пробельным function DBDLastNonSpaceSymbol(const txt: RawUtf8): PUtf8Char; overload; /// Функция возвращает позицию первого Unicode-символа строки не являющегося пробельным, если указатель PP задан, то // в нём возращется указатель на найденный символ function DBDFirstNonSpaceSymbol(const txt: RawUtf8; const PP: PPUtf8Char=nil): Integer; /// Функция возвращает позицию последнего Unicode-символа строки не являющегося пробельным, если указатель PP задан, то // в нём возращется указатель на найденный символ function DBDLastNonSpaceSymbol(const txt: RawUtf8; const PP: PPUtf8Char=nil): Integer; overload; /// Функция возвращает указатель на последний символ строки function DBDLastChar(const txt: PUtf8Char): PUtf8Char; overload; /// Функция возвращает указатель на последний символ строки function DBDLastChar(const txt: RawUtf8): PUtf8Char; overload; /// Функция возвращает UNICODE-категорию символа и переходит к следующему символу function DBDGetCategory(var p: PUtf8Char): Integer; /// Функция возвращает указатель на символ строки принадлежащий одной из заданных категорий, либо nil function DBDFindCharCategory(const p: PUtf8Char; const Categories: array of Integer): PUtf8Char; overload; /// Функция возвращает указатель на символ строки принадлежащий одной из заданных категорий, либо nil function DBDFindCharCategory(const p: PUtf8Char; const Categories: TdbdPUCUCategories): PUtf8Char; overload; /// Функция возвращает указатель на символ, заданный позицией в строке // Если заданная строка пуста или заданная позиция символа находится вне диапазона допустимых позиций (от 1 до // позиции конца строки "$00"), то резутат будет nil function DBDPositionToPointer(const txt: PUtf8Char; const PosNo: Integer): PUtf8Char; overload; /// Функция возвращает указатель на символ, заданный позицией в строке // Если заданная строка пуста или заданная позиция символа находится вне диапазона допустимых позиций (от 1 до // позиции конца строки "$00"), то резутат будет nil //! txt - указатель на начало строки //! PosNo - количество символов от начала строки //! oTxt - указатель на символ отстоящий от начала строки на PosNo позиций, либо на конец строки function DBDPositionToPointer(const txt: PUtf8Char; const PosNo: Integer; out oTxt: PUtf8Char): Boolean; overload; /// Функция возвращает указатель на символ, заданный позицией в строке // Если заданная строка пуста или заданная позиция символа находится вне диапазона допустимых позиций (от 1 до // позиции конца строки "$00"), то резутат будет nil function DBDPositionToPointer(const txt: RawUtf8; const PosNo: Integer): PUtf8Char; overload; /// Функция подсчитывает количество символов между двумя указателями на символы в строке function DBDCharCount(const PStart: PUtf8Char; const PEnd: PUtf8Char=nil): Integer; /// Функция выделяет из строки текст между пробельными символами начиная с символа на который указывает P // P должен указывать на первый байт символа // лидирующие пробельные символы пропускаются // после выделения текста Р указывает на символ следующий за пробельным символом ограничивающем справа выделенный текст // если начиная с символа на который указывает Р в строке отутствуют не пробельные символы, то функция возвращает // пустую строку function DBDGetWord(var P: PUtf8Char): RawUTF8; function DBDGetWords(const Txt: RawUTF8): TRawUTF8DynArray; /// Функция возвращает кодовую точку Unicode из строки и перемещает указатель на следующую кодовую точку function DBDUTF8ToUCS4(var p: PUtf8Char): Ucs4CodePoint; /// Функция возвращает кодовую точку Unicode из строки, переводит её в верхний регистр и перемещает указатель // на следующую кодовую точку function DBDUTF8ToUCS4Upper(var p: PUtf8Char): Ucs4CodePoint; inline; /// Функция переводит символ USC4 в верхний регистр function DBDUCS4ToLower(const C: Ucs4CodePoint): Ucs4CodePoint; inline; /// Функция переводит символ USC4 в верхний регистр function DBDUCS4ToUpper(const C: Ucs4CodePoint): Ucs4CodePoint; /// функция преобразует кодовую точку в символ Utf8 (строку) function DBDUSC4ToUtf8(const C: Ucs4CodePoint): RawUtf8; /// Функция вычислает длину текста в UTF8-символах function DBDLengthUTF8(const txt: RawUTF8): Integer; /// Функция вычисляет длину текста, заданного указателем, в байтах function DBDSizeUtf8(const Txt: PUtf8Char): Integer; /// Функция переводит строку в верхний регистр function DBDUtf8ToLower(const Txt: RawUtf8): RawUtf8; /// Функция переводит строку в верхний регистр function DBDUtf8ToUpper(const Txt: RawUtf8): RawUtf8; ///Функция преобразует вещественное число в строку function DBDDoubleToText(const D: Double; const Prec: Integer): RawUtf8; /// Функция читает дату function DBDReadDateUtf8(const Txt: RawUtf8): TDateTime; inline; overload; function DBDReadDateUtf8(const Txt: RawUtf8; out dt: TDateTime): Boolean; overload; function DBDReadDateUtf8(var p:PUTF8Char; out dt: TDateTime): Boolean; overload; /// Функция считывает из строки число начиная с заданной позиции, лидирующие пробельные символы пропускаются, // число может начинаться со знака ("+" или "-"), число может содержать символ разделителя тысяч (" "), в // качестве десятичного знака испольуется либо точка ("."), либо запятая (",") число завершается пробелом либо // знаком препинания (".", ",") //TODO: добавить настоящую обработку символа разделителя тысяч, и отрицательных чисел заключённых в скобки, //символа мантисы "E" function DBDReadFloatUtf8(var p:PUTF8Char; out d: Double): Boolean; function DBDReadFloatUtf8Def(var p:PUTF8Char; const Def: Double=0.0): Double; inline; /// Функция считывает из строки целое число (номер), которое заканчивается любым не цифровым символом function DBDReadNumberUtf8(var p:PUTF8Char; out i: Integer): Boolean; function DBDReadNumberUtf8Def(var p:PUTF8Char; const Def: Integer=0): Integer; inline; /// Функция считывает из строки число, лидирующие пробельные символы пропускаются, // число может начинаться со знака ("+" или "-"), число может содержать символ разделителя тысяч (" "), в // качестве десятичного знака испольуется либо точка ("."), либо запятая (",") число завершается пробелом либо // знаком препинания (".", ",") function DBDUtf8ToDouble(const txt: RawUtf8; out d: Double): Boolean; /// Функция считывает из строки число, лидирующие пробельные символы пропускаются, // число может начинаться со знака ("+" или "-"), число может содержать символ разделителя тысяч (" "), в // качестве десятичного знака испольуется либо точка ("."), либо запятая (",") число завершается пробелом либо // знаком препинания (".", ",") function DBDUtf8ToDoubleDef(const txt: RawUtf8; const Def: Double=0.0): Double; inline; function DBDUtf8ToCurrency(const txt: RawUtf8; out C: Currency): Boolean; function DBDUtf8ToCurrencyDef(const txt: RawUtf8; const Def: Currency): Currency; /// Функция считывает из строки "слово" - фрагмент текста между пробелами, начиная с заданной позиции, // лидирующие пробельные символы пропускаются, function DBDReadWordStr(var p:PUTF8Char; out wrd: RawUTF8): Boolean; /// Функция возвращает true, если строка похожа на Идентификатор - последовательность цифр разделенных // символами "." и/или "-" function DBDValidID(const ID: RawUTF8): Boolean; /// Функция разбивает строку на две по заданному Ansi-символу первому найденному в строке (поиск с начала строки). // Функция фозвращает True, если символ найден в исходной строке function DBDSplitTxt(const txt: RawUtf8; const C: AnsiChar; out Left: RawUtf8; out Right: RawUtf8): Boolean; /// Функция разбивает строку на две по заданному Ansi-символу последнему найденному в строке (поиск с конца строки). // Функция фозвращает True, если символ найден в исходной строке function DBDSplitTxtRight(const txt: RawUtf8; const C: AnsiChar; out Left: RawUtf8; out Right: RawUtf8): Boolean; /// Функция возвращает true, если строка начинается со строки-шаблона и, если PosAfterStTxt<>nil, передаёт указатель на позицию // символа стоящего за началом строки function DBDStartWith(const Txt, StTxt: PUtf8Char; const PosAfterStTxt: PPUtf8Char=nil): Boolean; overload; /// Функция возвращает true, если строка начинается со строки-шаблона function DBDStartWith(const Txt: RawUtf8; StTxt: RawUtf8): Boolean; overload; /// Функция возвращает True если строка Txt начинается с подстроки stTxt без учёта регистра //! txt - указатель на проверяемую строку //! stTxt - указатель на строку-шаблон, с которой должна начинаться проверяемая строка. Предполагается что строка-шаблон // содержит символы в верхнем регистре //! PosAfterStTxt - адрес указателя на символ, находящийся за началом строки (при совпадении со строкой-шаблоном) function DBDStartWithUCase(const Txt, StTxt: PUtf8Char; const PosAfterStTxt: PPUtf8Char=nil): Boolean; overload; /// Функция возвращает True если строка Txt начинается с подстроки stTxt без учёта регистра //! txt - проверяемая строка //! stTxt - строка-шаблон, с которой должна начинаться проверяемая строка. Предполагается что строка-шаблон // содержит символы в верхнем регистре //! SkipLeadingSpaces - игнорировать пробельные символы в начале проверяемой строки function DBDStartWithUCase(const Txt, StTxt: RawUtf8; const SkipLeadingSpaces: Boolean=False): Boolean; overload; /// Функция возвращает true, если строка заканчивается заданным образцом, если задан указатель PosBeforeStTxt, то в него // записывается указатель на символ слева от образца //! txt - указатель на проверяемую строку //! stTxt - указатель на строку-шаблон, с которой должна начинаться проверяемая строка. //! PosBeforeStTxt - адрес указателя на символ, находящийся перед окончанием строки (при совпадении со строкой-шаблоном) function DBDEndWith(const Txt, StTxt: PUtf8Char; const PosBeforeStTxt: PPUtf8Char=nil): Boolean; overload; /// Функция возвращает true, если строка заканчивается заданным образцом //! txt - проверяемая строка //! stTxt - строка-шаблон, с которой должна начинаться проверяемая строка. Предполагается что строка-шаблон function DBDEndWith(const Txt, StTxt: RawUtf8): Boolean; overload; inline; /// Функция возвращает True если строка Txt заканчивается подстрокой stTxt без учёта регистра //! txt - проверяемая строка //! stTxt - строка-шаблон, с которой должна начинаться проверяемая строка. Предполагается что строка-шаблон // содержит символы в верхнем регистре //! SkipTrailingSpaces - игнорировать пробельные символы в конце проверяемой строки function DBDEndWithUCase(const Txt, StTxt: RawUtf8; const SkipTrailingSpaces: Boolean=False): Boolean; overload; function DBDEndWithUCase(const Txt, StTxt: PUtf8Char; const SkipTrailingSpaces: Boolean=False): Boolean; overload; /// Функция возвращает True если строка Txt заканчивается подстрокой stTxt без учёта регистра //! txt - проверяемая строка //! stTxt - строка-шаблон, с которой должна начинаться проверяемая строка. Предполагается что строка-шаблон // содержит символы в верхнем регистре //! PosAfterStTxt - адрес указателя на символ, находящийся за началом строки (при совпадении со строкой-шаблоном) function DBDEndWithUCase(const Txt, StTxt: PUtf8Char; const PosBeforeStTxt: PPUtf8Char=nil): Boolean; overload; /// Функция возвращает указатель на предыдущий символ строки. Функция не безопасна, так как не может проверит выход за первый символ строки function DBDPrevSymbol(const Txt: PUtf8Char): PUtf8Char; /// Функция выделяет первую подстроку заключённую в круглые скобки и возвращает номер позиции следом за закрывающей скобкой // Txt - обрабатываемая строка // Sub - строка содержавшаяся в скобках function DBDTextWithinParenthesis(const Txt: RawUtf8; out Sub: RawUtf8): Boolean; overload; /// Функция выделяет первую построку заключённую в круглые скобки и, в случае успеха, // возвращает выделеннкю подстроку и перемещает уазатель на символ следом за закрывающей скобкой //! txt - указатель первый символ проверяемой строки function DBDTextWithinParenthesis(var Txt: PUtf8Char): RawUtf8; overload; /// Функция выделяет первую построку заключённую в скобки и, в случае успеха, // возвращает выделеннкю подстроку и перемещает уазатель на символ следом за закрывающей скобкой //! txt - проверяемая строка //! ParOpen - открывающая скобка //! ParClose - закрывающия скобка function DBDTextWithinParenthesis(var Txt: RawUtf8; const ParOpen, ParClose: RawUtf8): RawUtf8; overload; /// Функция разбирает строку на массив строк используя строку-разделитель function DBDTextToDynArray(const Txt: PUtf8Char; const Len: Integer; const Delimiter: RawUtf8=#13#10): TRawUtf8DynArray; overload; function DBDTextToDynArray(const Txt: RawUtf8; const Delimiter: RawUtf8=#13#10): TRawUtf8DynArray; overload; /// Функция заменяет в строке все вхождения образца на новое значение function DBDReplaceTextAll(Txt, Old, New: RawUtf8): RawUtf8; /// функция возвращает указатель на первый непробельный символ строки function DBDSkipBlankChars(const P: PUtf8Char): PUtf8Char; /// Функция разбивает строку с формулой на множители // строка имеет вид: [=]множитель{*множитель} // множитель - число | (выражение) // выражение не может быть функцией function DBDFactorsDisassemble(const Txt: RawUtf8): TRawUtf8DynArray; overload; /// Функция для обработки строки содержащий формулу вида [=]множитель{*множитель} // Функция выделяет из строки множитель слева и возвращает его в качестве результата, если указан isSimple, то // он устанавливается в true если множитель является числом. //Если множитель выделен то входная строка усекается слева до символа после * //TODO: выполнить реализацию function DBDFactorsDisassemble(var Txt: PUtf8Char; const isSimple: PBoolean=nil): RawUtf8; overload; function DBDFormulaIDisassemble(var Txt: PUtf8Char; out Params: TRawUtf8DynArray): Integer; type TDBDTrimOption = (dbdtoLeft, dbdtoRight, dbdtoBoth); function DBDTrimStr(const p: PUTF8Char; const Option: TDBDTrimOption=dbdtoBoth; const LenStr: integer=0): RawUTF8; overload; function DBDTrimStr(const txt: RawUTF8; const Option: TDBDTrimOption=dbdtoBoth): RawUTF8; overload; /// Функция обрезает с начала и/или конца строку, если она начинается и/или оканчивается на заданную подстроку // txt - обрабатываемая строка // sub - подстрока, с которой должна начинаться и/или заканчиваться обрабатываемая строка // Option - опции обрезания function DBDTrimStr(const txt, sub: PUtf8Char; const Option: TDBDTrimOption): RawUTF8; overload; /// Функция обрезает с начала и/или конца строку, если она начинается и/или оканчивается на заданную подстроку // txt - указатель на первый символ обрабатываемой строки // sub - указатель на первый символ подстроки, с которой должна начинаться и/или заканчиваться обрабатываемая строка // Option - опции обрезания function DBDTrimStr(const txt, sub: RawUtf8; const Option: TDBDTrimOption): RawUTF8; overload; /// функция удаляет заданный символ в начале или/или конце строки и возвращает полученную строку function DBDTrimAnsiChar(const Txt: RawUtf8; const C: AnsiChar; const Side:TDBDStringPart): RawUtf8; /// функция удаляет подстроку из строки. Если установален флаг All, то удаляются все вхождения подстоки, иначе - первое function DBDRemoveSubstr(const txt: PUtf8Char; const sub: RawUtf8; const All: Boolean=True): RawUtf8; overload; /// функция удаляет подстроку из строки. Если установален флаг All, то удаляются все вхождения подстоки, иначе - первое function DBDRemoveSubstr(const Txt: RawUtf8; const sub: RawUtf8; const All: Boolean=True): RawUtf8; inline; overload; /// function DBDRoundUtf8(const Txt: RawUtf8; const Prec: Integer): RawUtf8; type /// Опции для преобразования строки // dbdtnoUpper - преобразовать к верхнему регистру // dbdtnoLower - преобразовать к нижнему регистру // dbdtnoRemoveExtraChars - удалить лишние символы // dbdtnoExtraCharsToSpace - заменить лишние символы на пробелы // dbdtnoRemoveExtraBlanks - удалить лишние пробельные символы // dbdtnoBlankCharsToSpace - заменить все пробельные символы на символ пробела TDBDTextNormalizationOption = (dbdtnoUpper, dbdtnoLower, dbdtnoRemoveExtraChars, dbdtnoExtraCharsToSpace, dbdtnoBlankCharsToSpace, dbdtnoRemoveExtraBlanks); TDBDTextNormalizationOptions = set of TDBDTextNormalizationOption; const dbdTNODefault: TDBDTextNormalizationOptions=[dbdtnoUpper, dbdtnoRemoveExtraBlanks, dbdtnoExtraCharsToSpace]; // dbdTNODust: TDBDTextNormalizationOptions=[dbdtnoRemoveExtraBlanks, dbdtnoExtraCharsToSpace,dbdtnoBlankCharsToSpace]; dbdTNODust: TDBDTextNormalizationOptions=[dbdtnoExtraCharsToSpace,dbdtnoBlankCharsToSpace]; /// Используется для сравнения строк без учёта регистра (строка приводится к верхнему регистру, лишние пробелы удаляются dbdTNCompareUCase: TDBDTextNormalizationOptions=[dbdtnoUpper, dbdtnoRemoveExtraBlanks]; ///// функция преобразует текст к нормализованому виду для сравнения с другим нормализованным текстом или для получения CRC //function DBDTextNormalizationHum(const Txt: RawUTF8; const NormOpt: TDBDTextNormalizationOptions=[]): RawUTF8; /// функция преобразует текст к нормализованому виду для сравнения с другим нормализованным текстом или для получения CRC function DBDTextNormalization(const Txt: RawUTF8; const NormOpt: TDBDTextNormalizationOptions=[]): RawUTF8; /// функция возвращает True если строки одинаковые. Присравнении строки нормализуются с опцией dbdTNCompareUCase function DBDisEqualTexts(const Txt1, Txt2: RawUtf8): Boolean; /// функция возвращет true если строка заданная указателем PP содержит символы допустимые для идентификаторв (без цифр) // если ExtLetter не установлен, то строка может содержать только символы латинского алфавита и символ подчёркивания // если ExtLetter установлен, то добавляются символы входящие в dbdLetterCategory function DBDIsValidIdentStart(var PP: PUtf8Char; const ExtLetters: Boolean=False): Boolean; /// функция возвращет true если строка заданная указателем PP содержит символы допустимые для идентификаторв (без цифр) // если ExtLetter не установлен, то строка может содержать только символы латинского алфавита и символ подчёркивания // если ExtLetter установлен, то добавляются символы входящие в dbdLetterCategory function DBDIsValidIdentChar(var PP: PUtf8Char; const ExtLetters: Boolean=False): Boolean; /// function DBDGetIdent(PP: PUtf8Char; const ExtLetters: Boolean=False): PUtf8Char; implementation function DBDRawUtf8ToUcs4Array(const txt: RawUtf8; const ToUpper: Boolean): TUcs4DynArray; var p: PUtf8Char; l,i: Integer; c: Ucs4CodePoint; begin l:=Length(Txt); SetLength(Result,l); if l=0 then Exit; p:=@txt[1]; if ToUpper then c:=DBDUTF8ToUCS4Upper(p) else c:=DBDUTF8ToUCS4(p); Result[0]:=c; i:=1; while (p<>nil) and (p^<>#0) do begin if ToUpper then c:=DBDUTF8ToUCS4Upper(p) else c:=DBDUTF8ToUCS4(p); Result[i]:=c; Inc(i); end; if i>0 then SetLength(Result,i); end; function DBDTextToDynArray(const Txt: RawUtf8; const Delimiter: RawUtf8=#13#10): TRawUtf8DynArray; var p: PUtf8Char; begin p:=@Txt[1]; Result:=DBDTextToDynArray(p, Length(Txt),Delimiter); end; function DBDTextToDynArray(const Txt: PUtf8Char; const Len: Integer; const Delimiter: RawUtf8=#13#10): TRawUtf8DynArray; overload; var p,pb,pd,pe: PUtf8Char; l: Integer; A: TRawUtf8DynArray; DA: TDynArray; cnt: Integer; r: RawUtf8; begin SetLength(Result,0); l:=Length(Delimiter); if (Txt=nil) or (Txt^=#0) or (l=0) or (Len<=0) then Exit; DA.Init(TypeInfo(TRawUtf8DynArray), A, @cnt); p:=Txt; pd:=@Delimiter[1]; pe:=p+Len; repeat pb:=p; while p<pe do begin if (p^=pd^) and CompareMemSmall(p,pd,l) then Break; Inc(p); end; FastSetString(r,pb,p-pb); DA.Add(r); if p>=pe then Break; Inc(p,l); until p>=pe; DA.CopyTo(Result); end; function DBDSkipBlankChars(const P: PUtf8Char): PUtf8Char; var c: Ucs4CodePoint; cs: Integer; begin Result:=P; if (P=nil) or (P^=#0) then Exit; repeat cs:=PUCUUTF8CharSteps[Result^]; c:=DBDUTF8ToUCS4(Result); until (c=0) or (not DBDisWhiteSpace(c)); if c<>0 then begin Result:=DBDPrevSymbol(Result); end; end; function DBDCopyUtf8(const txt: PUtf8Char; const Count: Integer): RawUtf8; var p: PUtf8Char; l: Integer; begin Result:=''; if (txt=nil) or (txt^=#0) or (Count<1) then Exit; DBDPositionToPointer(txt,Count, p); if p=nil then Exit; l:=p-txt+1; if p^=#0 then Dec(l); // l:=p-txt+1; if p^<>#0 then Dec(l); FastSetString(Result,txt,l); end; function DBDCopyUtf8(const txt: RawUtf8; const Start, Count: Integer): RawUtf8; var p: PUtf8Char; begin if txt='' then Result:='' else begin p:=DBDPositionToPointer(txt,Start); Result:=DBDCopyUtf8(p,Count); end; end; function DBDCharCount(const PStart: PUtf8Char; const PEnd: PUtf8Char=nil): Integer; var p,p1: PUtf8Char; c: Ucs4CodePoint; begin if (PStart=nil) or (Ord(PStart^)=0) then Result:=-1 else begin if PEnd=nil then p1:=PStart+2147483647 // максимальный размер строки 2Гб else p1:=PEnd; if p1<PStart then Result:=-2 else if p1=PStart then Result:= 0 else begin Result:=1; p:=PStart; c:=DBDUTF8ToUCS4(p); while (p<p1) and (c>0) do begin c:=DBDUTF8ToUCS4(p); Inc(Result); end; if c=0 then Dec(Result); // исключаем символ конца строки ($00) end; end; end; function DBDPositionToPointer(const txt: RawUtf8; const PosNo: Integer): PUtf8Char; var p: PUtf8Char; begin Result:=nil; if (txt='') or (PosNo<1) then Exit; p:=@txt[1]; Result:= DBDPositionToPointer(p, PosNo); end; function DBDPositionToPointer(const txt: PUtf8Char; const PosNo: Integer): PUtf8Char; var p: PUtf8Char; c: Ucs4CodePoint; i: Integer; begin Result:=nil; if (txt=nil) or (Txt^=#0) or (PosNo<1) then Exit; if PosNo=1 then begin Result:=Txt; Exit; end else begin p:=txt; i:=1; c:=DBDUTF8ToUCS4(p); while (c>0) do begin Inc(i); if i=PosNo then begin Break; end; c:=DBDUTF8ToUCS4(p); end; if (i<PosNo) or (c=0) then Result:=nil else Result:=p; end; end; function DBDPositionToPointer(const txt: PUtf8Char; const PosNo: Integer; out oTxt: PUtf8Char): Boolean; var p: PUtf8Char; c: Ucs4CodePoint; i: Integer; begin oTxt:=txt; Result:=(txt<>nil) and (PosNo>0) and (Txt^<>#0); if (not Result) or (PosNo=1) then Exit; p:=txt; i:=1; c:=DBDUTF8ToUCS4(p); while (c>0) do begin Inc(i); if i=PosNo then Break; c:=DBDUTF8ToUCS4(p); end; oTxt:=p; Result:=(DBDUTF8ToUCS4(p)<>0); end; function DBDFindChar(const txt: PUTF8Char; const C: AnsiChar): PUtf8Char; overload; var c0: Ucs4CodePoint; begin c0:=Ord(C); Result:=DBDFindChar(txt, c0); end; function DBDFindChar(const txt: PUTF8Char; const USC4: Ucs4CodePoint): PUtf8Char; overload; var c: Ucs4CodePoint; p,p0: PUtf8Char; begin Result:=nil; if (txt=nil) or (Ord(txt^)=$0) or (USC4=$0) then Exit; p:=txt; repeat p0:=p; c:=DBDUTF8ToUCS4(p); if c=USC4 then begin Result:=p0; Break; end; until (c=0) or (p=nil); end; function DBDFindChar(const txt: RawUtf8; const USC4: Ucs4CodePoint; const Start: Integer=1; const CharPtr: PPUtf8Char=nil): Integer; var p,p1: PUtf8Char; begin Result:=-1; if (txt='') or (USC4=0) or (Start<1) or (Start>=Length(txt)) then Exit; Result:=-2; p:=DBDPositionToPointer(txt,Start); if (p=nil) or (Ord(p^)=$0) then Exit; Result:= 0; p1:=DBDFindChar(p,USC4); if (p1=nil) or (Ord(p1^)=$0) then Exit; if CharPtr<>nil then CharPtr^:=p1; Result := DBDCharCount(p,p1) + Start; end; function DBDFindCharCategory(const p: PUtf8Char; const Categories: array of Integer): PUtf8Char; var t,v: PUtf8Char; c,i: Integer; begin Result:=nil; if (p=nil) or (p^=#0) then Exit; t:=p; while (t<>nil) and (t^<>#0) do begin v:=t; c:=DBDGetCategory(t); for i := Low(Categories) to High(Categories) do begin if Categories[i]=c then begin Result:=v; Exit; end; end; end; end; function DBDFindCharCategory(const p: PUtf8Char; const Categories: TdbdPUCUCategories): PUtf8Char; var t: PUtf8Char; c: Integer; begin Result:=nil; if (p=nil) or (p^=#0) then Exit; t:=p; while (t<>nil) and (t^<>#0) do begin Result:=t; c:=DBDGetCategory(t); if c in Categories then Exit; end; end; function FindEndStr(const txt: PUTF8Char; const MaxLen: Cardinal): PUtf8Char; var i: Integer; p: PUtf8Char; begin Result:=txt; if txt=nil then Exit; for i := 1 to 1024 do if Result^=#0 then Exit else Inc(Result); Result:=nil; end; function DBDFindDigit(const txt: RawUtf8; const Start: Integer=1; const CharPtr: PPUtf8Char=nil): Integer; overload; var p,p0: PUtf8Char; i,l,n: Integer; begin Result:=0; if txt='' then Result:=-11 else if Start<1 then Result:=-12 else if Start>Length(txt) then Result:=-12 else begin for i := Start to Length(txt) do if Txt[i] in ['0'..'9'] then begin Result:=i; Break; end end; end; function DBDFindSubstr(const txt: PUtf8Char; const sub: RawUtf8; const Start: Integer=1; const After: Boolean=True): PUtf8Char; overload; var p,p0: PUtf8Char; i,l,n: Integer; begin Result:=nil; if (txt=nil) or (txt^=#0) then Exit; l:=Length(sub); if l=0 then Exit; p0:=@Sub[1]; p:=PosChar(txt, sub[1]); if (p=nil) or (p=#0) then Exit; while (p<>nil) and (p<>#3) do begin if CompareMemSmall(p, p0, l) then begin if After then Result:=p+l else Result:=p; Break; end; Inc(p); p:=PosChar(p, sub[1]); end; end; function DBDFindSubstr(const txt, sub: RawUtf8; const Start: Integer=1; const After: Boolean=True): PUtf8Char; var i,iSt: Integer; var p,p0: PUtf8Char; begin Result:=nil; if (txt='') or (sub='') then Exit; if Start=1 then iSt:=1 else begin p:=@txt[1]; DBDPositionToPointer(p, Start, p0); iSt:=p0-p+1; end; i:=PosEx(sub,txt,iSt); if i<1 then Exit; if After then Result:=@txt[i+Length(sub)] else Result:=@Txt[i]; //TODO: Переписать функцию end; function DBDCompareStringI(const txt: PUtf8Char; const S: string): Boolean; overload; var r: RawUtf8; begin Result := (Length(S)>0) and (txt<>nil) and (txt^<>#0); if not Result then Exit; r:=StringToUtf8(S); result:=StrIComp(txt, @r[1])=0; end; function DBDCompareUtf8U(const txt: PUtf8Char; const UpperTxt: RawUtf8): Boolean; overload; var au: TUcs4DynArray; begin Result := (Length(UpperTxt)>0) and (txt<>nil) and (txt^<>#0); if not Result then Exit; au:=DBDRawUtf8ToUcs4Array(UpperTxt); Result:=DBDCompareUtf8U(txt, au); end; function DBDCompareUtf8U(const txt: PUtf8Char; const UpperUcs4Array: TUcs4DynArray): Boolean; var i: Integer; p: PUtf8Char; c: Ucs4CodePoint; begin Result:=False; if (txt=nil) or (txt^=#0) or (Length(UpperUcs4Array)=0) Then Exit; p:=txt; i:=Low(UpperUcs4Array); while (p<>nil) and (p^<>#0) and (i<=High(UpperUcs4Array)) do begin c:=DBDUTF8ToUCS4Upper(p); if c<>UpperUcs4Array[i] then Exit; Inc(i); end; Result:=(i>High(UpperUcs4Array)); end; function DBDFindSubstrU(const txt, sub: RawUtf8; const Start: Integer; const After: Boolean): PUtf8Char; var lSub, lTxt: Integer; var p,p0: PUtf8Char; arr: TUcs4DynArray; begin Result:=nil; lSub:=Length(sub); lTxt:=Length(txt); if (lTxt=0) or (lSub=0) or (lTxt<lSub) then Exit; p0:=@txt[1]; if DBDPositionToPointer(p0, Start, p) then begin if (lTxt-(p-p0)<lSub) then Exit; arr:=DBDRawUtf8ToUcs4Array(sub); while (p<>nil) and (p^<>#0) do begin if DBDCompareUtf8U(p,arr) then begin if After then Result:=p+lSub else Result:=p; Exit; end; end; end; end; function DBDFindSubstrUCase(const txt, sub: RawUtf8; const Start: Integer=1; const After: Boolean=False; const PP: PPUtf8Char=nil): Integer; var lSub, lTxt: Integer; var p,p0: PUtf8Char; arr: TUcs4DynArray; begin Result:=-1; lSub:=Length(sub); lTxt:=Length(txt); if (lTxt=0) or (lSub=0) or (lTxt<lSub) then Exit; p0:=@txt[1]; if DBDPositionToPointer(p0, Start, p) then begin if (lTxt-(p-p0)<lSub) then Exit; arr:=DBDRawUtf8ToUcs4Array(sub); while (p<>nil) and (p^<>#0) do begin if DBDCompareUtf8U(p,arr) then begin if After then p:=p+lSub; Result:=DBDCharCount(p0,p)+1; if PP<>nil then PP^:=p; Exit; end; end; end; end; { Функция ищет подстроку aSub в строке aText, начиная с байтовой позиции aFromPos, двигаясь К НАЧАЛУ строки. Возвращает 1-based байтовую позицию начала подстроки, или 0, если ничего не найдено. } function DBDFindSubstrBackward(const aText, aSub: RawUtf8; aFromPos: Integer): Integer; var pTextStart, pStartSearch: PUtf8Char; SubLen, TextLen: Integer; begin Result := 0; TextLen := Length(aText); SubLen := Length(aSub); if (TextLen = 0) or (SubLen = 0) or (SubLen > TextLen) then Exit; if aFromPos > TextLen then aFromPos := TextLen; if aFromPos < SubLen then Exit; pTextStart := Pointer(aText); pStartSearch := pTextStart + (aFromPos - SubLen); while pStartSearch >= pTextStart do begin // В mORMot 2 CompareMem реализована на чистом ассемблере и работает молниеносно if CompareMem(pStartSearch, Pointer(aSub), SubLen) then begin Result := (pStartSearch - pTextStart) + 1; Exit; end; Dec(pStartSearch); end;end; function DBDFindSubStrBackwardAnsi(const aText, aSub: AnsiString; aFromPos: Integer): Integer; var pTextStart, pStartSearch: PAnsiChar; SubLen, TextLen: Integer; begin Result := 0; TextLen := Length(aText); SubLen := Length(aSub); // Базовые проверки на пустые строки и корректность длин if (TextLen = 0) or (SubLen = 0) or (SubLen > TextLen) then Exit; // Ограничиваем начальную позицию поиска реальным размером строки if aFromPos > TextLen then aFromPos := TextLen; // Если позиция меньше длины искомой подстроки, поиск невозможен if aFromPos < SubLen then Exit; pTextStart := PAnsiChar(aText); // Указатель на точку старта поиска (сдвигаемся на aFromPos, но назад на длину подстроки, // чтобы она гарантированно уместилась в границах проверяемого диапазона) pStartSearch := pTextStart + (aFromPos - SubLen); // Двигаемся побайтово к началу строки while pStartSearch >= pTextStart do begin // Сравниваем блоки памяти без использования mORMot (чистый WinAPI/System) if CompareMem(pStartSearch, PAnsiChar(aSub), SubLen) then begin // Вычисляем 1-based позицию совпадения Result := (pStartSearch - pTextStart) + 1; Exit; end; Dec(pStartSearch); end; end; function DBDFirstNonSpaceSymbol(const txt: RawUtf8; const PP: PPUtf8Char=nil): Integer; var p: PUtf8Char; cs,i: integer; c: Ucs4CodePoint; begin if (Txt='') then Result :=-1 else begin p:=@txt[1]; i:=0; Result:=0; while (p<>nil) and (p^<>#0) do begin Inc(i); cs:=PUCUUTF8CharSteps[p^]; c:=DBDUTF8ToUCS4(p); if not DBDisWhiteSpace(c) then begin Result:=i; Dec(p,cs); Break; end; end; if (Result<>0) and (PP<>nil) then pp^:=p; end; end; function DBDLastNonSpaceSymbol(const txt: RawUtf8): PUtf8Char; overload; var p,p0: PUtf8Char; begin if (Txt='') then Result:=nil else begin p0:=@txt[1]; p:=@Txt[Length(txt)]; Inc(p); while (p>p0) do begin if not DBDisWhiteSpace(p, False) then begin Result:=p; Break; end; p:=DBDPrevSymbol(p); end; end; end; function DBDLastNonSpaceSymbol(const txt: RawUtf8; const PP: PPUtf8Char=nil): Integer; var p,p0: PUtf8Char; begin if (Txt='') then Result :=-1 else begin p0:=@txt[1]; p:=@Txt[Length(txt)]; Inc(p); while (p>p0) do begin if not DBDisWhiteSpace(p, False) then begin Result:=DBDCharCount(p0, p)+1; Exit; end; p:=DBDPrevSymbol(p); end; end; end; function DBDTrimAnsiChar(const Txt: RawUtf8; const C: AnsiChar; const Side:TDBDStringPart): RawUtf8; var p1,p2: PUtf8Char; l:Integer; f: Boolean; begin Result:=Txt; if Length(Txt)=0 then Exit; p1:=@Txt[1]; p2:=@Txt[Length(Txt)]; if (Side=dbdSPLeft) or (Side=dbdSPBoth) then while (Ord(p1^)<>0) and (p1^=C) do Inc(p1); l:=p2-p1+1; f:= @Txt[1]<>p1; if (Ord(p1^)<>0) and ((Side=dbdSPRight) or (Side=dbdSPBoth)) then begin while (p2>=p1) and (p2^=C) do Dec(p2); l:=p2-p1+1; end; if (l=0) then Result:='' else FastSetString(Result,p1, l); end; function DBDTrimStr(const txt, sub: PUtf8Char; const Option: TDBDTrimOption): RawUTF8; var p: PUtf8Char; p1: PByte; EndPos, StartPos: PUtf8Char; buf:TSynTempBuffer; c: Cardinal; r: RawUtf8; begin if (txt=nil) or (txt^=#0) then Result:='' else if (sub=nil) or (sub^=#0) then begin // строка возвращается без изменений p1:=PByte(Txt); FromVarString(p1, buf); buf.Done; FastSetString(Result, buf.buf, buf.len); end else begin if Option in [dbdtoLeft, dbdtoBoth] then begin if not DBDStartWithUCase(txt, sub, @StartPos) then StartPos:=txt; end else StartPos:=Txt; if Option in [dbdtoRight, dbdtoBoth] then begin if not DBDEndWith(txt, sub, @EndPos) then EndPos:=DBDLastChar(txt); end else EndPos:=DBDLastChar(txt); c:=DBDUTF8ToUCS4(EndPos); if EndPos>StartPos then begin c:=EndPos-StartPos; FastSetString(Result,StartPos,c) end else Result:=''; end; end; function DBDTrimStr(const txt, sub: RawUtf8; const Option: TDBDTrimOption): RawUTF8; var ptxt, psub, pStart: PUtf8Char; f: Boolean; begin Result:=txt; if (txt='') or (sub='') then Exit; ptxt:=@txt[1]; psub:=@sub[1]; Result:=DBDTrimStr(ptxt, psub, Option); end; function DBDRemoveSubstr(const txt: PUtf8Char; const sub: RawUtf8; const All: Boolean=True): RawUtf8; var p,p0: PUtf8Char; r: RawUtf8; begin if (txt<>nil) and (txt^<>#0) and (sub<>'') then begin p0:=txt; p:=DBDFindSubstr(txt,sub,1,false); if (p=nil) or (p^=#0) then begin FastSetString(Result, txt, DBDSizeUtf8(txt)); end else begin FastSetString(Result, p0, p-p0); if All then begin p:=p+Length(sub); Result:=Result+DBDRemoveSubstr(p,sub,All); end; end; end else begin FastSetString(Result, txt, DBDSizeUtf8(txt)); end; end; function DBDRemoveSubstr(const txt: RawUtf8; const sub: RawUtf8; const All: Boolean=True): RawUtf8; begin Result:=txt; if (txt='') or (sub='') then Exit; Result:=DBDRemoveSubstr(@Txt[1], sub,All); end; function DBDTextWithinParenthesis(var Txt: PUtf8Char): RawUtf8; overload; var p, pOpen: PUtf8Char; c: Ucs4CodePoint; lvl: Integer; begin Result:=''; p:=Txt; lvl:=0; while (p<>nil) and (p^<>#0) do begin c:=DBDUTF8ToUCS4(p); if c=$28 then begin // открывающая скобка найдена pOpen:=p; Inc(lvl); end else if c=$29 then begin // закрывающая скобка найдена Dec(lvl); if lvl=0 then begin // закрывающая соответствует открывающей FastSetString(Result, POpen, p-POpen-1); Txt:=p; Break; end else if lvl<0 then Break; // открывающей скобки ещё не было end else if c<=0 then begin // ошибочный символ Break; end; end; end; function DBDTextWithinParenthesis(const Txt: RawUtf8; out Sub: RawUtf8): Boolean; var p: PUtf8Char; begin Result:=False; if Txt='' then Exit; p:=@Txt[1]; Sub:=DBDTextWithinParenthesis(p); Result:=(p<>Txt[1]); end; function DBDTextWithinParenthesis(var Txt: RawUtf8; const ParOpen, ParClose: RawUtf8): RawUtf8; overload; var p,p0,p1,pl: PUtf8Char; r,r1: RawUtf8; begin Result:=''; if (Txt='') or (ParOpen='') or (ParClose='') then Exit; p:=@Txt[1]; pl:=@Txt[Length(txt)]; p0:=DBDFindSubstr(p,ParOpen,1,True); if (p0=nil) or (p0=#0) then Exit; // открывающая скобка не найдена p1:=DBDFindSubstr(p0,ParClose,1,True); if (p1=nil) or (p1=#0) then Exit; // закрывающая скобка не найдена FastSetString(r, p0, p1-p0-Length(ParClose)); Result:=r; FastSetString(r, p, p0-p-Length(ParOpen)); FastSetString(r1, p1, pl-p1+1); txt:=r+r1; end; function DBDSplitTxt(const txt: RawUtf8; const C: AnsiChar; out Left: RawUtf8; out Right: RawUtf8): Boolean; var p,pC: PUtf8Char; l: Integer; begin Result:=False; if txt='' then Exit; p:=@txt[1]; pC:=DBDFindChar(p, Ord(C)); if pC=nil then Exit; l:=pC-p; FastSetString(Left, p, l); Inc(pC); if Length(txt)-l >0 then FastSetString(Right, pC, Length(txt)-l-1) else Right:=''; Result:=True; end; function DBDSplitTxtRight(const txt: RawUtf8; const C: AnsiChar; out Left: RawUtf8; out Right: RawUtf8): Boolean; var p,p0: PUtf8Char; l: Integer; begin Result:=False; if txt='' then Exit; p:=DBDLastChar(txt); p0:=@txt[1]; while p>=p0 do begin if p^=C then Break; p:=DBDPrevSymbol(p); end; Result:=(p>=p0); if Result then begin l:=p-p0; FastSetString(Left, p0, l); Inc(p); if Length(txt)-l>0 then FastSetString(Right, p, Length(txt)-l-1) else Right:=''; end; end; function DBDStartWith(const Txt, StTxt: PUtf8Char; const PosAfterStTxt: PPUtf8Char=nil): Boolean; var p1,p2: PUtf8Char; begin if (Txt=nil) or (Ord(Txt^)=0) or (StTxt=nil) or (Ord(StTxt^)=0) then Result:=False else begin p1:=Txt; p2:=StTxt; while (Ord(p2^)<>0) and (Ord(p1^)<>0) do begin Result:=(Ord(p1^)=Ord(p2^)); if not Result then Exit; Inc(p1); Inc(p2); end; Result:=(Ord(p2^)=0); if Result and (PosAfterStTxt<>nil) then PosAfterStTxt^:=p1; end; end; function DBDStartWith(const Txt: RawUtf8; StTxt: RawUtf8): Boolean; begin if (Txt='') or (StTxt='') then Result:=False else Result:=DBDStartWith(@Txt[1], @StTxt[1]); end; function DBDStartWithUCase(const Txt, StTxt: PUtf8Char; const PosAfterStTxt: PPUtf8Char): Boolean; var c1,c2: Ucs4CodePoint; p1,p2: PUtf8Char; begin if (Txt=nil) or (Txt^=#0) or (StTxt=nil) or (StTxt^=#0) then Result:=False else begin p1:=Txt; p2:=StTxt; c1:=DBDUTF8ToUCS4(p1); c2:=DBDUTF8ToUCS4(p2); while (c1<>0) and (c2<>0) do begin Result := (DBDUCS4ToUpper(c1)=c2); if not Result then Exit; c1:=DBDUTF8ToUCS4(p1); c2:=DBDUTF8ToUCS4(p2); end; Result:=(p2^=#0) or (p2=nil); if Result and (PosAfterStTxt<>nil) then PosAfterStTxt^:=p1; end; end; function DBDStartWithUCase(const Txt, StTxt: RawUtf8; const SkipLeadingSpaces: Boolean): Boolean; var p: PUtf8Char; begin if (Txt='') or (StTxt='') then Result:=False else begin if SkipLeadingSpaces then p:=DBDFindNonSpaceChar(@Txt[1]) else p:=@Txt[1]; Result:=DBDStartWithUCase(p, @StTxt[1]); end; end; function DBDEndWith(const Txt, StTxt: PUtf8Char; const PosBeforeStTxt: PPUtf8Char=nil): Boolean; var p1,p2: PUtf8Char; i: Integer; begin if (Txt=nil) or (Ord(Txt^)=$0) or (StTxt=nil) or (Ord(StTxt^)=$0) then Result:=False else begin Result:=False; p1:=DBDLastChar(txt); p2:=DBDLastChar(StTxt); if PosBeforeStTxt<>nil then PosBeforeStTxt^:=p1; i:=p2-StTxt+1; if i<=(p1-txt+1) then begin while i>0 do begin if Ord(p1^)<>Ord(p2^) then Break; Dec(i); Dec(p1); Dec(p2); end; Result:=(i=0); if Result and (PosBeforeStTxt<>nil) then PosBeforeStTxt^:=p1; end; end; end; function DBDEndWith(const Txt, StTxt: RawUtf8): Boolean; begin {$IFDEF DEBUG} Assert((Txt<>'') and (StTxt<>''), 'Проверяемая строка и образец не могут быть пустыми'); {$ENDIF} Result := (Txt<>'') and (StTxt<>'') and DBDEndWith(@Txt[1], @StTxt[1], nil); end; function DBDEndWithUCase(const Txt, StTxt: PUtf8Char; const PosBeforeStTxt: PPUtf8Char=nil): Boolean; overload; var p1,p2: PUtf8Char; i,j: Integer; begin if (Txt=nil) or (Ord(Txt^)=$0) or (StTxt=nil) or (Ord(StTxt^)=$0) then Result:=False else begin Result:=False; i:=DBDSizeUTF8(Txt); j:=DBDSizeUTF8(StTxt); i:=i-j; if i<0 then Exit; p1:=Txt+i; p2:=StTxt; if PosBeforeStTxt<>nil then PosBeforeStTxt^:=DBDPrevSymbol(p1); while (p1^<>#0) and (p2^<>#0) do begin Result:= (p1^=p2^); if not Result then Exit; Inc(p1); Inc(p2); end; Result:=(p2^=#0) and (p1^=#0); end; end; function DBDEndWithUCase(const Txt, StTxt: RawUtf8; const SkipTrailingSpaces: Boolean=False): Boolean; var p0,p1,p2: PUtf8Char; begin Result:=(txt<>'') and (StTxt<>''); if not Result then Exit; p1:=@Txt[Length(txt)]; p0:=@txt[1]; p2:=@StTxt[1]; if SkipTrailingSpaces then begin // пропускаем концевые пробелы Inc(p1); while (p1>p0) do begin if not DBDisWhiteSpace(p1, False) then Break; p1:=DBDPrevSymbol(p1); end; end; p1:=p1-Length(StTxt)+1; Result:=(p1>p0); if not Result then Exit; Result:=DBDEndWithUCase(p1,p2,nil); end; function DBDEndWithUCase(const Txt, StTxt: PUtf8Char; const SkipTrailingSpaces: Boolean=False): Boolean; overload; var p, pTxt, pStTxt: PUtf8Char; begin Result:=(Txt<>nil) and (Txt^<>#0) and (StTxt<>nil) and (StTxt^<>#0); if not Result then Exit; pTxt:=DBDLastChar(Txt); if SkipTrailingSpaces and DBDisWhiteSpace(pTxt, False) then begin // удаляем хвостовые пробелы Txt while pTxt>Txt do begin if not DBDisWhiteSpace(pTxt, False) then Break; pTxt:=DBDPrevSymbol(pTxt); end; if pTxt=Txt then Result:= (not DBDisWhiteSpace(pTxt, False)); // проверяем 1й символ Txt на пробел if not Result then Exit; end; pStTxt:=DBDLastChar(StTxt); if SkipTrailingSpaces and DBDisWhiteSpace(pStTxt, False) then begin // удаляем хвостовые пробелы StTxt while pStTxt>StTxt do begin if not DBDisWhiteSpace(pStTxt, False) then Break; pStTxt:=DBDPrevSymbol(pStTxt); end; if pStTxt=StTxt then Result:= (not DBDisWhiteSpace(pStTxt, False)); // проверяем 1й символ StTxt на пробел if not Result then Exit; end; while (pStTxt>StTxt) and (pTxt>Txt) do begin // сравниваем символы с конца строк if DBDUTF8ToUCS4Upper(pStTxt)<>DBDUTF8ToUCS4Upper(pTxt) then begin Result:=False; Exit; end; pTxt:=DBDPrevSymbol(pTxt); pStTxt:=DBDPrevSymbol(pStTxt); end; // проверяем 1й символ StTxt, при условии что есть что проверять в Txt if (pTxt>=Txt) and (pStTxt=StTxt) then Result:=DBDUTF8ToUCS4Upper(pStTxt)<>DBDUTF8ToUCS4Upper(pTxt) else Result:=False; end; function DBDPrevSymbol(const Txt: PUtf8Char): PUtf8Char; var p: PUtf8Char; begin Result:=nil; if Txt=nil then Exit; p:=Txt; Dec(p); while p^ in [#$80..#$bf] do Dec(p); if IsValidUtf8(p) then Result:=p; end; function DBDLastChar(const txt: PUtf8Char): PUtf8Char; var c: Ucs4CodePoint; p,p0: PUtf8Char; begin Result:=txt; if (txt=nil) or (Ord(Txt^)=$0) then Exit; p:=txt; repeat p0:=p; c:=DBDUTF8ToUCS4(p); until (c=0) or (Ord(p^)=$0); Result:=p0; end; function DBDLastChar(const txt: RawUtf8): PUtf8Char; var p: PUtf8Char; begin if txt='' then Result:=nil else begin p:=@txt[Length(txt)]; Inc(p); Result:=DBDPrevSymbol(p); end; end; function DBDisDupeString(const txt: PUtf8Char; const C: AnsiChar; Trimmed: Boolean=False): Boolean; var p: PUtf8Char; begin Result:=(txt<>nil) and (txt^<>#0) and (C<>#0); if not Result then Exit; if Trimmed then p:=DBDFindNonSpaceChar(@txt[1]) else p:=txt; Result:=(p^=C); if not Result then Exit; Inc(p); while (p^=C) or (p^<>#0) do Inc(p); if p^=#0 then Exit; if DBDisWhiteSpace(p) then Result:=DBDisEmptyString(p) else Result:=False; end; function DBDisDupeString(const txt: RawUtf8; const C: AnsiChar; Trimmed: Boolean=False): Boolean; begin Result:=DBDisDupeString(@txt[1], C, Trimmed); end; function DBDisDupeString(const txt, sub: PUtf8Char; Trimmed: Boolean=False): Boolean; var p0,p1,p2: PUtf8Char; pp: PPUtf8Char; begin Result:=(txt<>nil) and (txt^<>#0) and (sub<>nil) and (sub^<>#0); if not Result then Exit; if Trimmed then p1:=DBDFindNonSpaceChar(@txt[1]) else p1:=txt; p2:=sub; pp:=@p0; Result := DBDStartWith(p1,p2,pp); while Result do begin p1:=p0; Result := DBDStartWith(p1,p2,pp); end; if p1^=#0 then Exit; if DBDisWhiteSpace(p1) then Result:=DBDisEmptyString(p1) else Result:=False; end; function DBDisDupeString(const txt, sub: RawUtf8; Trimmed: Boolean=False): Boolean; begin if (txt='') or (sub='') or (Length(sub)>Length(txt)) then Result:=False else Result:=DBDisDupeString(@txt[1], @sub[1], Trimmed); end; function DBDisEmptyString(const txt: RawUtf8): Boolean; overload; begin Result:=DBDisEmptyString(@Txt[1]); end; function DBDisEmptyString(const txt: PUtf8Char): Boolean; overload; var c: Ucs4CodePoint; p: PUtf8Char; begin p:=txt; Result:=True; if p=nil then Exit; while (p^<>#0) do begin c:=DBDUTF8ToUCS4(p); Result:=DBDisWhiteSpace(c); if not Result then Break; end; end; function DBDisExtraChar(const USC4: Ucs4CodePoint): Boolean; begin Result:= (PUCUUnicodeGetCategoryFromTable(USC4) in dbdExtraCharCategory) end; function HexToInteger(const Hex: PUtf8Char): Int32; var f: Boolean; b: Byte; p: PUtf8Char; begin Result:=0; f:=false; p:=Hex; while (p<>nil) and (p^<>#0) do begin b:=Byte(p^); if (b>=$30) and (b<=$39) then Result:=(Result shl 4) + (b-$30) //0..9 else if (b>=$41) and (b<=$46) then Result:=(Result shl 4) + (b-$41) + 10 //A..F else if (b>=$61) and (b<=$66) then Result:=(Result shl 4) + (b-$61) + 10 //a..f else if (b=$24) and (not f) then f:=true // $ - первый: признак шестнацатеричного числа else begin Result:=-1; Break; end; Inc(p); end end; function HexToInteger(const Hex: RawUTF8): Int32; overload; begin Result:=HexToInteger(@Hex[1]); end; function HexToInteger(const Hex: ShortString): Int32; var i: integer; f: Boolean; begin Result:=0; f:=false; if Hex<>'' then Result:=0; for i:=1 to Length(Hex) do Case Hex[i] of '0'..'9': Result:=(Result shl 4) + (Ord(Hex[i])-$30); 'A'..'F': Result:=(Result shl 4) + (Ord(Hex[i])-$41)+10; 'a'..'f': Result:=(Result shl 4) + (Ord(Hex[i])-$61)+10; '$': if f then begin Result:=-1; Break; end else f:=True; else begin Result:=-1; Break; end; end; end; function DBDFindNonSpaceChar(const p: PUTF8Char): PUTF8Char; var cs: integer; c: Ucs4CodePoint; begin Result:=p; if (p=nil) or (p^=#0) then Exit; while (Result<>nil) and (Result^<>#0) do begin cs:=PUCUUTF8CharSteps[Result^]; c:=DBDUTF8ToUCS4(Result); if not DBDisWhiteSpace(c) then begin Dec(Result,cs); Break; end; end; end; function DBDFindSpaceChar(const p: PUTF8Char): PUTF8Char; var c: Ucs4CodePoint; begin Result:=p; if (p=nil) or (p^=#0) then Exit; while (Result<>nil) and (Result^<>#0) do begin c:=DBDUTF8ToUCS4(Result); if DBDisWhiteSpace(c) then Break; end; end; function DBDFindSpaceChar(const txt: RawUTF8; var vPos: integer): Boolean; var p,t: PUTF8Char; begin Result:=(txt<>'') and (vPos>0) and (vPos<Length(txt)); if not Result then Exit; p:=@txt[vPos]; t:=DBDFindSpaceChar(p); Result:=(t<>nil) and (t^<>#0); if not Result then Exit; vPos:=vPos+(t-p); end; function DBDFindStopChar(const txt: PUtf8Char; const Stops: array of AnsiChar; const Idx: PPtrUInt): PUtf8Char; var p: PUtf8Char; c: AnsiChar; i: PtrUInt; begin Result:=nil; if (txt<>nil) and (txt^<>#0) and (Length(Stops)>0) then begin p:=txt; repeat c:=p^; if c=#0 then Break; for i:=0 to High(Stops) do if c=Stops[i] then begin Result:=p; if Idx<>nil then Idx^:=i; Exit; end; Inc(p); until False; end; end; function DBDFindStopChar(const txt: PUtf8Char; const Stops: PAnsiChar; const Idx: PPtrUInt=nil): PUtf8Char; overload; var p: PUtf8Char; t: PAnsiChar; c: AnsiChar; begin Result:=nil; if (txt<>nil) and (txt^<>#0) and (Stops<>nil) and (Stops^<>#0) then begin p:=txt; repeat c:=p^; if c=#0 then Break; t:=Stops; repeat if c=t^ then begin Result:=p; if Idx<>nil then Idx^:=t-Stops; Exit; end; Inc(t); if t^=#0 then Break; until False; Inc(p); until False; end; end; function DBDGetCategory(var p: PUtf8Char): integer; var c: Ucs4CodePoint; begin c:= DBDUTF8ToUCS4(p); Result:=PUCUUnicodeGetCategoryFromTable(c); end; function DBDGetWord(var P: PUtf8Char): RawUTF8; var t: PUtf8Char; c: Ucs4CodePoint; fNonSpace: Boolean; cs: Integer; begin Result:=''; t:=p; fNonSpace:=false; while (t<>nil) and (t^<>#0) do begin cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); if DBDisWhiteSpace(c) then begin if fNonSpace then begin FastSetString(Result, P, t-P-cs); P:=t; Break; end; end else fNonSpace:=True; end; end; function DBDGetWords(const Txt: RawUTF8): TRawUTF8DynArray; var p: PUTF8Char; r: RawUTF8; i: integer; da: TDynArray; ra: TRawUTF8DynArray; cnt: Integer; begin if Length(Txt)=0 then SetLength(Result,0) else begin da.Init(TypeInfo(TRawUTF8DynArray), ra, @cnt); da.Capacity:=(Length(Txt) div 10)+10; p:=@Txt[1]; while p^<>#0 do begin r:=DBDGetWord(p); if r<>'' then da.Add(r) else Break; end; SetLength(Result,cnt); for i:=0 to cnt-1 do Result[i]:=ra[i]; end; end; function DBDCharSize(const P: PUtf8Char): PtrInt; var t: PUtf8Char; begin t:=p; if P^=#0 then Result:=0 else if DBDUTF8ToUCS4(t)=$fffd then Result:=-1 else Result:=PUCUUTF8CharSteps[P^]; // else result:=PUCUUTF8CharSteps[P^] end; function DBDUSC4ToUtf8(const C: Ucs4CodePoint): RawUtf8; begin Result:=PUCUUTF32CharToUTF8(TPUCUUTF32Char(c)); end; function DBDUTF8ToUCS4(var p: PUtf8Char): Ucs4CodePoint; var cc,st: UInt32; c:Byte; begin st:=ucACCEPT; if (p=nil) or (p^=#0) then Result:=0 else begin st:=0; c:=Byte(p[0]); while c<>0 do begin inc(p); cc:=PUCUUTF8DFACharClasses[TPUCURawByteChar(c)]; if st=ucACCEPT then begin Result:=c and ($ff shr cc); end else begin Result:=(Result shl 6) or (c and $3f); end; st:=PUCUUTF8DFATransitions[st+cc]; if st<=ucERROR then break; c:=Byte(p[0]); end; end; if st<>ucACCEPT then begin result:=$fffd; Inc(PUCUUTF8CharSteps[p^]); end; end; function DBDUCS4ToLower(const C: Ucs4CodePoint): Ucs4CodePoint; begin Result:=PUCUUnicodeToLower(C); end; function DBDUCS4ToUpper(Const C: Ucs4CodePoint): Ucs4CodePoint; var v: Ucs4CodePoint; begin if C=0 then Result:=0 else begin v:=C shr PUCUUnicodeUpperCaseDeltaArrayBlockBits; Result:= TPUCUUInt32(TPUCUInt32(TPUCUInt32(C)+ PUCUUnicodeUpperCaseDeltaArrayBlockData[ PUCUUnicodeUpperCaseDeltaArrayIndexBlockData[ PUCUUnicodeUpperCaseDeltaArrayIndexIndexData[v shr PUCUUnicodeUpperCaseDeltaArrayIndexBlockBits], v and PUCUUnicodeUpperCaseDeltaArrayIndexBlockMask], C and PUCUUnicodeUpperCaseDeltaArrayBlockMask])); end; end; function DBDUTF8ToUCS4Upper(var p: PUtf8Char): Ucs4CodePoint; begin Result:=DBDUCS4ToUpper(DBDUTF8ToUCS4(p)); end; function DBDIsCategory(const USC4: Ucs4CodePoint; const Categories: TdbdPUCUCategories): Boolean; begin Result:=PUCUUnicodeGetCategoryFromTable(USC4) in Categories; end; function DBDIsCategory(const Utf8Char: PUtf8Char; const Categories: TdbdPUCUCategories): Boolean; overload; var p: PUtf8Char; begin if Utf8Char=nil then Result:=False else begin p:=Utf8Char; Result := (DBDUTF8ToUCS4(p) in Categories); end; end; function DBDisLatinUpper(const USC4: Ucs4CodePoint): Boolean; begin Result:= (USC4>=$41) and (USC4<=$5A); end; function DBDisLatinLow(const USC4: Ucs4CodePoint): Boolean; begin Result:= (USC4>=$61) and (USC4<=$7A); end; function DBDisRussianUpper(const USC4: Ucs4CodePoint): Boolean; begin Result:= ((USC4>=$0410) and (USC4<=$042F)) or (USC4=$0401); end; function DBDisRussianLow(const USC4: Ucs4CodePoint): Boolean; begin Result:= ((USC4>=$0430) and (USC4<=$044F)) or (USC4=$0451); end; function DBDisDigit(const USC4: Ucs4CodePoint): Boolean; begin Result:= (PUCUUnicodeGetCategoryFromTable(USC4) = PUCU_CT_DECIMAL_DIGIT_NUMBER) end; function DBDisNumberChar(const USC4: Ucs4CodePoint): Boolean; begin Result := ((USC4>=$30) and (USC4<=$39)) // цифра от 0 до 9 or (USC4=$2C) or (USC4=$2E) // запятая или точка or (USC4=$2B) or (USC4=$2795) // плюс or (USC4=$2D) or (USC4=$2212) // минус or (USC4=$45) or (USC4=$65) // E или е - символ мантиссы or (USC4=$24) // $ - признак шестнадцатеричного числа or (USC4=$23) // # - признак восьмеричного числа ; end; function DBDValidID(const ID: RawUTF8): Boolean; var p: PUtf8Char; c: Ucs4CodePoint; begin Result:=ID<>''; if not Result then Exit; p:=@ID[1]; repeat c:=DBDUTF8ToUCS4(p); until (c=0) or (not DBDisWhiteSpace(c)); if not DBDisDigit(c) then Result:=False else repeat c:=DBDUTF8ToUCS4(p); Result:=DBDisDigit(c) or (c=$2d) or (c=$2e) or (c=0) or (c=$2212); until (c=0) or (not Result); end; function DBDIsValidIdentStart(var PP: PUtf8Char; const ExtLetters: Boolean=False): Boolean; begin Result:=(PP^ in ['a'..'z', 'A'..'Z', '_']) or (ExtLetters and DBDIsCategory(PP, dbdLetterCategory)); end; function DBDIsValidIdentChar(var PP: PUtf8Char; const ExtLetters: Boolean=False): Boolean; begin Result:=(PP^ in ['a'..'z', 'A'..'Z', '_']) or (ExtLetters and DBDIsCategory(PP, dbdLetterCategory)); end; function DBDGetIdent(PP: PUtf8Char; const ExtLetters: Boolean=False): PUtf8Char; begin end; function DBDisEndOfUtf8(const txt: PUtf8Char): Boolean; begin Result := (txt=nil) or (txt^=#0); end; function DBDisWhiteSpace(const USC4: Ucs4CodePoint): Boolean; begin Result:=PUCUUnicodeIsWhiteSpace(USC4); end; function DBDisWhiteSpace(var p: PUtf8Char; const MoveNext: Boolean): Boolean; var t: PUtf8Char; usc4: Ucs4CodePoint; begin t:=p; usc4:=DBDUTF8toUCS4(t); Result:=DBDisWhiteSpace(usc4); if Result and MoveNext then p:=t; end; function DBDisWordSymbol(const USC4: Ucs4CodePoint): Boolean; begin Result:=PUCUUnicodeGetCategoryFromTable(USC4) in dbdWordSymbolsCategory; end; function DBDisWordSymbol(var p: PUtf8Char): Boolean; var t: PUtf8Char; usc4: Ucs4CodePoint; begin t:=p; usc4:=DBDUTF8toUCS4(t); Result:=DBDisWordSymbol(usc4); if Result then p:=t; end; function DBDLengthUTF8(const txt: RawUTF8): Integer; begin Result := PUCUStringLength(txt) end; function DBDSizeUtf8(const txt: PUtf8Char): Integer; var p: PUtf8Char; begin Result:=0; if (Txt=nil) then Exit; p:=Txt; while p^<>#0 do begin Inc(p); end; Result:=p-txt; end; function DBDReadDateUtf8(const Txt: RawUtf8): TDateTime; overload; var dt: TDateTime; begin if DBDReadDateUtf8(Txt, dt) then Result:=dt else Result:=0.0; end; function DBDReadDateUtf8(const Txt: RawUtf8; out dt: TDateTime): Boolean; var p: PUtf8Char; begin dt:=0.0; if Txt='' then Result:=False else begin p:=@Txt[1]; Result:=DBDReadDateUtf8(p,dt) end; end; function DBDReadDateUtf8(var p:PUTF8Char; out dt: TDateTime): Boolean; var t:PUTF8Char; cs: integer; c: Ucs4CodePoint; y,m,d: Int64; sep: Ucs4CodePoint; digs: integer; begin Result:=False; dt:=0.0; if (p=nil) or (p^=#0) then Exit; t:=p; y:=0; m:=0; d:=0; // 1. пропускаем пробелы c:=$20; repeat if not DBDisWhiteSpace(c) then Break; cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); until (c=0) or (not DBDisWhiteSpace(c)); if (c=0) then Exit; // 2. Проверяем первый символ предполагаемого числа if (c>=$30) and (c<=$39) then d:=(c-$30) // "0123456789" else Exit; // символ вне числа digs:=1; while (t<>nil) and (t^<>#0) do begin c:=DBDUTF8ToUCS4(t); if (c>=$30) and (c<=$39) then begin // "0123456789" d:=d*10+(c-$30); Inc(digs); if digs>4 then Exit; end else if ((c=$2f) or (c=$2044) or (c=$2215)) or ((c=$2d) or (c=$2212)) then begin // "/" или "-" Sep:=c; Break; end else Break; // символ вне числа end; digs:=0; while (t<>nil) and (t^<>#0) do begin cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); if (c>=$30) and (c<=$39) then begin // "0123456789" m:=m*10+(c-$30); Inc(digs); if digs>2 then Exit; end else if (c=Sep) then begin // "/" или "-" Sep:=c; Break; end else Break; // символ вне числа end; digs:=0; while (t<>nil) and (t^<>#0) do begin cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); if (c>=$30) and (c<=$39) then begin // "0123456789" y:=y*10+(c-$30); Inc(digs); if digs>4 then Exit; end else Break; // символ вне числа end; if (d>0) and (m>0) and (y>0) then begin Result:=TryEncodeDate(y,m,d, dt); if not Result then dt:=0; end; if Result then p:=t; end; function DBDReadFloatUtf8(var p:PUtf8Char; out d: Double): Boolean; var t:PUTF8Char; cs: integer; c: Ucs4CodePoint; n,m: Int64; fracs, digs: integer; fEnd, fMinus, fExp, fExpMinus, fExpSig: Boolean; cDecimal: Ucs4CodePoint; begin Result:=False; d:=0.0; if (p=nil) or (p^=#0) then Exit; //TODO: разделители тысяч t:=p; n:=0; m:=0; fExp:=False; fExpMinus:=False; fExpSig:=False; // 1. пропускаем пробелы c:=$20; repeat if not DBDisWhiteSpace(c) then Break; cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); until (c=0) or (not DBDisWhiteSpace(c)); if (c=0) then Exit; cDecimal:=0; fMinus:=False; n:=0; // 2. Проверяем первый символ предполагаемого числа if (c>=$30) and (c<=$39) then n:=(c-$30) // "0123456789" else if (c=$2c) or (c=$2e) then cDecimal:=c // "," или "." else if (c=$2d) or (c=$2212) or (c=$2b) then begin // "-" или "+" fMinus:=(c<>$2b); c:=DBDUTF8ToUCS4(t); if (c>=$30) and (c<=$39) then n:=(c-$30) // "0123456789" else if (c=$2c) or (c=$2e) then cDecimal:=c // "," или "." else if DBDisWhiteSpace(c) then begin // одиночный - : число пропущено (0.0) d:=0.0; Result:=True; Exit; end else Exit; // после знака может быть только цифра или десятичный разделитель end else Exit; // символ вне числа fEnd:=False; if cDecimal=0 then begin // 3. считываем целую часть числа digs:=1; while (t<>nil) and (t^<>#0) do begin c:=DBDUTF8ToUCS4(t); if (c>=$30) and (c<=$39) then begin // "0123456789" n:=n*10+(c-$30); Inc(digs); end else if (c=$2c) or (c=$2e) then begin // "," или "." cDecimal:=c; Break; end else if DBDisWhiteSpace(c) then begin // пробельный символ if digs>3 then begin fEnd:=True; Break; end; // количество цифр до пробела не может быть больше трёх digs:=0; end else if (c=$45) or (c=$65) then begin // символ экспоненты "E" или "e" fexp:=True; Break; end else begin fEnd:=True; Break; end; // символ вне числа end; end; if (t=nil) or (t^=#0) then begin fEnd:=True; c:=$20 end; if fEnd then begin // конец числа if n>=0 then begin d:=n; Result:=True; if not DBDisWhiteSpace(c) then Dec(t,cs); p:=t; end; if fMinus then d:=-d; Exit; end; // 4. считываем дробную часть числа fracs:=0; Result:=True; m:=0; while (t<>nil) and (t^<>#0) do begin cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); if (c>=$30) and (c<=$39) then begin // "0123456789" Inc(fracs); m:=m*10+(c-$30); end else if fExp and ((c=$2d) or (c=$2212) or (c=$2b)) then begin // "-" или "+" для экспоненты if fExpSig then begin Result:=False; Exit; end; fExpSig:=True; fExpMinus:=(c<>$2b); end else Break; // не цифирка end; if (t<>nil) and (t^<>#0) and (not DBDisWhiteSpace(c)) then Dec(t,cs); p:=t; if fExp then begin if (n<>0) and (m<>0) then begin if fExpMinus then d:=n*POW10[-m] else d:=n*POW10[m]; end; if fMinus then d:=-d; Exit; end else begin d:=n+m*POW10[-fracs]; if fMinus then d:=-d; end; if (t=nil) or (t^=#0) then Exit; // экспонента cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); if (c=$45) or (c=$65) then begin // символ экспоненты "E" или "e" fExp:=True; fExpSig:=False; Result:=True; m:=0; while (t<>nil) and (t^<>#0) do begin cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); if (c>=$30) and (c<=$39) then begin // "0123456789" m:=m*10+(c-$30); end else if ((c=$2d) or (c=$2212) or (c=$2b)) then begin // "-" или "+" для экспоненты if fExpSig then begin Result:=False; Exit; end; fExpSig:=True; fExpMinus:=(c<>$2b); end else Break; // не цифирка end; if m<>0 then begin if fExpMinus then d:=d*POW10[-m] else d:=d*POW10[m]; end; p:=t; end; end; function DBDReadFloatUtf8Def(var p: PUTF8Char; const Def: Double): Double; begin if not DBDReadFloatUtf8(p,Result) then Result:=Def; end; function DBDReadNumberUtf8(var p:PUTF8Char; out i: Integer): Boolean; var t,t0:PUTF8Char; cs: integer; c: Ucs4CodePoint; begin t:=p; Result:=False; i:=0; cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); Result:=DBDisDigit(c); if not Result then Exit; i:=(c-$30); while (t<>nil) and (t^<>#0) do begin t0:=t; cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); if not DBDisDigit(c) then begin t:=t0; Break; end; i:=i*10+(c-$30); end; if Result then p:=t; end; function DBDReadNumberUtf8Def(var p:PUTF8Char; const Def: Integer): Integer; begin if not DBDReadNumberUtf8(p, Result) then Result:=Def; end; function DBDUtf8ToDoubleDef(const txt: RawUtf8; const Def: Double=0.0): Double; begin if not DBDUtf8ToDouble(txt, Result) then Result:=Def; end; function DBDUtf8ToDouble(const txt: RawUtf8; out d: Double): Boolean; var p: PUtf8Char; begin d:=0.0; if txt='' then Result:=False else begin p:=@txt[1]; Result:=DBDReadFloatUtf8(p,d); end; end; function DBDUtf8ToCurrency(const txt: RawUtf8; out C: Currency): Boolean; var p: PUtf8Char; d:Double; begin C:=0.0; if txt='' then Result:=False else begin p:=@txt[1]; Result:=DBDReadFloatUtf8(p,d); if Result then C:=DoubleToCurrency(d); end; end; function DBDUtf8ToCurrencyDef(const txt: RawUtf8; const Def: Currency): Currency; begin if not DBDUtf8ToCurrency(txt, Result) then Result:=Def; end; function DBDDoubleToText(const D: Double; const Prec: Integer): RawUtf8; begin if D=0.00 then Result := '0' else begin Result := FloatToStrF(D, ffFixed, DBD_DOUBLE_PRECISION, Prec, DBDFormatSettings); Result := DBDTrimAnsiChar(Result,'0', dbdSPRight); end; end; function DBDRoundUtf8(const Txt: RawUtf8; const Prec: Integer): RawUtf8; var i,k: Integer; c: AnsiChar; begin Result:=Txt; if Txt='' then Exit; k:=-1; for i := 1 to Length(Txt) do begin c:=Txt[i]; if c='.' then k:=0 else if not (c in ['0'..'9']) then begin Result:=''; Exit; end else if k=Prec then begin k:=i; Break; end else if k>=0 then Inc(k); end; if (k=-1) or (Length(Txt)=k) then Result:=Txt else begin end; end; function DBDReadWordStr(var p:PUTF8Char; out wrd: RawUTF8): Boolean; var t: PUtf8Char; c: Ucs4CodePoint; cs: Integer; l: Integer; begin wrd:=''; Result:=False; if (p=nil) or (p^=#0) then Exit; t:=p; // 1. пропускаем пробелы repeat cs:=PUCUUTF8CharSteps[t^]; c:=DBDUTF8ToUCS4(t); until (c=0) or (not DBDisWhiteSpace(c)); if c=0 then Exit; // 2. ищем пробелы p:=t; Dec(t, cs); Result:=True; repeat cs:=PUCUUTF8CharSteps[p^]; c:=DBDUTF8ToUCS4(p); until (c=0) or (DBDisWhiteSpace(c)); if c=0 then l:=p-t else l:=p-t-cs; if l<>0 then begin FastSetString(wrd, t, l); end; end; //function DBDTextNormalizationHum(const Txt: RawUTF8; const NormOpt: TDBDTextNormalizationOptions): RawUTF8; //var p,s: PUtf8Char; c,t: Ucs4CodePoint; r,rr: RawUtf8; //begin // Result:=''; if Txt='' then Exit; // p:=@Txt[1]; rr:=''; // while (p<>nil) and (Byte(p^)<>0) do begin // c:=DBDUTF8ToUCS4(p); // if (c<>$2116) and DBDisExtraChar(c) and (dbdtnoExtraCharsToSpace in NormOpt) then c:=$20; // if DBDisWhiteSpace(c) then begin //пробельный символ // if dbdtnoBlankCharsToSpace in NormOpt then t:=$20 else t:=c; // if dbdtnoBlankCharsToSpace in NormOpt then c:=$20;// else t:=c; // if dbdtnoRemoveExtraBlanks in NormOpt then begin //пропускаем пробельные символы //// t:=$20; // while (p<>nil) and (Byte(p^)<>0) do begin // c:=DBDUTF8ToUCS4(p); // if not (DBDisWhiteSpace(c) or ((c<>$2116) and DBDisExtraChar(c) and (dbdtnoExtraCharsToSpace in NormOpt))) then begin // Break; // end; // end; // if c<>0 then // rr:=rr+' '; // end; //// if (p<>nil) and (Byte(p^)<>0) then // // if (p=nil) or (Byte(p^)=0) then Break; //// r:=PUCUUTF32CharToUTF8(TPUCUUTF32Char(t)); //// rr:=rr+r; // end; // if dbdtnoUpper in NormOpt then c:=DBDUCS4ToUpper(c); // r:=PUCUUTF32CharToUTF8(TPUCUUTF32Char(c)); // rr := rr + r; // end; // Result:=rr; //// FastSetString(Result,@rr[1],Length(rr)); //end; function DBDTextNormalization(const Txt: RawUTF8; const NormOpt: TDBDTextNormalizationOptions): RawUTF8; var p, pEnd: PUtf8Char; c: Ucs4CodePoint; l: Integer; Writer: TTextWriter; // Используем быстрый писатель mORMot LastWasSpace: Boolean; begin Result:=''; if Txt = '' then Exit; // Резервируем память примерно равную исходной, чтобы избежать Realloc l:=Length(Txt); if l<32 then l:=32; Writer := TTextWriter.CreateOwnedStream(l); try p := @Txt[1]; pEnd := p + Length(Txt); LastWasSpace := False; while p < pEnd do begin c := DBDUTF8ToUCS4(p); if c = 0 then Break; // 1. Обработка ExtraChars -> Space if (c <> $2116) and (dbdtnoExtraCharsToSpace in NormOpt) and DBDisExtraChar(c) then c := $20; // 2. Обработка пробелов if DBDisWhiteSpace(c) then begin if dbdtnoBlankCharsToSpace in NormOpt then c := $20; if dbdtnoRemoveExtraBlanks in NormOpt then begin if LastWasSpace then Continue; // Просто пропускаем, если уже добавили пробел c := $20; LastWasSpace := True; end; end else LastWasSpace := False; // 3. Регистр if dbdtnoUpper in NormOpt then c := DBDUCS4ToUpper(c) else if dbdtnoLower in NormOpt then c := DBDUCS4ToLower(c); // 4. Запись (оптимизировано для ASCII) if c < 128 then Writer.Add(AnsiChar(c)) else Writer.AddUcs4(c); // В mORMot есть методы прямой записи Ucs4 end; Result := Writer.Text; finally Writer.Free; end; end; function DBDisEqualTexts(const Txt1, Txt2: RawUtf8): Boolean; begin Result:=DBDTextNormalization(Txt1, dbdTNCompareUCase)=DBDTextNormalization(Txt2,dbdTNCompareUCase); end; function DBDUtf8ToLower(const Txt: RawUtf8): RawUtf8; begin if Txt='' then Result:='' else Result:=PUCUUTF8LowerCase(Txt); end; function DBDUtf8ToUpper(const Txt: RawUtf8): RawUtf8; begin if Txt='' then Result:='' else Result:=PUCUUTF8UpperCase(Txt); end; function DBDTrimStr(const p: PUTF8Char; const Option: TDBDTrimOption; const LenStr: integer): RawUTF8; overload; var pStart, pEnd: PUTF8Char; cs: integer; c: Ucs4CodePoint; begin Result:=''; if (p=nil) or (p^=#0) then Exit; pStart:=p; if LenStr>0 then pEnd:=pStart+LenStr else pEnd:=nil; if (Option=dbdtoLeft) or (Option=dbdtoBoth) then begin // первый не пробел слева while (pStart<>nil) and (Byte(pStart^)<>0) do begin cs:=PUCUUTF8CharSteps[pStart^]; c:=DBDUTF8ToUCS4(pStart); if not DBDisWhiteSpace(c) then begin Dec(pStart,cs); Break; end; if (pEnd<>nil) and (pStart>pEnd) then Exit; end; end; if (pStart=nil) or (pStart^=#0) then Exit; if LenStr<=0 then begin pEnd:=pStart; while (pEnd^<>#0) do Inc(pEnd); Dec(pEnd); end; if (Option=dbdtoRight) or (Option=dbdtoBoth) then begin // первый не пробел справа while pEnd>pStart do begin while pEnd>pStart do if (Byte(pEnd^)>=$80) and (Byte(pEnd^)<=$bf) then Dec(pEnd) else Break; cs:=PUCUUTF8CharSteps[pEnd^]; c:=DBDUTF8ToUCS4(pEnd); if not DBDisWhiteSpace(c) then Break; pEnd:=pEnd-cs-1; end; end; if pStart<pEnd then begin FastSetString(Result, pStart, pEnd-pStart); end; end; function DBDTrimStr(const txt: RawUTF8; const Option: TDBDTrimOption): RawUTF8; overload; var p: PUTF8Char; begin Result:=''; if Txt='' then Exit; case Option of dbdtoLeft: Result:=PUCUUTF8TrimLeft(txt); dbdtoRight: Result:=PUCUUTF8TrimRight(txt); dbdtoBoth: Result:=PUCUUTF8Trim(txt); end; end; function DBDReplaceTextAll(Txt, Old, New: RawUtf8): RawUtf8; begin if (Txt='') or (Old='') then Result:=Txt else Result := StringReplaceAll(Txt, Old, New, False); end; function DBDFactorsDisassemble(const Txt: RawUtf8): TRawUtf8DynArray; var oBrackets: Integer; i,j,l, cnt: Integer; p,p0,p1: PUtf8Char; idx: PtrUint; r: RawUtf8; begin if Txt='' then begin SetLength(Result,0); Exit; end; p0:=PUtf8Char(@Txt[1]); l:=Length(Txt); p:=DBDSkipBlankChars(p0); p:=p0; if (p=nil) or (p^=#0) then begin SetLength(Result,0); Exit; end; if p^='=' then Inc(p); oBrackets:=0; cnt:=0; i:=Trunc(l/2)+2; SetLength(Result, i); p1:=p0+l-1; p0:=p; p:=DBDFindStopChar(p,['*','@','(',')',';'], @idx); try while (p<>nil) and (p^<>#0) do begin if idx=2 then Inc(oBrackets) else if idx=4 then begin while (p<>nil) and (p^<>#0) do begin p:=DBDFindChar(p,')'); inc(p); end; if p^=')' then Dec(p) else Break; end else if idx=3 then begin Dec(oBrackets); if oBrackets=0 then begin FastSetString(r,p0, p-p0+1); Result[cnt]:=r; Inc(cnt); p0:=p+1; end; end else if (idx in[0,1]) and (oBrackets=0)then begin if p-p0>0 then begin FastSetString(r,p0, p-p0); Result[cnt]:=r; Inc(cnt); end; p0:=p+1; end else if (p<>nil) and (p^<>#0) then begin end; Inc(p); p:=DBDFindStopChar(p,['*','@','(',')',';'], @idx); end; if (p0<>nil) and (p0^<>#0) then begin FastSetString(r,p0, l-(p0-@Txt[1])); Result[cnt]:=r; Inc(cnt); end; except cnt:=0; end; SetLength(Result,cnt); end; function DBDFactorsDisassemble(var Txt: PUtf8Char; const isSimple: PBoolean=nil): RawUtf8; begin Result:=''; if IsSimple<>nil then IsSimple^:=True; if (Txt=nil) or (Txt^=#0) then Exit; end; function DBDFormulaIDisassemble(var Txt: PUtf8Char; out Params: TRawUtf8DynArray): integer; begin end; end.