вторник, 10 марта 2009 г.

Работа со строками

2. Работа со строками.

2.1. Поиск подстроки в строке.

Обычно программист, перешедший с Pascal на Delphi, сам пишет функцию поиска подстроки в строке. Выглядит она примерно так:
Function FindSubStrInStr(Const Str,SubStr:string):Boolean;
Var
  i,j:integer;
Begin
  Result:=false;
  For i:=1 to Length(str) do
  begin
    If (Str[i]=SubStr[1]) and (Length(substr)<=Length(Str)-(i-1)) then
    begin
      Result:=true;
      For j:=2 to Length(SubStr) do
      begin
        If SubStr[j]<>Str[i+j-1] then
        begin
          Result:=false;
          Break;
        End;
      End;
    End;
    If Result Then
      Break;
  End;
End;

Я написал и отладил эту функцию где-то за 3 минуты, но я уже, когда писал что-то подобное и имел представление что мне нужно. А теперь давайте обратимся к стандартному модулю System. В нем есть замечательная функция Pos.

Функция Pos:

Параметры:
1. Подстрока, тип string.
2. Строка, тип string.

Возвращаемое значение:
Тип integer. Функция возвращает индекс первого символа подстроки в строке. Если подстрока не найдена – 0.

И что же у нас получается? Программист, не знающий о функции Pos потратит минуты 3, пока будет писать свою. Программист, знающий о существовании этой функции потратит на ту же задачу секунд десять. Однако описание этой функции все-таки иногда встречается в книгах.

2.2. Подсчет вхождений подстроки в строку.

Теперь давайте рассмотрим вторую похожую задачу. Нужно подсчитать количество вхождение подстроки в строку. Перепишем нашу функцию:

Function CountSubStrInStr(Const Str, SubStr:string):Integer;
Var
  i,j:integer;
  find:Boolean;
Begin
  Result:=0;
  For i:=1 to length(str) do
  begin
    Find:=false;
    If (Str[i]=SubStr[1]) and (Length(substr)<=Length(Str)-(i-1)) then
    begin
      Find:=true;
      For j:=2 to Length(SubStr) do
      begin
        If SubStr[j]<>Str[i+j-1] then
        begin
          Find:=false;
          Break;
        End;
      End;
    End;
    If Find Then
      Inc(Result);
  End;
End;

На эту функцию я так же потратил где-то три минуты. А можно подключить к проекту модуль StrUtils в котором есть функция PosEx.

Функция PosEx:

Параметры:
1. Подстрока, тип string.
2. Строка, тип string.
3. Смещение (индекс символа, с которого начинается поиск), тип integer;

Возвращаемое значение:
Тип integer. Функция возвращает индекс первого символа подстроки в строке. Если подстрока не найдена – 0.

Теперь функция CountSubStrInStr будет выглядеть так:

Function CountSubStrInStr(Const Str, SubStr:string):Integer;
var
  i:integer;
begin
  Result:=0; //Значение по-умолчанию 0 (подстрок не найдено).
  i:=1; //Начинаем поиск с первого символа.
  repeat
    i:=PosEx(SubStr,Str,i); //Присваиваем i результат работы PosEx.
    if i<>0 then //Если подстрока найдена…
    begin
      Inc(Result); //…увеличиваем результат на единицу…
      i:=i+Length(SubStr); //…и увеличиваем значение i на длину подстроки.
    end;
  until i=0; //Выходим из цикла, когда подстрок больше (вообще) не найдено.
end;

По-моему, смориться куда лучше, да и скорость выполнения значительно выше.

2.3 Разбить предложение на слова.

Это задача является одной из моих любимых. Помниться еще на первом курсе на её решение на Pascal потратил минут 40. Потом как-то, пришлось решать заново уже на Delphi, и минут за 10 накидал такую процедуру:

Procedure DivideString(const Str:string; StrList:TStrings);
Type
  Mnoj=set of char;
Var
  i:integer;
  subs:string;
Сonst
  Mn:Mnoj=['А'..'Я', 'а'..'я', '0'..'9', '-', #39];
Begin
  subs:='';
  for i:=1 to length(str) do
  begin
  if not (str[i] in Mn) then
    Continue;
    subs:=subs+str[i];
    if (not (str[i+1] in Mn)) or (i=length(str)) then
    begin
      StrList.Add(subs);
      subs:='';
    end;
  end;
End;

Она вполне работоспособна и отлично справляется со своими обязанностями, разбивая строку за один проход (а ведь были предложения сначала разбить предложение на слова, используя в качестве разделителей только пробелы, а потом еще и знаки препинания в другом цикле отсекать). Но вместо того, чтобы тратить 10 минут, можно потрать одну, и использовать в коде функцию ExtractStrings.

Функция ExtractStrings:

Параметры:
1. Множество символов используемых в качестве делителей, тип TSysCharSet.
2. Множество символов, которые будут игнорироваться, только если они находятся в начале строки, тип TSysCharSet.
3. Строка, тип PChar.
4. Список строк, в который будут добавлены получившиеся в результате разбиения слова, тип TStrings.

Возвращаемое значение:
Тип integer. Функция возвращает количество получившихся слов в результате разбиения строки.

Единственный минус, найденный мной в работе этой функции, это не возможность обработать символ «’» в сочетании с русскими буквами. То есть если мы введет строку: «Computed solution d’Alembert’s force», - то получим вполне ожидаемые четыре слова. А если: «Найденное решение и есть д'Аламберова сила», - функция просто откажется разбивать строку после «’» вернув оставшееся как одно слово. То есть мы получим пять слов вместо шести.

Комментариев нет:

Отправить комментарий