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
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).
- 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.
- 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
Claus Kick wrote:
- 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.
"Claus" == Claus Kick claus_kick@web.de 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 wrote:
"Claus" == Claus Kick claus_kick@web.de 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?
"Claus" == Claus Kick claus_kick@web.de 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.
On Mon, Feb 2, 2009 at 8:46 AM, Sebastian Nozzi sebnozzi@googlemail.comwrote:
Hello List,
I've been struggling a bit with some basics about Strings for which I couldn't find an answer (yet).
- 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.
- 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.
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 sebnozzi@googlemail.com wrote:
Hello List,
I've been struggling a bit with some basics about Strings for which I couldn't find an answer (yet).
- 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
- 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 Beginners@lists.squeakfoundation.org http://lists.squeakfoundation.org/mailman/listinfo/beginners
"David" == David Mitchell david.mitchell@gmail.com 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.
beginners@lists.squeakfoundation.org