String construction and replacement

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

String construction and replacement

Sebastian Nozzi-2
Hello List,

I've been struggling a bit with some basics about Strings for which I
couldn't find an answer (yet).

1) How to construct a String from Characters?

For example, I want to construct a String from the Chatacter literals:

$H $I
Character cr
$T $H $E $R $E

2) How to replace a sequence of Characters in a String for others?

For exaple, I want to replace every 'HI' (in this case only one) for
'HELLO' in the String above (not necesarily destructively, getting a
new String is also ok). Is there a quick way to do that?

Thanks in advance!

Sebastian
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Claus Kick
Sebastian Nozzi wrote:

> Hello List,
>
> I've been struggling a bit with some basics about Strings for which I
> couldn't find an answer (yet).
>
> 1) How to construct a String from Characters?
>
> For example, I want to construct a String from the Chatacter literals:
>
> $H $I
> Character cr
> $T $H $E $R $E

Dirty solution:

|col str |
col := OrderedCollection new.
col add:$H
;add:  $I;
add: (Character cr);
add:$T;
add: $H;
add: $E;
add: $R;
add: $E.
str := String new.
col do: [:element | str := str, element asString].
^str.

This probably creates a bunch of unnecessary string objects, but I can
never remember the best implementation.


> 2) How to replace a sequence of Characters in a String for others?
>
> For exaple, I want to replace every 'HI' (in this case only one) for
> 'HELLO' in the String above (not necesarily destructively, getting a
> new String is also ok). Is there a quick way to do that?

How about this?

| str3  str2 str index |

str := 'HI THERE'.
str2 := 'HI'.

index := str findString: 'HI'.
"str := str replaceFrom: index to: str2 size  with: 'HELLO' startingAt:1."

str3 := 'HELLO', (str copyFrom: (index + str2 size) to: (str size)).
^str3





_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Michael van der Gulik-2
In reply to this post by Sebastian Nozzi-2


On Mon, Feb 2, 2009 at 8:46 AM, Sebastian Nozzi <[hidden email]> wrote:
Hello List,

I've been struggling a bit with some basics about Strings for which I
couldn't find an answer (yet).

1) How to construct a String from Characters?

For example, I want to construct a String from the Chatacter literals:

$H $I
Character cr
$T $H $E $R $E

If you're dealing with less than four characters, you could do:

a := String with: $H with: $I with: Character cr with: $T.
 
Otherwise, I'm not aware of any trivial way to do this.

The first issue is that you need to have all those characters available as a collection somehow (without using a String). My best effort, without adding convenience methods to the String class, is:

a := #( $H $I $- $T $H $E $R $E ). "Makes an array of literals - I never liked this syntax though"

You have to manually put the "Character cr" in there; I'm not aware of any way to declare it as a literal like that:

a at: 3 put: Character cr.

And then you can do Smalltalk magic with it:

b := a inject: '' into: [ :each :sum | each, sum asString]. "Makes 8 copies of a String; inefficient"

If there were more than just a handful of characters (e.g. writing to a file), then you'd want to use streams instead:

c := WriteStream on: (String new: a size).  " It's important to make a good estimate of size here "
c nextPutAll: a.
b := c contents.

c is a stream with a String as a target, so "nextPutAll:" will accept any collection of characters and "nextPut:" will accept any individual character.



2) How to replace a sequence of Characters in a String for others?

For exaple, I want to replace every 'HI' (in this case only one) for
'HELLO' in the String above (not necesarily destructively, getting a
new String is also ok). Is there a quick way to do that?;;


This is something that I don't know off the top of my head, so I'm going to give you a small insight as to how I work this out.

First, I bring up the browser and intuitively go to the String class. I have a quick look at the method categories, and see "converting" and "copying" which might be useful. I notice "copyReplaceTokens:with:" which invokes "copyReplaceAll:with:asTokens".

I highlight the invocation of "copyReplaceAll:with:asTokens" and press alt-m to see which classes implement this. But then a little lightbulb goes off in my head; perhaps "copyReplaceAll:with:" exists. It does - in class SequencableCollection.

