文字列
Stringとそのスーパークラス、サブクラスを紹介。
配列と同じ項目もあり。
ただしモノによっては文字列での使用が禁止されているメソッドもある。
s := 'Hello'.
s1 := s addAll: ' World' >> 'This message is not appropriate for this object'
※Smalltalkの文字列では、破壊的に修正するような
コレクション共通メソッドは禁止されているようだ。
文字列の特徴:
- リテラルで定義するとimmutableな状態になり、エラーになる処理がある。(インスペクタの画面ラベルが違うよね)
'Hello World' at: 1 put: $F >> NoModificationError
(WriteStream on: '') nextPut: $a >> NoModificationError
beMutableとするとこれらの処理ができるようになる。
- マルチバイト文字を含む文字列はTwoByteStringになる。
'Hello Worldあ' class
このクラスになると、シングルバイトの文字を含む場合も全て2バイト計算になる。
また、3バイト以上の文字は存在しない。
'×' class
例えば乗算記号はビッグエンディアン側の数値(218)に割り当てられている。
'Hello'
まずはここから。
String new: 10 withAll: $a
単一文字から指定した数の文字列を作れるが、文字列を増幅して文字列を作る方法は基本的には用意されていない。
String with: $a
または
$a asSymbol asString
シンボルがStringのサブクラスなので、asSymbolまですれば事足りることが多い。
'Hello' first
'Hello' last
コレクション共通。
'Hello' at: 3
コレクション共通。
'Hello' first: 3
コレクション共通。
'Hello' last: 3
コレクション共通。
'Hello' allButFirst: 2
コレクション共通。値段で'\ 500'みたいな文字列を数値に変換するときとかに使えるのかな?思いつくのはそれくらい。
'Hello' allButLast: 1
コレクション共通。ファイルを読み込んで改行文字が最後に必ず入る場合とかに使えるかも。
'Hello' size
コレクション共通。シングルバイト(ByteStringクラス)ならこの一本槍でよい。
マルチバイト(TwoByteStringクラス)の場合もバイト数ではなく文字数が返る。
'こんにちは。' sizeInBytes
マルチバイト文字を含む文字列は基本的には文字数の2倍が返ってくる。
foo := 'AAAAAA'.
foo sizeInBytes = foo size
マルチバイト文字を含んでいれば数が異なるのでfalseになる。
asIntegerが256以上になる文字を含んでいる場合、自動的にTwoByteStringクラスのインスタンスになる。
'AAAAAA' isKindOf: TwoByteString
としてもよいかも。
ただし、これらのチェックには注意点あり。
1. 'AAA' asTwoByteString なんてやると、シングルバイト文字だけで構成されていてもTwoByteStringになる。
2. 元々TwoByteStringだった文字列のマルチバイト部分をシングルバイトに置き換えても、属するクラスはTwoByteStringのまま。
3.±×÷なんかは全角文字だけどシングルバイトである。
万全を期すなら (String with: foo) sizeInBytes = (String with: foo) size のよに文字列を作り直すのが良いのではないか。
'Hello', ' ', 'World'
コレクション共通。なのでその他のコレクション型クラスでもガンガン使うべし。
'Small Talk' asLowercase.
'Big Talk' asUppercase.
文字(Character)自体にasLowercase、asUppercaseが定義されていて、これを1文字ずつ繰り返している。
'Hello' printString
SQLをDBに渡すときは、文字列が同じくシングルクォーテーションくくりなのでこの仕様も使いどころがある。
だが、単に^selfして欲しいときがあるのもまた事実。
'Hello' reverse
コレクション共通。
'Hello' copyFrom: 2 to: 4
コレクション共通。でも文字列で使うことが一番多いのではと思う。
'Hello' copyUpTo: $o
コレクション共通。長い文字列になるともう使えないだろうなぁ。
'Hello' beginsWith: 'Hel'
コレクション共通。といっても('Hello' first: 3) = 'Hel'とかやれば代用できるので登場する機会は少なそうだ。
'Hello World' includes: $o
コレクション共通。trueかfalseかだけ返す。位置まで知りたい場合はindexOf:を使う。
'Hello World。' contains: [:i | i isLetter and: [i isAlphabetic not]]
コレクション共通。条件をブロックで渡すところがincludes:と異なる。
ちなみにisLetterは半角アルファベットか全角文字に対しtrueとなるので、このブロックは全角文字が含まれていればtrue。
'Hello World' indexOf: $o
コレクション共通。ブロックで渡せるfindFirstの方がが汎用性が高い。
'Hello World' lastIndexOf: $o
コレクション共通。ブロックで渡せるfindLastの方が汎用性が高い。
'Hello World' findFirst: [:i | i = $o]
コレクション共通。文字単位で探したい場合。
'Hello World' findLast: [:i | i = $o]
コレクション共通。findFirstと対を成すメソッド。
'Hello World。' detect: [:i | i isLetter and: [i isAlphabetic not]]
コレクション共通。先頭からチェックしていくメソッドしか用意されていない。
また、見つからないとエラーになる。
'Hello World。' detect: [:i | i isLetter and: [i isAlphabetic not]] ifNone: [nil]
コレクション共通。先頭からチェックしていくメソッドしか用意されていない。
見つからない場合、ifNoneブロックの評価結果が返る。
あるかないかだけ判断したいならconteins:を使ったほうが便利。
'Hello World' findString: 'Wo' startingAt: 1
発展形でfindString:startingAt:ifAbsent:、findString:startingAt:ignoreCase:useWildcards:といった
タイプもある。
'H*o W##ld' match: 'Helllllo World'
発展形としてmatch:ignoreCase:もある。*は0文字以上にマッチ、#は1文字にマッチする。
| str |
str := 'Morning,Afternoon/Evening;Night'.
str matchesPattern: '*,*/*;*' ignoreCase: false do: [:st :ed | Transcript cr; show: (str copyFrom: st to: ed)]
こんなことをしたい日がいつか来るのか疑問だが、何か面白いことができた感じがする。
'Hello World' rangeOfPattern: 'H*W' startingAt: 1 ignoreCase: false
'*'で0文字以上にマッチ、'#'で1文字にマッチする。Rangeオブジェクトで返ってくる。
最短マッチしか行われない。
'Good morning' copyReplaceAll: 'morning' with: 'afternoon'
コレクション共通。開始終了位置を指定できるcopyReplaceFrom:to:with:というメソッドもある。
'Hello\World' withCRs
'\'そのものを残したい場合もあるかもしれないが、残念ながらエスケープする方法はない。
'Hello<n>World' expandMacros
という方法もある。expandMacrosの使用方法は本家のCincomのチュートリアルが詳しい。
'77.7s' asNumber
数字さえ入っていれば適当に数値系のクラスに変換してくれる。.があればFloatに、.があってsで終われば
FixedPointになる。数字がない場合は0が返る。
'C:\temp\foo.st' asFilename
STファイルのフルパスがコピペできるならファイルインも簡単。
'C:\xxx\yyy\zzz\bar.st' asFilename fileIn
' H e l l o W o r l d ' trimBlanks
できれば指定した文字を取り除けるメソッドが欲しかった。
((String new: 10 withAll: $0), 'Hello') last: 10
このテのことがしたい場合、文字数よりバイト数指定したい場合のほうが多いのではと思うが、
残念ながらマルチバイトが混じりつつ指定したバイト数に詰めるのは結構難しい。
とりあえずシングルバイトだけの場合に有効な方法を紹介。
('Hello', (String new: 10 withAll: $0)) first: 10
左詰めを逆方向にしただけ。
((('-----Hello - World-----' copyReplaceAll: ' ' with: 0 asCharacter asSymbol) copyReplaceAll: '-' with: ' ')
trimBlanks copyReplaceAll: ' ' with: '-') copyReplaceAll: 0 asCharacter asSymbol with: ' ' >> 'Hello - World'
(1)半角スペースをNULL文字へ退避させ、(2)ハイフンを半角スペースに置き換え、(3)その半角スペースを取り除く。
(4)残った半角スペースをハイフンに戻し、(5)NULL文字を半角スペースに戻せばよい。
でもターゲットの文字列を変数に入れて、
foo copyFrom: (foo findFirst: [:i| i ~= $-]) to: (foo findLast: [:i | i ~= $-])
としたほうが楽。
((((('-----Hello - World-----', 0 asCharacter asSymbol) copyReplaceAll: ' ' with: 0 asCharacter asSymbol) copyReplaceAll: '-' with: ' ')
trimBlanks copyReplaceAll: ' ' with: '-') copyReplaceAll: 0 asCharacter asSymbol with: ' ') allButLast: 1 >> 'Hello - World----'
両端の特定の文字を取り除く方法の最初にNULL文字付加する処理、最後に取り除く処理を追加。
でもターゲットの文字列を変数に入れて、
foo copyFrom: (foo findFirst: [:i| i ~= $-]) to: foo size
としたほうが楽。
(((((0 asCharacter asSymbol, '-----Hello - World-----') copyReplaceAll: ' ' with: 0 asCharacter asSymbol) copyReplaceAll: '-' with: ' ')
trimBlanks copyReplaceAll: ' ' with: '-') copyReplaceAll: 0 asCharacter asSymbol with: ' ') allButFirst: 1 >> '-----Hello - World'
左側バージョンと基本的に同じ。NULL文字付加する場所が最初か最後かの違い。
でもターゲットの文字列を変数に入れて、
foo copyFrom: 1 to: (foo findLast: [:i| i ~= $-])
としたほうが楽。
'Morning,Afternoon,Evening' tokensBasedOn: $,
コレクション共通。でも文字列で使うことが多いと思う。
'Morning,Afternoon/Evening;Night' runsFailing: [:i | #($, $/ $;) includes: i]
コレクション共通。マッチする文字が続く場合、まとめて一区切りとする。tokensBasedOn:だけで十分な気もする。
'Morning,Afternoon/Evening;Night' runsFailing: [:i | #($, $/ $;) includes: i] do: [:i | Transcript cr; show: i]
コレクション共通。マッチする文字が続く場合、まとめて一区切りとする。tokensBasedOn:とdo:で十分な気もする。
'123,456,789.321' runsSatisfying: [:i | i isDigit]
コレクション共通。runsFailing:の逆条件バージョン。マッチする文字が続く場合、まとめて一区切りとする。
'123,456,789.321' runsSatisfying: [:i | i isDigit] do: [:i | Transcript cr; show: i]
コレクション共通。runsFailing:do:の逆条件バージョン。マッチする文字が続く場合、まとめて一区切りとする。
'getFieldKnownNotToBeAName:' piecesCutWhere: [:i :j | i isLowercase and: [j isUppercase]]
コレクション共通。これはキャメルケースを単語ごとにカットした場合。
tokensBasedOnやrunsXxxing:は区切り文字が残らないが、これは残る。また、1文字の条件でも区切ることができる。
/**/型のコメントをより分けることも可能。
('DECLARE
dt DATE; /*変数定義*/
BEGIN
/*今日の日付をdtに格納*/
SELECT SYSDATE
INTO dt
FROM DUAL;
/*標準出力させる*/
/*DBMS_OUTPUT.PUT_LINE(dt);/*''YYYY-MM-DD''*/
DBMS_OUTPUT.PUT_LINE(TO_CHAR(dt, ''YYYY/MM/DD''));
EXCEPTION
WHEN OTHERS THEN
/*例外処理*/
DBMS_OUTPUT.PUT_LINE(SQLERRM)
END;
/'
piecesCutWhere: [:i :j | ((i = $/) & (j = $*)) | ((i = $*) & (j = $/))])
groupedBy: [:i | i first = $*]
s := 'Hello' chopTo: 4
何故か両端から切り出す。何に使うんだろう?
s := 'Hello World' contractTo: 2
さっぱり使いどころがわからない。。。
s := 'Hello World' dropFinalVowels
さっぱり使いどころがわからない。。。
s := 'Hello World' dropVowels: 5
後ろから母音を除いていくが、母音の数が引数より少ない場合、今度は何でもいいから
前から文字を削り始める。
さっぱり使いどころがわからない。
最終更新:2012年07月01日 00:34