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

时间:2018-05-09 18:45:15

标签: pascal freepascal

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

  $da $request->birthday_day. ' ' . $request->birthday_month.' '. $request->birthday_year

1 个答案:

答案 0 :(得分:5)

学术问题:使用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的字符开始。然后我们一次又一次地这样做,直到第一个角色不是空格。

例如,如果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的长度至少为1,因此它不会是空字符串,它只包含零个字符。)

访问不存在的字符会导致程序崩溃。

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

result

由于称为“延迟布尔评估”(或'short-circuit evaluation')的技术,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.