Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[35.173.48.18] |
|
Сообщ.
#1
,
|
|
|
Функции Split, Join, GetElement
Апдейт этой темы - т.к. отредактировать тот пост нет возможности, а исходная тема удалена. Набор функций для работы со строками, содержащими списки элементов с разделителями. Аналог одноименных функций из JS, PHP. Разделитель может быть любой длины. Проверено в Delphi 2009-10, но должно работать и в более старых версиях uses StrUtils; // для PosEx const // Разделитель по умолчанию для Split, Join, GetElement DefListDelim = ';'; ... type // Алиас типа вместо нового объявления - в целях совместимости. // Можно было бы юзать TArray<string>, но он глючит при использовании в параметрах // (i := Low(arr) - пишет Incompatible types) TStrArray = Types.TStringDynArray; ... function Split(const Str: string; Delim: string = DefListDelim; AllowEmpty: Boolean = True): TStrArray; function GetElement(const Str: string; ElemIdx: Integer; Delim: string = DefListDelim): string; function Join(const Arr: array of string; Delim: string = DefListDelim; AllowEmpty: Boolean = True): string; ... // Расщепление строки Str на массив элементов. // Str - исходная строка // Delim - разделитель элементов в строке (может насчитывать сколько угодно символов) // AllowEmpty - добавлять ли в массив пустые элементы ('elem1;elem2;;elem3'). // Потребуется, если исходная строка представляет собой неоднородный список // с фиксированными индексами элементов // Пример: // HTMLlines := Split(HTMLPageSource, #13#10, False) // BananaProperties := Split('banana;yellow;;Africa', ';', True) function Split(const Str: string; Delim: string; AllowEmpty: Boolean): TStrArray; var CurrDelim, NextDelim, CurrIdx: Integer; begin if Str = '' then begin SetLength(Result, 0); Exit; end; CurrDelim := 1; CurrIdx := 0; SetLength(Result, 16); repeat if CurrIdx = Length(Result) then SetLength(Result, CurrIdx + 16); // проверяем наполненность массива и расширяем при необходимости NextDelim := PosEx(Delim, Str, CurrDelim); // позиция следующего разделителя if NextDelim = 0 then NextDelim := Length(Str)+1; // строка кончилась Result[CurrIdx] := Copy(Str, CurrDelim, NextDelim - CurrDelim); CurrDelim := NextDelim + Length(Delim); // если элемент непуст или же пустые допустимы - увеличиваем индекс if (Result[CurrIdx] <> '') or AllowEmpty then Inc(CurrIdx) else Continue; until CurrDelim > Length(Str); SetLength(Result, CurrIdx); // обрезаем массив end; // Получение 0-based элемента из строки с разделителями // Str - исходная строка // ElemIdx - индекс элемента, от 0 до ... // Delim - разделитель элементов в строке (может насчитывать сколько угодно символов) // Пример: // value := GetElement('GeneralSettings.SomeIniOption = 123', 1, ' = '); function GetElement(const Str: string; ElemIdx: Integer; Delim: string): string; var CurrDelim, NextDelim, Idx: Integer; begin Result := ''; CurrDelim := 1; // ElemIdx раз ищем символ разделителя в строке for Idx := 1 to ElemIdx do begin CurrDelim := PosEx(Delim, Str, CurrDelim); if CurrDelim = 0 then Exit; Inc(CurrDelim, Length(Delim)); // теперь здесь индекс первого символа следующего элемента end; NextDelim := PosEx(Delim, Str, CurrDelim); // ищем конечный разделитель if NextDelim = 0 then NextDelim := Length(Str) + 1; Result := Copy(Str, CurrDelim, NextDelim-CurrDelim); end; // Объединение всех строк из массива в одну с разделителями (обратно Split) // Arr - массив строк // Delim - разделитель элементов в строке (может насчитывать сколько угодно символов, // (!) в том числе и ни одного (!) ) // AllowEmpty - добавлять ли в строку пустые элементы // Потребуется, если итоговая строка должна представлять собой неоднородный список // с фиксированными индексами элементов // Пример: // FruitList := Join(['apples', 'bananas', 'grape'], '; ') // BananaProperties := Join([Banana.Name, Banana.Color, '', Banana.Country], ';', True) // ReJoin := Join(Split('one;two;three'), '|'); function Join(const Arr: array of string; Delim: string; AllowEmpty: Boolean): string; var i: Integer; begin Result := ''; for i := Low(Arr) to High(Arr) do begin if (Arr[i] = '') and not AllowEmpty then Continue; if Result = '' then Result := Arr[i] else Result := Result + Delim + Arr[i]; end; end; 12.11 * Тип TStrArray теперь является алиасом Types.TStringDynArray по совету jack128. Более стильный TArray<string>, к сожалению, глючит в качестве параметра. * Добавил Join с параметром типа array of string - код полностью аналогичен, но позволяет задавать параметр-массив прямо в коде без использования вспомогательной StrArray 17.12 * Убраны StrArray и Join с параметром TStrArray: Join с параметром array of string работает и с переменными типа TStrArray |
Сообщ.
#2
,
|
|
|
Fr0sT, а чем не устроила стандартная ExtractStrings? И зачем выходные данные в виде массива? TStrings всяко удобнее, особенно тем, что повсеместно используется в VCL - можно сразу подставлять в него TMemo.Lines или TComboBox.Items и так длаее
|
Сообщ.
#3
,
|
|
|
--Ins--, тем, что TStrings надо удалять.
|
Сообщ.
#4
,
|
|
|
Цитата Fr0sT @ --Ins--, тем, что TStrings надо удалять. Ну, сомнительный недостаток, имхо. По мне так массивы - крайне неудобная вещь, практически нигде их не использую, заменяя контейнерными классами. Особенно с учетом того, что в интерфейсах VCL именно они и используются, что позволяет их использовать "как есть", а вот массивы почти всегда нужно вручную обрабатывать и преобразовывать к какому-либо виду. Не говоря уже о том, что контейнерные классы по сравнению с массивами поддерживают ряд дополнительных возможностей |
Сообщ.
#5
,
|
|
|
Жалко, что предыдущей темы не осталось - там было в точности то же самое, причем с твоим же участием
Мне лично удобно, очень часто юзаю эти функции для кратковременных задач. |
Сообщ.
#6
,
|
|
|
Цитата Fr0sT @ Жалко, что предыдущей темы не осталось - там было в точности то же самое, причем с твоим же участием Серьезно? Я уже и не помню. Ну тогда ладно |
Сообщ.
#7
,
|
|
|
Цитата Fr0sT @ type TStrArray = array of string; за такое - растрел на месте полагается. Либо TArray<string> должен быть если >=Delphi2009 юзаешь, либо Types.TStringDynArray если раньше. |
Сообщ.
#8
,
|
|
|
Не стреляйте, дяденька!
Лучше аргументируй столь категоричное высказывание. |
Сообщ.
#9
,
|
|
|
Цитата Fr0sT @ Лучше аргументируй столь категоричное высказывание. А чего тут аргументировать?? Вот есть три модуля unit Unit1; type TStrArray1 = array of string; funciton f1(): TStrArray1; ... unit Unit2; type TStrArray2 = array of string; function f2(arr: TStrArray2): TStrArray2; unit Unit3; type TStrArray3 = array of string; function f3(arr: TStrArray3): TStrArray3; разработчик каждого нового модуля считает себя самым умными и объявляет новый тип для массива строк. в результате я вместо того, чтобы просто написать f3(f2(f1)) вынужден либо unsafe type cast'ом заниматься, либо писать пачку преобразователей TStrArray1 -> TStrArray2 -> TStrArray3. |
Сообщ.
#10
,
|
|
|
* Тип TStrArray теперь является алиасом Types.TStringDynArray по совету jack128. Более стильный TArray<string>, к сожалению, глючит в качестве параметра.
* Добавил Join с параметром типа array of string - код полностью аналогичен, но позволяет задавать параметр-массив прямо в коде без использования вспомогательной StrArray |
Сообщ.
#11
,
|
|
|
TStringBuilder в D2009 есть?? Если есть, то в Join - он так и просится.
|
Сообщ.
#12
,
|
|
|
jack128, хм, а что, в нём нет такой функции? Интересная идея, погляжу
|
Сообщ.
#13
,
|
|
|
Цитата Fr0sT @ * Добавил Join с параметром типа array of string - код полностью аналогичен, но позволяет задавать параметр-массив прямо в коде без использования вспомогательной StrArray В таком случае вариант с параметром Arr:TStrArray является вроде как излишним\масло-масляным, т.к. дин.массив можно передавать вместо открытого массива |
Сообщ.
#14
,
|
|
|
Цитата leo @ В таком случае вариант с параметром Arr:TStrArray является вроде как излишним\масло-масляным, т.к. дин.массив можно передавать вместо открытого массива Только вроде как в рантайм преобразование будет, если память не подводит, так что можно второй вариант оставить |
Сообщ.
#15
,
|
|
|
Цитата --Ins-- @ Только вроде как в рантайм преобразование будет Нет, в обоих случаях массив передается непосредственно по указателю, и разница лишь в том, что при Arr:TStringArray ничего доп-но не передается и High(Arr) определяется внутри функции по теневому заголовку Arr (вызовом _DinArrayHigh), а в случае с open array _DinArrayHigh(Arr) вызывается до вызова функции и передается в нее в виде доп.теневого параметра, а внутри функции High(Arr) просто берет это значение из параметра (в данном случае из регистра EDX). |
Сообщ.
#16
,
|
|
|
Цитата leo @ Нет, в обоих случаях массив передается непосредственно по указателю, и разница лишь в том, что при Arr:TStringArray ничего доп-но не передается и High(Arr) определяется внутри функции по теневому заголовку Arr (вызовом _DinArrayHigh), а в случае с open array _DinArrayHigh(Arr) вызывается до вызова функции и передается в нее в виде доп.теневого параметра, а внутри функции High(Arr) просто берет это значение из параметра (в данном случае из регистра EDX) О! А я всё кумекал, в чем разница и почему нельзя просто сделать одну функцию для обоих случаев. Спасибо! |
Сообщ.
#17
,
|
|
|
Цитата Fr0sT @ О! А я всё кумекал, в чем разница и почему нельзя просто сделать одну функцию для обоих случаев В качестве open array параметра, можно передавать не только [...] - конструктор массива "на лету", но и любой массив, совместимый по типу элементов (динамический, статический или выделенный в куче по GetMem), а также одну переменную типа элемента массива (которая рассматривается как массив длины 1). Причем для статических и выделенных по GetMem массивов, во-первых, не обязательна индексация от 0 (хотя внутри функции open array по любому индексируется с 0), во-вторых, можно передавать не весь массив, а произвольное число первых элементов с помощью псевдофункции Slice(массив,Count), которая на этапе компиляции просто заменяет значение передаваемого теневого параметра High(Arr) на Count. Ну и разумеется, во всех этих случаях при объявлении параметра-массива как const в функцию просто передается указатель на исходный массив и значение High=Count-1 |
Сообщ.
#18
,
|
|
|
Fr0sT в итоге что, в первом посте у тебя финальная версия кода?
Если нет - то приведи в порядок, перед НГ будем причесывать раздел и переносить все в FAQ, поэтому желательно чтобы все было готово. |
Сообщ.
#19
,
|
|
|
Rouse_, готово.
|