wildzero
asked on
Remove all space, line breaks and paragrahs from text
I have a bunch of text in a Tmemo component, could look like this (could be any text)
Hello my name is bob and
I have two cats, each can catch
four mice a day
Or whatever
What I need is to make it
Hello my name is bob and I have two cats, each can catch four mice a day
Even
HellomynameisbobandIhavetw ocats,each cancatchfo urmiceaday
I have tired replacing all the ' ' with '' but no go, have tried using trim, again no go.
Basically, if I had a 20 line string in a Memo, I want it all on 1 line. Yes I have turned off word wrap.
Hello my name is bob and
I have two cats, each can catch
four mice a day
Or whatever
What I need is to make it
Hello my name is bob and I have two cats, each can catch four mice a day
Even
HellomynameisbobandIhavetw
I have tired replacing all the ' ' with '' but no go, have tried using trim, again no go.
Basically, if I had a 20 line string in a Memo, I want it all on 1 line. Yes I have turned off word wrap.
procedure TForm1.Button1Click(Sender : TObject);
var
I: Integer;
P: Integer;
S: string;
T: string;
begin
T := '';
Memo2.Clear;
for I := 0 to Memo1.Lines.Count-1 do
begin
S := Memo1.Lines[I];
// StringReplace(S, ' ', '', [rfReplaceAll]);
P := Pos(' ', S);
while (P>0) do
begin
Delete(S, P, 1);
P := Pos(' ', S);
end;
T := T + S;
end;
Memo2.Text := T;
end;
var
I: Integer;
P: Integer;
S: string;
T: string;
begin
T := '';
Memo2.Clear;
for I := 0 to Memo1.Lines.Count-1 do
begin
S := Memo1.Lines[I];
// StringReplace(S, ' ', '', [rfReplaceAll]);
P := Pos(' ', S);
while (P>0) do
begin
Delete(S, P, 1);
P := Pos(' ', S);
end;
T := T + S;
end;
Memo2.Text := T;
end;
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Yea I get that ;)
What am getting is with the memo1, if I have 5 pages of text it reduces it to 6-7 lines
WIth a richedit I get it down to 3 lines
So I guess it's a limit with the components, if I load it into a string list ,or maybe just assign it to a stringvar do you think it'd keep 1 line?
What am getting is with the memo1, if I have 5 pages of text it reduces it to 6-7 lines
WIth a richedit I get it down to 3 lines
So I guess it's a limit with the components, if I load it into a string list ,or maybe just assign it to a stringvar do you think it'd keep 1 line?
try to use stringvar as esoftbg wrote and also you can try this :
procedure TForm1.Button1Click(Sender : TObject);
var delimitedtext:String;i,j:i nteger;
delimitedtextremoveallspac es:string;
begin
memo1.Lines.Delimiter:=' ';
memo1.Lines.QuoteChar:='~' ;
setlength(delimitedtext,le ngth(memo1 .text));
setlength(delimitedtextrem oveallspac es,length( memo1.text ));
delimitedtext:=memo1.lines .Delimited Text;
while Pos('~', delimitedtext) > 0 do
delimitedtext[Pos('~', delimitedtext)] := ' ';
// delimitedtext is now delimited text without removing spaces
// delimitedtextremoveallspac es is now delimited text with removing spaces
j:=1;
for i:=1 to length(delimitedtext) do
if delimitedtext[i]<>' ' then
begin
delimitedtextremoveallspac es[j]:=del imitedtext [i];
inc(j);
end;
end;
procedure TForm1.Button1Click(Sender
var delimitedtext:String;i,j:i
delimitedtextremoveallspac
begin
memo1.Lines.Delimiter:=' ';
memo1.Lines.QuoteChar:='~'
setlength(delimitedtext,le
setlength(delimitedtextrem
delimitedtext:=memo1.lines
while Pos('~', delimitedtext) > 0 do
delimitedtext[Pos('~', delimitedtext)] := ' ';
// delimitedtext is now delimited text without removing spaces
// delimitedtextremoveallspac
j:=1;
for i:=1 to length(delimitedtext) do
if delimitedtext[i]<>' ' then
begin
delimitedtextremoveallspac
inc(j);
end;
end;
procedure TForm1.Button1Click(Sender : TObject);
begin
Memo1.Text := StringReplace(Memo1.Text, Chr(13) + Chr(10), ' ', [rfReplaceAll]);
Memo1.Text := StringReplace(Memo1.Text, ' ', '', [rfReplaceAll]);
end;
begin
Memo1.Text := StringReplace(Memo1.Text, Chr(13) + Chr(10), ' ', [rfReplaceAll]);
Memo1.Text := StringReplace(Memo1.Text, ' ', '', [rfReplaceAll]);
end;
Wow, I react really slow today :-)
Well, okay. Some pretty fast code that strips unwanted characters faster than StringReplace:
type
TSkipChars = set of char;
function StripChars( const Value: string; SkipChars: TSkipChars ): string;
var
I, J: Integer;
Max: Integer;
begin
Result := Value;
Max := Length( Value ); // Avoids recalculating the length.
I := 1;
while ( I <= Max ) and not ( Result[ I ] in SkipChars ) do
Inc( I );
if ( I <= Max ) then begin
J := I + 1;
while ( J <= Max ) do begin
if not ( Result[ J ] in SkipChars ) then begin
Result[ I ] := Result[ J ];
Inc( I );
end;
Inc( J );
end;
end;
SetLength( Result, I - 1 );
end;
Yes, it looks complex. It took me over 10 minutes to write so it really must be complex... :-)
50 points for the person who finds a bug in that piece of code of mine... ;-)
type
TSkipChars = set of char;
function StripChars( const Value: string; SkipChars: TSkipChars ): string;
var
I, J: Integer;
Max: Integer;
begin
Result := Value;
Max := Length( Value ); // Avoids recalculating the length.
I := 1;
while ( I <= Max ) and not ( Result[ I ] in SkipChars ) do
Inc( I );
if ( I <= Max ) then begin
J := I + 1;
while ( J <= Max ) do begin
if not ( Result[ J ] in SkipChars ) then begin
Result[ I ] := Result[ J ];
Inc( I );
end;
Inc( J );
end;
end;
SetLength( Result, I - 1 );
end;
Yes, it looks complex. It took me over 10 minutes to write so it really must be complex... :-)
50 points for the person who finds a bug in that piece of code of mine... ;-)
Yes, Geo... You're slow today. Old age? :-)
About that function of mine, call it like:
Memo1.Lines.Text := StripChars( Memo1.Lines.Text, [ #10, #13, #32, '.', ',' ] ) );
However, keep in mind that the line length of a memo isn't endless. Very long strings are either cut off at the end or split in multiple lines. I'm not sure exactly about the maximum line length of a TMemo and TRichBox but it's less than a TStringist can have, per line. A memo or richtext component in Delphi doesn't have a real stringlist connected to it. It just has some stringlist-compatible object linked to the Windows control. It's the Windows control that enforces this limit. The stringlist used by the memo is the TMemoStrings class. I'm not sure about the maximum line length of a Memo, but think it's around 1024, since that's the limit of the Delphi editor.
About that function of mine, call it like:
Memo1.Lines.Text := StripChars( Memo1.Lines.Text, [ #10, #13, #32, '.', ',' ] ) );
However, keep in mind that the line length of a memo isn't endless. Very long strings are either cut off at the end or split in multiple lines. I'm not sure exactly about the maximum line length of a TMemo and TRichBox but it's less than a TStringist can have, per line. A memo or richtext component in Delphi doesn't have a real stringlist connected to it. It just has some stringlist-compatible object linked to the Windows control. It's the Windows control that enforces this limit. The stringlist used by the memo is the TMemoStrings class. I'm not sure about the maximum line length of a Memo, but think it's around 1024, since that's the limit of the Delphi editor.
Alex, exactly, ooold age combined with slooow internet connection :-)
Howzit Alex,
I couldn't find a bug, but here's a more "english" (as opposed to greek... hehehe) version of your solution:
function PCStripChars( const Value: string; SkipChars: TSkipChars ): string;
var
I, J: Integer;
Max: Integer;
begin
Result := Value;
Max := Length( Value ); // Avoids recalculating the length.
j:= 0;
for i:= 1 to Max do
if NOT (Value[i] in SkipChars)
then begin
Inc(j);
result[j]:= Value[i];
end;
SetLength(result,j);
end;
Speedwise, it results in the same as your function.
I couldn't find a bug, but here's a more "english" (as opposed to greek... hehehe) version of your solution:
function PCStripChars( const Value: string; SkipChars: TSkipChars ): string;
var
I, J: Integer;
Max: Integer;
begin
Result := Value;
Max := Length( Value ); // Avoids recalculating the length.
j:= 0;
for i:= 1 to Max do
if NOT (Value[i] in SkipChars)
then begin
Inc(j);
result[j]:= Value[i];
end;
SetLength(result,j);
end;
Speedwise, it results in the same as your function.
Avg time "English" = 0.2083 milliseconds
avg time "Greek" = 0.2173 milliseconds
avg time "Greek" = 0.2173 milliseconds
True, same result. You're using a for-next loop, which is where you gain more speed. :-) Still, we're talking about a 1% difference on a millisecond. :-P
I'll give you (or anyone else) 500 points (in a new topic) if you manage to create a function that's at least twice as fast as mine. :-)
https://www.experts-exchange.com/questions/21165246/Impossible-A-speed-challenge.html for the challenge...
I'll give you (or anyone else) 500 points (in a new topic) if you manage to create a function that's at least twice as fast as mine. :-)
https://www.experts-exchange.com/questions/21165246/Impossible-A-speed-challenge.html for the challenge...
Not exactly that fast... maybe 30% faster (based on a 2mb text file, averaged over 10,000 runs):
function MyStrip(const sValue: string): string;
var
Max: Integer;
i, j: Integer;
begin
Max := Length(sValue);
SetLength(Result, Max);
j := 0;
for i := 1 to Max do
if sValue[i] in ['A' .. 'Z', 'a' .. 'z', '0' .. '9'] then
begin
Inc(j);
Result[j] := sValue[i];
end;
SetLength(Result, j);
end;
Okay, so I cheated a bit in the code =p
DragonSlayer.
function MyStrip(const sValue: string): string;
var
Max: Integer;
i, j: Integer;
begin
Max := Length(sValue);
SetLength(Result, Max);
j := 0;
for i := 1 to Max do
if sValue[i] in ['A' .. 'Z', 'a' .. 'z', '0' .. '9'] then
begin
Inc(j);
Result[j] := sValue[i];
end;
SetLength(Result, j);
end;
Okay, so I cheated a bit in the code =p
DragonSlayer.
>> if sValue[i] in ['A' .. 'Z', 'a' .. 'z', '0' .. '9'] then
Even if the set is a parameter, it would still be faster because it has no Result := Value. There is no need for such an assignment, SetLength(Result, Max) is perfect. Workshop_Alex's would suffer very much if most of the chars in Value are in SkipChars and Value is a very large string. :-)
Another improvement can be gained if we make Value a reference parameter. We know for sure that we're not changing it inside, right?
Even if the set is a parameter, it would still be faster because it has no Result := Value. There is no need for such an assignment, SetLength(Result, Max) is perfect. Workshop_Alex's would suffer very much if most of the chars in Value are in SkipChars and Value is a very large string. :-)
Another improvement can be gained if we make Value a reference parameter. We know for sure that we're not changing it inside, right?
DragonSlayer,
your code does not work properly ....
your code does not work properly ....
Emil, what is wrong with it?
Your code is faster, but it eliminates many symbols as
','
'.'
':'
'!'
'@'
'#'
'$'
'%'
'&'
'*'
'('
')'
'-'
'+'
........
I think only improvement for your code makes it a perfect one:
function MyStrip(const sValue: string): string;
var
Max: Integer;
i, j: Integer;
begin
Max := Length(sValue);
SetLength(Result, Max);
j := 0;
for i := 1 to Max do
if sValue[i] in [#0..#9, #11..#12, #14..#31, #33..#255] then
begin
Inc(j);
Result[j] := sValue[i];
end;
SetLength(Result, j);
end;
','
'.'
':'
'!'
'@'
'#'
'$'
'%'
'&'
'*'
'('
')'
'-'
'+'
........
I think only improvement for your code makes it a perfect one:
function MyStrip(const sValue: string): string;
var
Max: Integer;
i, j: Integer;
begin
Max := Length(sValue);
SetLength(Result, Max);
j := 0;
for i := 1 to Max do
if sValue[i] in [#0..#9, #11..#12, #14..#31, #33..#255] then
begin
Inc(j);
Result[j] := sValue[i];
end;
SetLength(Result, j);
end;
@joncmora, if you look at https://www.experts-exchange.com/questions/21165246/Impossible-A-speed-challenge.html then you'll see that I've actually organized a benchmark for my own stripchar function and for other alternative solutions. While my suggested solution might be slow, no one really managed to create a similar function that would be twice as fast in all situations. However, even if the memo is about 2500 lines long, these functions are just too fast to notice any real speed improvement. My method requires 9.425.846 processor ticks to handle 2500 strings. It's about twice as fast when it has to strip nothing or everything. On a 1 GHz machine you would have 1.000.000.000 processor ticks per second. Thus the speed is 1 millisecond for my method and about a half for the fastest one.
And since we would use:
Memo1.Lines.Text := StripChars(Memo1.Lines.Tex t, [#10, #13]);
to remove all linebreaks, we just have only one single string manipulation. (We're not stripping each memo line since that way we would never see the linebreaks!)
The problem with Madshi's KillChar solution which uses this function header is simple:
function KillChars( var str: string; killChrs: TSkipChars ): boolean;
The TMemo.Lines.Text property is just that! A property! Properties cannot be passed as var parameters to a function or procedure. If you would try to use:
KillChars(Memo1.Lines.Text , [#10, #13]);
then the compiler will not compile this line of code, since you cannot pass a property as var parameter. Thus you would have to assign the property to a variable, pass the variable to the function and then assign the value back to the property. This additional overhead will just slow things down again.
One other solution would be this:
var
I:Integer;
Value: string;
begin
Value := '';
for I := 0 to Pred(Memo1.Lines.Count) do Value := Value + Memo1.Lines[I];
Memo1.Lines.Text := Value;
end;
But this code would also be slower than just using one of the SkipChars versions in the other question.
And since we would use:
Memo1.Lines.Text := StripChars(Memo1.Lines.Tex
to remove all linebreaks, we just have only one single string manipulation. (We're not stripping each memo line since that way we would never see the linebreaks!)
The problem with Madshi's KillChar solution which uses this function header is simple:
function KillChars( var str: string; killChrs: TSkipChars ): boolean;
The TMemo.Lines.Text property is just that! A property! Properties cannot be passed as var parameters to a function or procedure. If you would try to use:
KillChars(Memo1.Lines.Text
then the compiler will not compile this line of code, since you cannot pass a property as var parameter. Thus you would have to assign the property to a variable, pass the variable to the function and then assign the value back to the property. This additional overhead will just slow things down again.
One other solution would be this:
var
I:Integer;
Value: string;
begin
Value := '';
for I := 0 to Pred(Memo1.Lines.Count) do Value := Value + Memo1.Lines[I];
Memo1.Lines.Text := Value;
end;
But this code would also be slower than just using one of the SkipChars versions in the other question.
I thought we're talking about a "fast" char stripper. So the source string will not be considered. If you're saying that the source will always be from <TString>.Text, then why not pass the <TString> instead. :-)
Tried that already? :-)