如何去除字符周围的空格?

9

假设我有以下字符串:

s := 'This , is,       the Delphi  ,    World!';

我希望得到以下输出:
Result := 'This,is,the Delphi,World!';

基本上我需要一个程序,它可以去除逗号字符(我的分隔符)前后出现的所有空格,同时保留其他单词之间的空格。
非常感谢任何帮助。
您对这个解决方案有何看法?
function RemoveSpacesAroundDelimiter(var aString: string; aDelimiter:
    string): string;
begin
  while AnsiContainsText(aString, aDelimiter + ' ') do
    begin
    aString := StringReplace(aString, ', ', aDelimiter, [rfReplaceAll, rfIgnoreCase]);
    end;

  while AnsiContainsText(aString, ' ' + aDelimiter) do
    begin
    aString := StringReplace(aString, ' ' + aDelimiter, aDelimiter, [rfReplaceAll, rfIgnoreCase]);
    end;

  Result := aString;
end;

thanks

fabio


4
我不了解Delphi,但您可以使用逗号作为分隔符将字符串拆分,然后对每个结果子字符串调用Trim函数(http://www.delphibasics.co.uk/RTL.asp?Name=Trim),然后再次组装您的字符串(可能有一个Join方法可用于字符串列表)。 - Klaus Byskov Pedersen
哪个版本的Delphi?如果您有XE或更高版本,则可以使用正则表达式来完成此操作。 - David Heffernan
我更喜欢使用stringReplace的解决方案,即使所有情况都没有被处理。 - philnext
您的解决方案更通用,因为您可以更改分隔符,但是我的“更轻量级”。只是一个备注:我认为您不需要将字符串作为Var参数传递到函数中,因为您使用Result,除非您想将其用作函数或类似过程。 - philnext
11个回答

10

听起来这似乎是TStringList的任务。

function UltraTrim(Value: string): string;
var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    // Prevent the stringlist from using spaces as delimiters too.
    sl.StrictDelimiter := True;

    // Set the comma separated text.
    sl.CommaText := Value;

    // Trim each item.
    for i := 0 to sl.Count -1 do
      sl[i] := Trim(sl[i]);

    // Concat back to comma separated string.
    Result := sl.CommaText;
  finally
    sl.Free;
  end;

end;

8
可能是有史以来最有用的组件 - Hugh Jones
8
除了它不是一个组件之外,我完全同意。TStringList 在许多时候都救了我的命。 - GolezTrol
1
这也会从字符串的开头和结尾删除空格。我认为它们不应该被删除,因为它们没有与分隔符相邻。 - Rob Kennedy
1
@Uwe,问题有两个地方与您的陈述相矛盾。首先,它要求仅在逗号字符之前或之后出现时删除空格“ONLY”。大写字母和斜体字使这成为一个非常强的要求。其次,Fabio的代码仅删除紧邻定界符的空格。 - Rob Kennedy
2
你对“共识”这个词有一种奇怪的理解,@Hugh。在我提出这个问题之前,我完全没有看到任何关于Fabio是否想要这样做的讨论,所以我不确定你是如何认为有共识的。即使以前有,现在也没有了。 - Rob Kennedy
显示剩余22条评论

4
一个快速版本可能是:
function RemoveSpacesAroundDelimiter(const aString: string; aDelimiter: char = ','): string;
var S, D, D2: PChar;
begin
  SetLength(result,length(aString));
  if aString<>'' then
  begin
    S := pointer(aString);
    D := pointer(result);
    while S^<>#0 do
    begin
      if S^=' ' then
      begin
        D2 := D;
        repeat
          inc(S);
          D^ := ' ';
          inc(D);
        until S^<>' ';
        if S^=#0 then
          break;
        if S^=aDelimiter then
          D := D2; // trim spaces before comma
      end;
      D^ := S^;
      if (S[0]=aDelimiter) and (S[1]=' ') then
        repeat inc(S) until S^<>' ' else // trim spaces after comma
        inc(S);
      inc(D);
    end;
    SetLength(result,D-pointer(result));
  end;
end;

一些示例代码:

  assert(RemoveSpacesAroundDelimiter('one two,three')='one two,three');
  assert(RemoveSpacesAroundDelimiter('one two , three')='one two,three');
  assert(RemoveSpacesAroundDelimiter('one,two,three')='one,two,three');
  assert(RemoveSpacesAroundDelimiter('one   ,   two,  three')='one,two,three');

+1。我添加了一个“快速”的答案,但删除了它,因为你的更快。 - Cosmin Prund
1
请注意,此代码假定字符串不包含任何 #0 字符。 - Rob Kennedy
1
@RobKennedy 一个文本字符串中不应该包含任何#0字符,否则它将破坏大量的Windows API。谁会在这样一个Unicode字符串的中间放置一个#0呢? - Arnaud Bouchez
2
@CosminPrund,我喜欢你的版本。它是一个清晰的状态机示例(我的代码将其隐藏在代码中)。所以对我来说,它听起来不像是重复的版本。 - Arnaud Bouchez
如果你想区分文本字符串和其他字符串,那很好。(问题没有说明。)尽管如此,我仍然认为在你的回答中指出这里的假设是很重要的,即使不是针对Fabio的具体用途,也是为了后来者能够更好地完成类似的任务。 - Rob Kennedy

3
如果您使用的是 Delphi XE 或更高版本,您可以使用正则表达式在一行代码中轻松实现此操作。
program regex;

{$APPTYPE CONSOLE}

uses
  RegularExpressions;

const
  Input = 'This , is,       the Delphi  ,    World!';

begin
  Writeln(TRegEx.Replace(Input, ' *, *', ','));
  Readln;
end.

自然地,这不是提供的解决方案中最快的运行方式,但也许这对你并不重要。

3
将字符逐个复制到目标缓冲区,但要查找空格和定界符,并记住上一次复制非空格字符的位置。如果看到一个空格且你复制的最后一个非空格字符是定界符,则跳过该空格。如果它是一个空格且你复制的最后一个字符不是定界符,则将其复制到目标中,但请记住最后添加的非空格字符。这样,如果你以后看到一个定界符,就可以返回并覆盖它。
function RemoveSpacesAroundDelimiter(const AString: string; ADelimiter: Char): string;
var
  c: Char;
  dest: Integer;
  LastNonSpace: Integer;
  HaveDelimiter: Boolean;
begin
  Assert(ADelimiter <> ' ');
  SetLength(Result, Length(AString));
  dest := 1;
  LastNonSpace := 0;
  HaveDelimiter := False;
  for c in AString do begin
    if (c = ' ') and HaveDelimiter then
      continue; // Skip this character

    if c = ADelimiter then begin
      dest := LastNonSpace + 1;
      HaveDelimiter := True;
    end else
      HaveDelimiter := False;
    Result[dest] := c;
    if c <> ' ' then
      LastNonSpace := dest;
    Inc(dest);
  end;
  SetLength(Result, dest - 1);
end;

从概念上讲与Arnaud的解决方案相同。更易读,因为您不使用指针,但效率较低,因为Delphi每次执行Result[dest] := c时都会插入保护代码。 - Cosmin Prund
2
我点了赞。如果我想要比我的字符串列表解决方案更好的性能,我会选择这个。它在性能和可读性之间达到了很好的平衡。 - GolezTrol
@GolezTrol 这只是个人口味问题。对我来说,指向当前字符的指针比字符串变量加上当前索引更容易阅读和理解。这样就少了一个要记住的变量 :-) - Arioch 'The

