Как удалить пробелы из строки с оператором while do? паскаль - PullRequest
0 голосов
/ 09 мая 2018

У меня есть текст, и мне нужно удалить пробелы в начале текста и в конце текста. И я могу сделать это только с оператором. Как я могу это сделать? Вот программный код

     program RandomTeksts;
     uses crt;
     var
     t:String;
     l, x, y:Integer;

    const tmin=1; tmax=30;
    label
    Start,
    end;
    begin
    Start:
    clrscr;
    writeln('write text (from ',tmin,' to ',tmax,' chars): ');
    readln(t);
    l:=length(t);

    if (l<tmin) or (l>tmax) then
    begin
    writeln('Text doesn't apply to rules!');
    goto end;
    end;
    clrscr;
    begin
    randomize;
    repeat
    x:=random(52+1);
    y:=random(80+1);
    textcolor(white);

    gotoxy(x,y);
    writeln(t);

    delay(700);
    clrscr;
    until keypressed;
    end;
    ord (readkey)<>27 then
    goto Start;
    end:
    end.

1 Ответ

0 голосов
/ 09 мая 2018

Академическая задача: Удалить начальные и конечные пробелы из строки, используя петлю while.

Как мы подходим к этой проблеме?

Что ж, мы, конечно, хотели бы создать функцию , которая обрезает строку. Таким образом, мы можем просто вызывать эту функцию каждый раз, когда нам нужно выполнить такую ​​операцию. Это сделает код более читабельным и простым в обслуживании.

Очевидно, что эта функция принимает строку и возвращает строку. Следовательно, его декларация должна быть

function Trim(const AText: string): string;

Здесь я следую условию добавления префикса к аргументу "A". Я также использую префикс const, чтобы сообщить компилятору, что мне не нужно изменять аргумент внутри функции; это может улучшить производительность (хотя и незначительно).

Определение будет выглядеть так:

function Trim(const AText: string): string;
begin
  // Compute the trimmed string and save it in the result variable.
end;

Первая попытка

Теперь давайте попробуем реализовать этот алгоритм, используя цикл while. Наша первая попытка будет очень медленной, но за ней довольно легко последовать.

Сначала давайте скопируем строку аргумента AText в переменную result; когда функция вернется, значением result будет его возвращаемое значение:

result := AText;

Теперь давайте попробуем удалить ведущих пробелов.

while result[1] = ' ' do
  Delete(result, 1, 1);

Мы проверяем, является ли первый символ result[1] пробелом, и если это так, мы используем процедуру Delete, чтобы удалить его из строки (в частности, Delete(result, 1, 1) удаляет символ 1 из строки начиная с символа с индексом 1). Затем мы делаем это снова и снова, пока первый символ не станет чем-то отличным от пробела.

Например, если result изначально равно ' Hello, World!', это сделает его равным 'Hello, World!'.

Полный код, пока:

function Trim(const AText: string): string;
begin
  result := AText;
  while result[1] = ' ' do
    Delete(result, 1, 1);
end;

Теперь попробуйте сделать это со строкой, состоящей только из пробелов, таких как ' ', или пустой строки, ''. Что просходит? Почему?

Подумайте об этом.

Очевидно, что в таком случае result рано или поздно будет пустой строкой, а затем символ result[1] не будет существовать. (Действительно, если бы существовал первый символ result, result имел бы длину не менее 1, и поэтому это не была бы пустая строка, состоящая ровно из нулевых символов.)

Доступ к несуществующему символу приведет к сбою программы.

Чтобы исправить эту ошибку, мы изменим цикл следующим образом:

while (Length(result) >= 1) and (result[1] = ' ') do
  Delete(result, 1, 1);

Из-за метода, известного как «ленивая логическая оценка» (или «оценка короткого замыкания» ), второй операнд оператора and, то есть result[1] = ' ', даже не будет запускается, если первый операнд, в данном случае Length(result) >= 1, имеет значение false. Действительно, false and <anything> равно false, поэтому мы уже знаем значение соединения в этом случае.

Другими словами, result[1] = ' ' будет оцениваться только если Length(result) >= 1, и в этом случае ошибки не будет. Кроме того, алгоритм выдает правильный ответ, потому что если мы в конечном итоге обнаружим, что Length(result) = 0, ясно, что мы закончили и должны вернуть пустую строку.

Удаление завершающих пробелов аналогичным образом, в итоге мы получим

function Trim(const AText: string): string;
begin
  result := AText;

  while (Length(result) >= 1) and (result[1] = ' ') do
    Delete(result, 1, 1);

  while (Length(result) >= 1) and (result[Length(result)] = ' ') do
    Delete(result, Length(result), 1);
end;

крошечное улучшение

Мне не совсем нравятся литералы пробела ' ', потому что визуально сложно определить, сколько пробелов есть. Действительно, у нас может быть даже другой символ пробела, чем простой пробел. Следовательно, я бы написал #32 или #$20 вместо этого. 32 (десятичный) или $20 (шестнадцатеричный) - код символа обычного пробела.

(намного) лучшее решение

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

Гораздо лучший алгоритм будет просто определять количество начальных и конечных пробелов путем чтения символов в строке, а затем за один шаг выполнить выделение памяти для новой строки.

В следующем коде я определяю индекс FirstPos первого непробельного символа в строке и индекс LastPos последнего непробельного символа в строке:

function Trim2(const AText: string): string;
var
  FirstPos, LastPos: integer;
begin

  FirstPos := 1;
  while (FirstPos <= Length(AText)) and (AText[FirstPos] = #32) do
    Inc(FirstPos);

  LastPos := Length(AText);
  while (LastPos >= 1) and (AText[LastPos] = #32) do
    Dec(LastPos);

  result := Copy(AText, FirstPos, LastPos - FirstPos + 1);

end;

Я оставлю это в качестве упражнения для читателя, чтобы выяснить точную работу алгоритма. В качестве бонусного упражнения попробуйте сравнить два алгоритма: насколько быстрее последний? (Подсказка: речь идет о порядках!)

Простой тест

Ради полноты я написал следующий очень простой тест:

const
  N = 10000;
var
  t: cardinal;
  dur1, dur2: cardinal;
  S: array[1..N] of string;
  S1: array[1..N] of string;
  S2: array[1..N] of string;
  i: Integer;
begin

  Randomize;

  for i := 1 to N do
    S[i] := StringOfChar(#32, Random(10000)) + StringOfChar('a', Random(10000)) + StringOfChar(#32, Random(10000));

  t := GetTickCount;
  for i := 1 to N do
    S1[i] := Trim(S[i]);
  dur1 := GetTickCount - t;

  t := GetTickCount;
  for i := 1 to N do
    S2[i] := Trim2(S[i]);
  dur2 := GetTickCount - t;

  Writeln('trim1: ', dur1, ' ms');
  Writeln('trim2: ', dur2, ' ms');

end.

Я получил следующий вывод:

trim1: 159573 ms
trim2: 484 ms
...