Fabio Vitale
Fabio Vitale

Reputation: 2287

How to remove space around a character?

Say I have the following string:

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

I would like the following output:

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

Basically I need a routine that strips ALL occurrences of spaces ONLY if they appears before or after the comma char (which is my delimiter), leaving intact spaces between other words.

Any help is much appreciated.

What do you think of this solution?

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

Upvotes: 9

Views: 3662

Answers (11)

I have this solution:

slValores.DelimitedText := StringReplace(sListSummary,' ','',[rfReplaceAll]);

Upvotes: 1

MohsenB
MohsenB

Reputation: 1921

with this function :

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;

you can add other conditions simply.

for example i add (' ', ' ') to convert space between words like :

'This , is,       the       Delphi  ,    World!'

Upvotes: 0

Arioch 'The
Arioch 'The

Reputation: 16045

Using Jedi Code Library, answer by @GolezTrol can be reformulated using one-liner.

function UltraTrim(Value: string): string;
begin
  Result := JclStringList.Split(Value, ',').Trim.Join(',')
end;

Upvotes: 0

Hugh Jones
Hugh Jones

Reputation: 2694

I thought this was worth adding because it will work with early versions of Delphi, which the stringlist solution (which I liked) does not.

It is alo reasonably quick, I believe, and fairly simple to read and understand.

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;

Upvotes: 0

philnext
philnext

Reputation: 3402

Changed, one more time.

  while (pos(', ',s)>0) or (pos(' ,',s)>0) do   begin
    s := StringReplace(s, ', ', ',', [rfReplaceAll]);
    s := StringReplace(s, ' ,', ',', [rfReplaceAll]);   end;

OK for all the Delphi versions.

Upvotes: -2

David Heffernan
David Heffernan

Reputation: 612993

If you are using Delphi XE or above you can do this trivially in a single line of code, using a regular expression.

program regex;

{$APPTYPE CONSOLE}

uses
  RegularExpressions;

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

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

Naturally this is not the fastest running of the solutions on offer, but maybe that doesn't matter to you.

Upvotes: 3

Rafael Piccolo
Rafael Piccolo

Reputation: 2338

The simpler and easiest way is to use regular expressions. The last thing you would need is a huge complicated code block to solve such a simple problem. Unfortunatly I don't have Delphi with me right now, I can't test this code, but if it's nothing exactly like this, it's very very close:

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

Upvotes: 1

Rob Kennedy
Rob Kennedy

Reputation: 163287

You can use regular expressions. You want to find the delimiter preceded or followed by any number of spaces, and replace it all with a single copy of the delimiter.

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;

Newer Delphi versions can use the built-in RegularExpressionCore unit. Older versions can use the equivalent PerlRegEx unit from Jan Goyvaerts.

Mick previously posted an answer demonstrating this, but he deleted it because he got the regular expression wrong (deleting all spaces instead of just the ones abutting the delimiter).

Upvotes: 1

Rob Kennedy
Rob Kennedy

Reputation: 163287

Copy characters one-by-one into the destination buffer, but look for spaces and delimiters, and remember the last location you copied a non-space character into. If you see a space and the last non-space you copied was the delimiter, then skip the space. If it's a space and the last character you copied wasn't the delimiter, then copy it to the destination, but remember the last non-space you added. That way, if you see a delimiter later, you can go back and overwrite it.

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;

Upvotes: 3

Arnaud Bouchez
Arnaud Bouchez

Reputation: 43033

A fast version could be:

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;

Some test code:

  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');

Upvotes: 4

GolezTrol
GolezTrol

Reputation: 116110

Sounds like a task for 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;

Upvotes: 10

Related Questions