I also discover "replaceAll:with" on a hunch as well, but it doesn't work and doesn't give any errors. A bug maybe?

So you can do this:

d := b copyReplaceAll: 'HI' with: 'HELLO'.

Now, I know that there are also more advanced things you can do, such as replacing using regular expressions, but I think that is in a downloadable package somewhere. You'll need to Google for it.

Gulik.

--
http://people.squeakfoundation.org/person/mikevdg
http://gulik.pbwiki.com/

_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

David Mitchell-10
In reply to this post by Sebastian Nozzi-2
Great question.

To deal with the input characters, it is useful to have them in an
array. A traditional Smalltalk array with characters would look like
this #($a $b $c) but it isn't obvious what to do with the carriage
return. For this, the Squeak brace array is handy and also works in
Pharo.

characters := {$H.$I.Character cr. $T.$H.$E.$R.$E}.

It isn't terribly portable to other Smalltalks, but it sure is easy to
type. Now we've got an array of characters, how to create the new
String? Another Squeak-ism is the class method #streamContents:. It
takes a one argument block. The argument is a writeable stream that
will return its contents at the end. I have to admit I was thrown by
it the first time I saw it.

string := String streamContents: [:writeStream | characters do: [:c |
writeStream nextPut: c]].
Like the brace array, it isn't very portable to other Smalltalks, but
it is pretty handy.

That leaves us with replacing. I opened the method finder and typed
replace into the search box.

That gives us the following complete solution:

| characters string |
characters := {$H.$I.Character cr. $T.$H.$E.$R.$E}.
string := String streamContents: [:writeStream | characters do: [:c |
writeStream nextPut: c]].
string copyReplaceAll: 'HI' with: 'HELLO'

I also posted this to my blog (with a picture of the Method Finder)

http://www.withaguide.com/2009/02/characters-strings-and-things.html

On Sun, Feb 1, 2009 at 1:46 PM, Sebastian Nozzi <[hidden email]> wrote:

>
> Hello List,
>
> I've been struggling a bit with some basics about Strings for which I
> couldn't find an answer (yet).
>
> 1) How to construct a String from Characters?
>
> For example, I want to construct a String from the Chatacter literals:
>
> $H $I
> Character cr
> $T $H $E $R $E
>
> 2) How to replace a sequence of Characters in a String for others?
>
> For exaple, I want to replace every 'HI' (in this case only one) for
> 'HELLO' in the String above (not necesarily destructively, getting a
> new String is also ok). Is there a quick way to do that?
>
> Thanks in advance!
>
> Sebastian
> _______________________________________________
> Beginners mailing list
> [hidden email]
> http://lists.squeakfoundation.org/mailman/listinfo/beginners
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Randal L. Schwartz
>>>>> "David" == David Mitchell <[hidden email]> writes:

David> | characters string |
David> characters := {$H.$I.Character cr. $T.$H.$E.$R.$E}.
David> string := String streamContents: [:writeStream | characters do: [:c |
David> writeStream nextPut: c]].

chars := {$H.$I.Character cr. $T.$H.$E.$R.$E}.
string := chars as: String.

Far simpler.  Look at #as:... it's a pretty good "force this into that"
call.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<[hidden email]> <URL:http://www.stonehenge.com/merlyn/>
Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.
See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Sebastian Nozzi-2
To all whom I wrote that I found it strange that there was no class
method to construct a String from an array... I was wrong!

I debugged Randal's suggestion of using "as: String" and the result
that "newFrom: aCollection" is called. So, the solution could also be:

string := String newFrom: {$H.$I.Character cr. $T.$H.$E.$R.$E}.

Of course, to make it completely portable you could also use two ways
that have been suggested: add every character into an
OrderedCollection and use that, or use a standard array-literal and
manually #put: the carriage-return afterwards.

Thank you again for your answers,

Sebastian


