如何使用while运算符从字符串中删除空格?帕斯卡尔

-5 pascal freepascal

我有文本,我需要从文本的开头和文本的结尾删除空格.我只能用while do operator来做.我怎样才能做到这一点?这是程序代码

     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.
Run Code Online (Sandbox Code Playgroud)

And*_*and 5

学术问题:使用while循环从字符串中删除前导和尾随空格.

我们如何处理这个问题?

好吧,我们当然想创建一个修剪字符串的函数.这样,我们可以在每次需要执行此类操作时简单地调用此函数.这将使代码更易读,更易于维护.

显然,此函数接受一个字符串并返回一个字符串.因此它的声明应该是

function Trim(const AText: string): string;
Run Code Online (Sandbox Code Playgroud)

在这里,我遵循"A"前缀参数的约定.我也使用const前缀告诉编译器我不需要修改函数内的参数; 这可以提高性能(虽然非常轻微).

定义将如下所示:

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

第一次尝试

现在,让我们尝试使用while循环实现此算法.我们的第一次尝试将非常缓慢,但相当容易遵循.

首先,让我们将参数字符串复制ATextresult变量中; 当函数返回时,值result将是其返回值:

result := AText;
Run Code Online (Sandbox Code Playgroud)

现在,让我们尝试删除前导空格字符.

while result[1] = ' ' do
  Delete(result, 1, 1);
Run Code Online (Sandbox Code Playgroud)

我们测试第一个字符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;
Run Code Online (Sandbox Code Playgroud)

现在尝试使用仅包含空格字符的字符串,例如' '空字符串''.怎么了?为什么?

想一想.

显然,在这种情况下,result迟早会是空字符串,然后字符result[1]就不存在了.(实际上,如果第一个字符result存在,则result长度至少为1,因此它不会是空字符串,它只包含零个字符.)

访问不存在的字符会使程序崩溃.

要修复此错误,我们将循环更改为:

while (Length(result) >= 1) and (result[1] = ' ') do
  Delete(result, 1, 1);
Run Code Online (Sandbox Code Playgroud)

由于称为"延迟布尔评估"(或"短路评估")的技术,操作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;
Run Code Online (Sandbox Code Playgroud)

微小的改进

我不太喜欢空格字符文字' ',因为从视觉上讲有多少空格是有点困难的.实际上,我们甚至可能拥有与简单空间不同的空白字符.因此,我会写#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;
Run Code Online (Sandbox Code Playgroud)

我将把它作为练习让读者弄清楚算法的精确工作原理.作为奖励练习,尝试对两种算法进行基准测试:最后一种算法的速度要快多少?(提示:我们正在谈论数量级!)

一个简单的基准

为了完整起见,我写了以下非常简单的测试:

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.
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

trim1: 159573 ms
trim2: 484 ms
Run Code Online (Sandbox Code Playgroud)