1
你可以使用正则表达式。您想要查找由任意数量空格前导或后继的分隔符,并将其全部替换为单个副本的分隔符。
function RemoveSpacesAroundDelimiter(const AString: string; const ADelimiter: string): string;
var
  re: TPerlRexEx;
begin
  re := TPerlRegEx.Create;
  try
    re.RegEx := '\s*' + TPerlRegEx.EscapeRegExChars(ADelimiter) + '\s*';
    re.Subject := AString;
    re.Replacement := TPerlRegEx.EscapeRegExChars(ADelimiter);
    re.ReplaceAll;
    Result := re.Subject;
  finally
    re.Free;
  end;
end;

较新版本的 Delphi 可以使用内置的 RegularExpressionCore 单元。旧版本可以使用等效的 Jan Goyvaerts 的 PerlRegEx 单元Mick 之前发布了 一个答案 来演示这一点,但他删除了它,因为他的正则表达式有误(删除了与分隔符相邻的所有空格,而不仅仅是它们)。

Rob,你需要与名为“RegularExpressions”的单元进行交互。它使用“RegularExpressionCore”作为引擎。高级单元允许根据我的答案编写非常简洁的代码。 - David Heffernan

1

最简单和最容易的方法是使用正则表达式。你不需要一个庞大而复杂的代码块来解决这样一个简单的问题。不幸的是,我现在没有 Delphi,无法测试这段代码,但如果它不完全像这样,那么它非常接近:

s := 'This , is,       the Delphi  ,    World!';
RegEx := TRegEx.Create('[ ]*,[ ]*');
CleanStr := RegEx.Replace(s, ',');

1
我有这个解决方案:
slValores.DelimitedText := StringReplace(sListSummary,' ','',[rfReplaceAll]);

1
请不要仅仅发布代码作为答案,还要提供解释您的代码是如何解决问题的。带有解释的答案通常更有帮助和更高质量,并且更有可能吸引赞同。 - Dima Kozhevin

0

我认为这很值得添加,因为它可以与早期版本的Delphi一起使用,而StringList解决方案(我喜欢)不行。

我相信它也相当快速,并且非常简单易读易懂。

function TForm1.UltraTrim(const InString : String; Delim : Char) : String;
var
  Buf : String;
  i : Integer;
  Token : String;
begin
  Result := '';
  if Trim(InString) <> '' then begin
    i := 1;
    Buf := StringReplace(InString, Delim, #0, [rfReplaceAll]) + #0;
    while i < Length(Buf) do begin
      Token := StrPas(@Buf[i]);
      i := i + Length(Token) + 1;
      Result := Result + Delim + Trim(Token);
    end;
    Result := Copy(Result,2,Length(Result));
  end;
end;

0

0

使用这个函数:

function MBTrim(iStr :string):string;
const CTc= 3{Conditions Count};
      CT :array[0..(CTc-1),0..1]of string= ( (' ,', ','), (', ', ','), ('  ', ' ') );
var   i  :Integer;
begin
  for i := 0 to CTc-1 do while Pos(CT[i,0], iStr) > 0 do
    iStr:= StringReplace(iStr, CT[i,0], CT[i,1], [rfReplaceAll, rfIgnoreCase]);
  Result:= Trim(iStr);
end;

你可以简单地添加其他条件。

例如,我会添加(' ', ' ')来转换单词之间的空格,就像:

'This , is,       the       Delphi  ,    World!'

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