> 2009/2/1 Randal L. Schwartz <[hidden email]>:
>>>>>>> "David" == David Mitchell <[hidden email]> writes:
>>
>> David> | characters string |
>> David> characters := {$H.$I.Character cr. $T.$H.$E.$R.$E}.
>> David> string := String streamContents: [:writeStream | characters do: [:c |
>> David> writeStream nextPut: c]].
>>
>> chars := {$H.$I.Character cr. $T.$H.$E.$R.$E}.
>> string := chars as: String.
>>
>> Far simpler.  Look at #as:... it's a pretty good "force this into that"
>> call.
>>
>> --
>> Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
>> <[hidden email]> <URL:http://www.stonehenge.com/merlyn/>
>> Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.
>> See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Ken G. Brown
In reply to this post by Sebastian Nozzi-2
Is there something wrong with just doing:

string := 'Hello
There'.

?

Ken G. Brown


>Date: Mon, 2 Feb 2009 11:57:51 +0100
>From: Sebastian Nozzi <[hidden email]>
>Subject: Re: [Newbies] String construction and replacement
>To: [hidden email]
>Message-ID:
> <[hidden email]>
>Content-Type: text/plain; charset=UTF-8
>
>To all whom I wrote that I found it strange that there was no class
>method to construct a String from an array... I was wrong!
>
>I debugged Randal's suggestion of using "as: String" and the result
>that "newFrom: aCollection" is called. So, the solution could also be:
>
>string := String newFrom: {$H.$I.Character cr. $T.$H.$E.$R.$E}.
>
>Of course, to make it completely portable you could also use two ways
>that have been suggested: add every character into an
>OrderedCollection and use that, or use a standard array-literal and
>manually #put: the carriage-return afterwards.
>
>Thank you again for your answers,
>
>Sebastian
>
>
>> 2009/2/1 Randal L. Schwartz <[hidden email]>:
>>>>>>>> "David" == David Mitchell <[hidden email]> writes:
>>>
>>> David> | characters string |
>>> David> characters := {$H.$I.Character cr. $T.$H.$E.$R.$E}.
>>> David> string := String streamContents: [:writeStream | characters do: [:c |
>>> David> writeStream nextPut: c]].
>>>
>>> chars := {$H.$I.Character cr. $T.$H.$E.$R.$E}.
>>> string := chars as: String.
>>>
>>> Far simpler.  Look at #as:... it's a pretty good "force this into that"
>>> call.
>>>
>>> --
>>> Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
>>> <[hidden email]> <URL:http://www.stonehenge.com/merlyn/>
>>> Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.
> >> See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Sebastian Nozzi-2
In reply to this post by Sebastian Nozzi-2
No, actually no.

But the background to my question (which I didn't mention since it
doesn't belong to this list) is that I was dealing with a Seaside
issue in which one string's newlines came back with CR+LF (carriage
return and line feed) and under other conditions it came back as
having LF+LF.

I wanted to construct a String for these exact sequences and do the
appropriate replacement (that is replace all LF+LF ocurrences for
CR+LF).


I am very glad that in Smalltalk it's possible to write a
string-literal like you did (with a newline in-between).

2009/2/2 Ken G. Brown <[hidden email]>:
> Is there something wrong with just doing:
>
> string := 'Hello
> There'.
>
> ?
>
> Ken G. Brown
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Matthias Berth-2
For your particular need (normalizing line breaks), use:

  aString withSqueakLineEndings

That gives you a defined line end convention, suitable for use with
other methods like linesDo:.

HTH

Matthias

On Mon, Feb 2, 2009 at 4:09 PM, Sebastian Nozzi <[hidden email]> wrote:

> No, actually no.
>
> But the background to my question (which I didn't mention since it
> doesn't belong to this list) is that I was dealing with a Seaside
> issue in which one string's newlines came back with CR+LF (carriage
> return and line feed) and under other conditions it came back as
> having LF+LF.
>
> I wanted to construct a String for these exact sequences and do the
> appropriate replacement (that is replace all LF+LF ocurrences for
> CR+LF).
>
>
> I am very glad that in Smalltalk it's possible to write a
> string-literal like you did (with a newline in-between).
>
> 2009/2/2 Ken G. Brown <[hidden email]>:
>> Is there something wrong with just doing:
>>
>> string := 'Hello
>> There'.
>>
>> ?
>>
>> Ken G. Brown
> _______________________________________________
> Beginners mailing list
> [hidden email]
> http://lists.squeakfoundation.org/mailman/listinfo/beginners
>
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Claus Kick
In reply to this post by Claus Kick
Claus Kick wrote:

