страничка tripsin'а


Главная | Статьи | Заметки | Файлы| Ссылки | я

Некоторые интересные строковые функции Delphi.

Автор: Орехов Роман aka tripsin

Не изобретай лишних сутей

AdjustLineBreaks

 

function AdjustLineBreaks(const S: string [; Style: TTextLineBreakStyle]): string;

 

Вот так определена эта функция в SysUtils:

 

  TTextLineBreakStyle = (tlbsLF, tlbsCRLF);

function AdjustLineBreaks(const S: string; Style: TTextLineBreakStyle =

        {$IFDEF LINUX} tlbsLF {$ENDIF}

        {$IFDEF MSWINDOWS} tlbsCRLF {$ENDIF}): string;

 

(А в модуле ComCtrls.pas эта функция написана на ассемблере для работы с PChar:

function AdjustLineBreaks(Dest, Source: PChar): Integer; assembler;)

 

Функция AdjustLineBreaks приводит строку S к формату установленной OS, преобразуя символы перевода строки (одиночные символы возврата каретки (#13), перевода строки (#10), и пары CR-LF) применительно к данной ОС. Файлы, копируемые из систем Unix и Macintosh, содержат иные символы окончания строк, чем могут "ввести в заблуждение" многие программы DOS и Windows. (В Macintosh используется только символ возврата каретки; в Unix - только символ перевода строки.)

Второй необязательный параметр позволяет управлять форматированием. Ты можешь адаптировать переносы строк, как под Windows (tlbsCRLF), так и под Unix (tlbsLF). По умолчанию он определяется установленной ОС.

var UnixStr: String;

begin // Преобразуем текст под Unix

  UnixStr := AdjustLineBreaks(Memo1.Text, tlbsLF);

end;

Эта функция часто может тебе понадобиться, чтобы общаться с серверами в сети, т.к. серверы чаще всего работают по никсами (а ты конечно под Виндой :) ).

Для примера переделаем WhoIs-клиент из всеми любимой ДГХ (стр. 142 «Их разыскивают бойцы 139-го порта»).  Там, чтобы отформатировать полученную от сервера строку, написан цикл, сканирующий строку на символ #10 и делящий текст на кусочки. Мы сделаем все это в одной строке (для надежности я добавил обработку ошибок):

 

procedure TForm1.btnFindClick(Sender: TObject);

var

  FindResult: String;

begin

ResultMemo.Clear;

  try

    FindResult := IdWhoIs.WhoIs(edtDomain.Text); // Получаем данные

    // Обработка – всего одна строка

    ResultMemo.Text := AdjustLineBreaks(FindResult);

  except

    on E: Exception do

    ResultMemo.Lines.Add('ОШИБКА: ' + E.Message);

  end;

end;

 

StringReplace

 

type TRepiaceFlags = set of (rfReplaceAll,  rfIgnoreCase);

function StringReplace(const S,  OldSubStr,  NewSubStr:  string; Flags:  TReplaceFlags):  string;

 

StringReplace возвращает копию S, где OldSubStr заменена на NewSubStr. Если Flags содержит rfReplaceAll, заменяются все вхождения OldSubStr; иначе заменяется только первое вхождение. Поиск OldSubStr выполняется с учетом регистра, если только не включен флаг rflgnoreCase.

Пример замены текста в TMemo:

 

procedure TForm1.Button1Click(Sender: TObject);

begin

  Memo1.Lines.Add('Зачем ты учишь Delphi?');

end;

 

procedure TForm1.Button2Click(Sender: TObject);

begin

  Memo1.Text := StringReplace(Memo1.Text, 'ты учишь', 'он учит', [rfReplaceAll, rfIgnoreCase]);

end;

 

WrapText

 

function WrapText(const Line, BreakStr: string; BreakChars: TSysCharSet; MaxCol:  Integer): string;overload;

function WrapText(const Line: string;  MaxCol:  Integer = 45): string;overload;

 

Функция WrapText возвращает копию Line, разбитую на несколько строк шириной MaxCol столбцов. Каждая строка разбивается, когда ее длина доходит до MaxCol символов. Разбиение производится там, где есть символы из множества BreakChars. При нахождении символа из  BreakChars, после  него вставляется строка BreakStr. (В Delphi 5 как существующие переводы строк рассматриваются символы #13 и #10, независимо от BreakStr.)
Вторая форма WrapText использует [' ', ' -' , #9] (пробел, дефис, табуляция) в качестве BreakChars и #13#10 (возврат каретки, перевод строки) в качестве BreakStr. Получается эта форма просто переносит строку по словам.

Сама Delphi к этой функции в своих модулях не обращается. Для разных компонентов пишутся аналоги с другими названиями. Я не додумался до сколько-нибудь полезного примера, т.к. например в TMemo и TRichEdit это свойство уже реализовано.

В моем примере TListBox заполняется строками, примерно, как в TMemo и ширина и количество строк меняются, при изменении размера TListBox. Также в примере есть алгоритм подгонки ширины строк к ширине окна:

 

procedure TForm1.FormResize(Sender: TObject);

var

 S: String;

 DC: HDC;

 Metrics: TTextMetric;

 CharsInWidth: Integer; //Сколько символов влезет в ширину окна

 OldMapMode: Integer;

begin

  S :=     'Очень длинная строка, которую мы сейчас разбиваем на ';

  S := S + 'кусочки и заполняем ими строки TListBox по всей ширине. ';

  S := S + 'Ширину TListBox мы меняем, изменяя размеры формы, ';

  S := S + 'т.к. у TListBox свойство Align равно alClient.';

  DC := GetWindowDC(ListBox1.Handle); // Получаем контекст окна

    SelectObject(DC, ListBox1.Font.Handle);//Выбираем в него шрифт окна

    OldMapMode := GetMapMode(DC);// Запоминаем старый режим отображения

    SetMapMode(DC, MM_TEXT); //Устанавливаем отображение в пикселях

    GetTextMetrics(DC, Metrics); //Получаем параметры шрифта

    SetMapMode(DC, OldMapMode);//Восстанавливаем режим отображения

  ReleaseDC(ListBox1.Handle, DC); //Освобождаем контекст

  //Получаем количество средних символов на ширину окна (6 - подобрано)

  CharsInWidth := ListBox1.ClientWidth div Metrics.tmAveCharWidth - 6;

  ListBox1.Clear; //Очищаем TListBox

  ListBox1.Items.Text := WrapText(S, CharsInWidth); //Заполняем TListBox

  Caption := 'Количество строк в ListBox = ' + IntToStr(ListBox1.Count);

end;

IsValidIdent

 

function IsValidIdent(const Ident: string): Boolean;

 

Функция IsValidIdent возвращает True, если Ident содержит корректный идентификатор Delphi Pascal, т. е. строку, первый символ которой является буквой или подчеркиванием: ['A'..'Z', 'a'..'z', '_'] ,а последующие символы - буквами, цифрами или символами подчеркивания: ['A'..'Z', 'a'..'z', '0..'9', '_'].

Наверняка пригодится где-нибудь при разработке компонентов для проверки корректности установленных свойств. Например Delphi использует эту функцию при установке имени компонента TComponent.Name.

Хочется показать определение этой функции. Смотри какой простой и правильный код:

 

function IsValidIdent(const Ident: string): Boolean;

const

  Alpha = ['A'..'Z', 'a'..'z', '_'];

  AlphaNumeric = Alpha + ['0'..'9'];

var

  I: Integer;

begin

  Result := False;

  if (Length(Ident) = 0) or not (Ident[1] in Alpha) then Exit;

  for I := 2 to Length(Ident) do if not (Ident[I] in AlphaNumeric) then Exit;

  Result := True;

end;

LastDelimiter

 

function LastDelimiter(const Delimiters,  S :   string):  Integer;

 

Функция LastDelimiter возвращает индекс последнего (самого правого) вхождения любого из символов Delimters в строке S. Если ни один из символов Delimiters не присутствует в S, LastDelimiter возвращает ноль. Delimiters не может быть многобайтовой строкой, и нельзя использовать #0 в качестве одного из разделителей.

Может, например, понадобиться для разбора пути к файлу на каталоги: от конечного к корню диска.

А я для примера написал функцию, которая выводит слова в предложении в обратном порядке:

 

function ReverseString(S: String): String;

var

 delim: String;

 LastDelim: Integer;

begin

  delim := ' '; // разделитель - пробел

  Result := ''; // Обнуляем на всякий случай

  repeat

    // Получаем последний пробел

    LastDelim := LastDelimiter(delim, S);

    // Копируем в newstr из tmpstr слово после пробела + пробел

    Result := Result + Copy(S, LastDelim + 1, Length(S)-LastDelim) + delim;

    // Укорачиваем tmpstr на последнее слово

    Delete(S,LastDelim, Length(S)-LastDelim + 1);

  until LastDelim = 0; // Работаем пока не кончатся пробелы

  Result := TrimRight(Result); // Удаляем лишний пробел на конце

end;

 

procedure TForm1.FormCreate(Sender: TObject);

begin

 Edit1.Text := 'Ехал грека через реку.';

end;

 

procedure TForm1.Button1Click(Sender: TObject);

begin

 Edit1.Text := ReverseString(Edit1.Text);

end;

 

При работе с PChar для тех же целей используется функция AnsiStrRScan:

function AnsiStrRScan(Str: PChar; Chr: Char): PChar;

Функция AnsiStrRScan выполняет поиск последнего (самого правого) вхождения символа Chr в Str и возвращает указатель на символ в строке Str или nil, если символ не найден.

AnsiQuotedStr и  AnsiExtractQuotedStr

 

function AnsiQuotedStr(const S: string; Quote: Char): string;

 

Функция AnsiQuotedStr возвращает копию S, заключенную в кавычки. Символ кавычек указан в параметре Quote. Вхождения Quote в S повторяются и в результирующей строке. Эта функция предполагает, что в S нет символов #0 (кроме #0, находящегося за концом строки).

 

function AnsiExtractQuotedStr(var Src: PChar; Quote: Char):  string;

 

Функция AnsiExtractQuotedStr выбирает из Src строку, заключенную в кавычки. Первый символ Src должен быть равен Quote, иначе функция сразу завершается, возвращая пустую строку. Если это условие выполняется, строка, заключенная в символы кавычек, копируется и возвращается в качестве результата функции. Повторяющиеся символы кавычек преобразуются в одиночные кавычки. Src изменяется, указывая теперь на символ сразу за закрывающей кавычкой. Если в Src нет закрывающей кавычки, то Src присваивается указатель на завершающий байт #0, и функция возвращает весь текст до конца строки.

Эти функции могут тебе понадобится при работе с файлами, при записи-чтении информации. Delphi использует эти функции при чтении-записи свойства TStrings.CommaText.

 

Если хочешь знать еще больше, то запускай Delphi, создавай проект по умолчанию и, удерживая кнопочку CTRL кликай мышкой по слову SysUtils в секции uses. Смещай взгляд свой в левую сторону окна с кодом. И будет там окно Code Explorer. И когда нажмешь ты на плюсик слева от папочки Procedures, то будет тебе счастье. Изучай.

Использованная, так сказать, литература:

  1. Лишнер Р. «Delphi. Справочник» Пер.с англ. – СПб.: Символ-Плюс, 2001.
  2. Фленов М.Е. «Программирование в Delphi глазами ][акера» - СПб.: БХВ-Петербург, 2004.
  3. Исходный код модуля Delphi6 SysUtils.pas.

Опубликована на VR-online в 2006 г.

Hosted by uCoz