如何从字符串中删除字符,直到第一个字符是字母?

4
我有一个处理字符串的程序(使用Pascal语言)。在读取字符串后,如果第一个字符不是字母,则需要删除所有第一个字母之前的字符,直到第一个字母为止。我已经尝试写了几次,但总是会删除整个字符串或什么都不删除。
如果程序读取"123%^&abc",则结果应该是"abc"。 在ASCII表中,字母的编码范围为65..90和97..122。
以下是我的代码:
variables    a: set of 65..90;
             b: set of 97..122;
-------------------
  bool:=false;
  While (bool=false) do
  begin
    Writeln(s[1]);
    If (Ord(s[1]) in a) or (Ord(s[1]) in b) then
    begin
    bool:=true;
    end else
    delete(s,1,1);
  end;

我不明白为什么它不起作用? 你能帮我处理一下这个小程序吗?谢谢。


1
并且,和往常一样,我添加了Delphi标签,以便您的问题能够得到真正的关注! - Andreas Rejbrand
1
a和b是未初始化的变量。仅仅因为它们被定义为可以包含65到90和97到122值的集合,并不意味着所有这些位都已经设置。 - Thorsten Engler
2
@Thorsten:确实,人们必须编写const a = [65..90]; b = [97..122]。我假设OP已经这样做了,但你可能是对的。也许OP编写了var a: set of 65..90; b: set of 97..122,它将ab声明为变量,可以通过后续赋值存储指定范围内的任何整数集。对于OP的教训可能是在问题中包含确切不起作用的代码,而不是伪代码!(65..90是所谓的“子范围类型”;因此,set of 65..90类似于set of byte,但更挑剔一些。) - Andreas Rejbrand
当 (bool=false) 时执行 ... 天哪。首先,你为什么要将变量命名为 bool,而不是(例如)completed?Bool 可能会被混淆为类型名称,因此对于其他任何事物来说都是一个糟糕的名称。接下来,你为什么不直接写 while not completed do?这样会更易读,也更能传达你的意图。 - JensG
1
谢谢你,@JensG!我考虑了将近3年。现在终于有意义了 :) - va.
显示剩余3条评论
2个回答

13

你可以这样做

function RemoveNonAlphaASCIIFromStart(const Str: AnsiString): AnsiString;
const
  ALPHA = ['A'..'Z', 'a'..'z'];
var
  i: Integer;
  firstIndex: integer;
begin
  result := '';
  firstIndex := 0;
  for i := 1 to length(Str) do
    if Str[i] in ALPHA then
    begin
      firstIndex := i;
      break;
    end;
  if firstIndex > 0 then
    result := Copy(Str, firstIndex, length(Str));
end;

或者,作为一个过程

procedure RemoveNonAlphaASCIIFromStart(var Str: AnsiString);
const
  ALPHA = ['A'..'Z', 'a'..'z'];
var
  i: Integer;
  firstIndex: integer;
begin
  firstIndex := 0;
  for i := 1 to length(Str) do
    if Str[i] in ALPHA then
    begin
      firstIndex := i;
      break;
    end;
  if firstIndex > 0 then
    Delete(Str, 1, firstIndex - 1)
  else
    Str := '';
end;

如果您需要更复杂的方法来处理Unicode Delphi,可以查看我的类似问题的答案。[这会从字符串中删除所有非字母字符。]

那么,为什么您的算法不起作用?嗯,它应该起作用,并且它对我有效。但请注意,它可以以稍微更优雅的形式书写。

const
  ALPHA = ['A'..'Z', 'a'..'z'];

while true do
  if (length(s) = 0) or (s[1] in ALPHA) then
    break
  else
    delete(s, 1, 1);

然而,原始代码存在一个问题,如果s是空字符串,则会失败。实际上,那时候s[1]不存在。如果s完全由非字母字符组成(例如'!"#¤%),它也无法工作。


比 OP 更好的风格,但就像你一样,我在原始代码中看不到错误。 - CodesInChaos
1
请注意,我在代码的最后一个块中的 if 语句中依赖于布尔短路求值(BSCE)。我不确定普通的 Pascal 是否使用 BSCE。 - Andreas Rejbrand
@Andreas Rejbrand:这有关紧要吗?你只是得到了99个声望而不是100个……似乎大多数人都喜欢你的工作。 - Мסž
1
@moz:你说得对。虽然不是因为声望分,但当有人明显地对我每个回答都进行了负投票时,我觉得感觉很奇怪。我想太多了... - Andreas Rejbrand
@AndreasRejbrand-你知道删除字符(Delete)是否比复制字符串的一部分(Copy)更快吗? - Gabriel

2
虽然之前的解决方案确实可行,但它们非常低效。原因有两个: 1. 在集合中搜索需要时间 2. 每次从字符串中删除一个字符更加低效,因为该字符串(对象)必须在内部删除字符并调整其数组等。
理想情况下,您将把字符串转换为PChar并使用它来检查字符范围。我们将让搜索一直运行,直到找到第一个字母,然后才调用DeleteString方法。以下是我方法的演示:
procedure Frapp;
var
  TheString: string;
  pcStr: PChar;
  StrLen, I: Integer;
begin
  TheString := '123%^&abc';
  StrLen := Length(TheString);
  pcStr := PChar(TheString);

  for I := 0 to StrLen - 1 do
  begin
    if ((pcStr^ >= #65) and (pcStr <= #90)) or ((pcStr >= #97) and (pcStr <= #122)) then
    begin
      Delete(TheString, 1, I);
      Break;
    end;
    Inc(pcStr);
  end;
end;

1
请注意,我的两个算法都不会受到重复的Delete调用的影响(这确实非常低效)。无论如何,您上面的代码是无效的... - Andreas Rejbrand
抱歉,你不经常调用Delete。但是为什么你说我的代码不能工作? - fmotis
我试过了。它生成的是^&abc,而不是预期的abc。更新:但当时我非常匆忙(要赶公交车)!实际上,在Delphi 2009及更高版本(即Unicode)中无法正常工作!但是,对于纯ANSI字符串,它确实可以工作!请原谅我的错误! - Andreas Rejbrand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接