>> 2) How to replace a sequence of Characters in a String for others?
>>
>> For exaple, I want to replace every 'HI' (in this case only one) for
>> 'HELLO' in the String above (not necesarily destructively, getting a
>> new String is also ok). Is there a quick way to do that?
>
>
> How about this?
>
> | str3  str2 str index |
>
> str := 'HI THERE'.
> str2 := 'HI'.
>
> index := str findString: 'HI'.
> "str := str replaceFrom: index to: str2 size  with: 'HELLO' startingAt:1."
>
> str3 := 'HELLO', (str copyFrom: (index + str2 size) to: (str size)).
> ^str3

Woops, thats hardly generic ...

str := 'HI THERE BLA HI HERE'.
answer := (str (asArrayOfSubstringsSeparatedBy: (Character space)))
asOrderedCollection.

pattern := 'HI'.
replacement := 'HELLO'.
resultString := ''.
i := 1.
answer do:[:element | (element = pattern ) ifTrue:[i = 1
ifTrue:[resultString := resultString, replacement . i := i + i]
ifFalse:[resultString := resultString, ' ', replacement ]]
ifFalse:[resultString := resultString, ' ',  element]].
^resultString.


_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Randal L. Schwartz
>>>>> "Claus" == Claus Kick <[hidden email]> writes:

Claus> pattern := 'HI'.
Claus> replacement := 'HELLO'.
Claus> resultString := ''.
Claus> i := 1.
Claus> answer do:[:element | (element = pattern ) ifTrue:[i = 1 ifTrue:[resultString
Claus> := resultString, replacement . i := i + i] ifFalse:[resultString :=
Claus> resultString, ' ', replacement ]] ifFalse:[resultString := resultString, ' ',
Claus> element]].
Claus> ^resultString.

That sort of "build a string by repeated concatenation" scares me.  Might
be ok for very short things, but for longer things, learn to use streams.

For example, look at the implementation of String>>expandMacrosWithArguments:
to see how to use a ReadStream on the source and WriteStream to hold the
destination.

Also, if the logic gets to be nested blocks, I try to refactor that
rather quickly.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<[hidden email]> <URL:http://www.stonehenge.com/merlyn/>
Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.
See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Claus Kick
Randal L. Schwartz wrote:

>>>>>>"Claus" == Claus Kick <[hidden email]> writes:
>
>
> Claus> pattern := 'HI'.
> Claus> replacement := 'HELLO'.
> Claus> resultString := ''.
> Claus> i := 1.
> Claus> answer do:[:element | (element = pattern ) ifTrue:[i = 1 ifTrue:[resultString
> Claus> := resultString, replacement . i := i + i] ifFalse:[resultString :=
> Claus> resultString, ' ', replacement ]] ifFalse:[resultString := resultString, ' ',
> Claus> element]].
> Claus> ^resultString.
>
> That sort of "build a string by repeated concatenation" scares me.  Might
> be ok for very short things, but for longer things, learn to use streams.

Honestly, I never think of Streams in this context. One day, I might
actually remember to use them.

> For example, look at the implementation of String>>expandMacrosWithArguments:
> to see how to use a ReadStream on the source and WriteStream to hold the
> destination.

Ah, ok, I will really note this down this time.

> Also, if the logic gets to be nested blocks, I try to refactor that
> rather quickly.

How would you refactor something like that?
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: String construction and replacement

Randal L. Schwartz
>>>>> "Claus" == Claus Kick <[hidden email]> writes:

Claus> How would you refactor something like that?

If you can, get a copy of Kent Beck's "Smalltalk Best Practice Patterns".

But the key thing to keep in mind is that anything that isn't just a simple
message send (unary, binary or keyword) has some subsequence of one or more
message sends that can be pulled out and named separately with an
intention-revealing selector, providing both code reuse, and clarity.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<[hidden email]> <URL:http://www.stonehenge.com/merlyn/>
Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.
See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners