四章第一回 文字列の操作
このページの最終更新日:
第四章のトピックは文字列です。第二章・第三章はDOMの話題でしたが、少し離れることにしましょう。
今回基礎的な文字列の操作について解説します。より詳しい説明が十一章第五回にもあります。
文字列の長さ
ある文字列があるとき、その「長さ」を知りたいことがあると思います。長さとは、つまり文字数のことです。そういう方法はちゃんとあります。
var str = "abcde";
console.log(str.length);
このサンプルでは、変数strに"abcde"
を代入し、そのlengthというプロパティを見ています。「5」が表示されます。試しにこのstrの内容を変えてみると、表示されるlengthプロパティの中身もそれにあわせて変わるはずです。
つまり、文字列の長さを知るには、その文字列のlengthプロパティを使えばいいのです。今回の場合何の役に立つのかあまり分からないかもしれませんが、文字列処理の基本であり、たとえばinput要素に入力された文字列を処理するときなどに、役立つことがあります。ちなみに、これは数値です。
ちなみに、ここですごい人なら違和感を覚えるかもしれません。ここで、変数strに代入されている"abcde"は、プリミティブ値(一章第二回)です。プリミティブ値なら、オブジェクトではないのだから、プロパティを持たないはずです。
それにもかかわらずプリミティブ値のプロパティを参照できているのにはまた特殊な事情があります。なかなか難しいので、今は解説しません(九章第七回に説明があります)。不思議に思った人も、今はそういうものだと思っておきましょう。この先も文字列がプロパティ(あるいはメソッド)を持っているような解説が続きますが、まあそういうものなので気にしないようにしましょう。
少し細かい話をすると、JavaScriptにおける文字列はUTF-16で符号化されています。つまり、文字列は16bit符号なし整数(UTF-16コードユニット)の列です。(ただし、JavaScriptではUTF-16としては妥当でないコードユニット列を文字列として扱うこともできます。)
UTF-16では、コードポイントがU+FFFF以下の文字はコードユニット1つで表されますが、それ以上のコードポイントを持つ文字はコードユニット2つ(サロゲートペア)で表されます。
文字列のlengthプロパティはコードユニットの数を数えます。つまり、サロゲートペアで表される文字は2文字として数えられるということです。これは意図していない動作となる恐れがあるので注意が必要です。
console.log("𠮷野家".length); // 4
この例では、文字列は3文字であるにも関わらず、"𠮷野家"
のlengthプロパティは4です。これは、「𠮷」の文字がサロゲートペアで表される文字だからです。
また、以降の説明で「何文字目」とか「文字列の位置」といった説明が出てくることがありますが、それは文字列をコードユニット列としてみたときの何番目かということであり、サロゲートペアまで考慮した文字列位置とは異なります。
これらの問題に対処するにはES2015の新しい機能を使う必要があります。それは後々紹介することにします(第十六章第十八回)。
文字列の検索
ここでいう検索とは、文字列に、ある文字列が含まれているかどうかを調べるということです。次のサンプルを見てみましょう。
var str = "abcde";
var idx = str.indexOf("b");
console.log(idx);
変数idxに、str.indexOf("b")
の戻り値を代入して、それを表示しています。1と表示されます。
このindexOfは、その文字列から、引数で指定した文字列を検索して、その位置を返します。
今回の場合、strは"abcde"
で、そのうち引数の"b"
は1文字目(JavaScriptでは、最初の文字が0文字目だから、aが0文字目、bが1文字目となります)なので、1が返されたというわけです。
では、もし検索した文字列が含まれていなかったらどうなのでしょう。
var str = "abcde";
var idx = str.indexOf("f");
console.log(idx);
この場合、strから"f"
を検索していますが、fは含まれていません。そういう場合は-1が返されます。この性質から、ある文字列にある文字列が含まれているかどうかを調べるためにindexOfが利用されることも多くあります。
また、1文字ではない文字列も検索できます。
var str = "abcde";
var idx = str.indexOf("bcd");
console.log(idx);
この場合、strに"bcd"
という文字列は含まれているので、その開始位置(先頭の文字の位置)である1が返されます。
文字列から抜き出す
文字列のうち指定した一部分だけを抜き出した文字列を取得することは、文字列に対する基本的な操作のひとつです。この方法はいくつかあります。
slice・substring
まず、sliceを使う方法を紹介します。
var str = "abcde";
console.log( str.slice(1,4) );
"bcd"
が表示されます。1〜3文字目だけが抜き出されました。
ここで、この2つの引数は、抜き出しの開始位置と終了位置を表しています。開始位置は1、つまり1文字目だからb
です。終了位置は4文字目だから、e
の位置になります。
となるとbからeまでが抜き出されるように思えますが、実は終了位置の1つ手前まで抜き出されるようになっています。したがって、bからdまでが実際に抜き出されて、"bcd"
になりました。終了位置を指定する場合、その位置の文字自体は含まれないということは往々にしてありますから、終了位置が出てきたらそれが何を意味しているのか注意しましょう。
ちなみに、抜き出すといっても、strから"bcd"
が取り除かれるというわけではありません。sliceを使ったあともstrの中身は変わらず"abcde"
です。ここでの抜き出しとはあくまで「指定した範囲の文字列を得る」ということです。
次に紹介するのはsubstringです。これの使い方はsliceと同様で、開始位置と終了位置を指定します。
var str = "abcde";
console.log( str.substring(1,4) ); // "bcd"
結果は同じです。
では、これらの違いはどこにあるかというと、引数に負の数を指定したときに違いがでてきます。「-1文字目」とか「-2文字目」というような位置はありえませんので、負の数を指定された場合は特別な位置指定となります。負の数の扱い方がsliceとsubstringの違いです。
まず、substringは簡単で、負の数が渡されたとき、それは0ということにします。つまり、
str.substring(-2,2)
str.substring(0,-1)
str.substring(-100,3)
などは、
aaa.substring(0,2)
aaa.substring(0,0)
aaa.substring(0,3)
と同義になります。
それに対し、sliceの場合は、負の数が渡されたとき、文字数を後ろから数えます。例えば、
var aaa = "abcde";
console.log( aaa.slice(-3,5) );
の場合、後ろから3番目の文字が開始位置で、終了位置は5文字目です。
ただし、注意点があります。前から普通に数える場合最初の"a"は0番目であるのに対して、後ろから数える場合、最初の"e"は0番目ではなく1番目になるということです。理由は、-0というのは0と同じなので-0で"e"の位置を表すことはできないからですね。
つまり、-3で指定した開始位置は"c"の位置になるということです。従って、"cde"
が返ります。
第2引数に負の数を指定した場合も同様に後ろから数えます。
sliceのほうがsubstringより機能が豊富であることから、sliceのほうがよく使われる傾向にあるようです。
ちなみに、sliceやsubstringの2つめの引数は省略できます。省略した場合、終了位置は文字列の一番最後になり、文字列の一番最後まで抜き出されます。これもよく使われます。
charAt
1文字だけ抜き出す場合、もっと簡単なものがあります。それはcharAtです。
var str = "abcde";
console.log( str.charAt(1) );
引数が1つありますね。簡単に、その引数で指定された位置の文字を返します。今回の場合、引数は1だから、1文字目、つまり"b"
が返ります。
なお、文字と述べましたが、これは1文字だけからなる文字列のことです。
メソッドの利用例
それでは、これらを利用して、実際にどんなことができるか考えてみましょう。
0詰め
0詰めとは、例えば、数字を5桁に統一したいとき、4桁以下の数字は"00025"・"01234"
のように先頭に0をつけて5文字にするというものです。
その性質上、もともとは数値でも、0詰めされてできたものは文字列になりますね。
よく行われるのは次の方法です。
var num = 1234; //0詰めする数字
var result = ("00000" + num).slice(-5); //できた文字列
console.log(result);
変数resultに、0詰めされた文字列が入ります。3行目で今回解説したsliceが使われていますね。
最初に"00000"
の後にnumをくっつけています。今回の場合、"000001234"
となります。
その後、その文字列からsliceで抜き出しています。開始位置は-5で、終了位置は省略されているから、最後まで抜き出されます。開始位置が-5ということは、後ろから5文字めということです。そこから最後まで抜き出すから、結果として後ろから5文字を抜き出していることになります。
今回の場合、000001234
の部分が抜き出されます。見事、0詰めされました。
0を5個つける理由は、0を5個つけておけば必ず5文字以上になるからです。
最後にsliceで5文字抜き出すから、5文字以上ないと困るわけですね。
以上が文字列の基礎の基礎です。次回は正規表現を用いた少し発展的な文字列操作を解説します。
エスケープシーケンス
最後に、少しメソッドから離れてエスケープシーケンスについて解説します。これは、文字列リテラルとして文字列を記述する際にソースコード中に書きにくい特殊な文字を表すための方法です。たとえば、次のように改行を含む文字列を表したいとします。
foo
bar
これは「foo(改行)bar」ということです。実は、文字列リテラル中に改行を含めることはできません。次のように書くのは文法エラーです。
var a = "foo
bar";
そこで、改行を表すための特別な記法が必要になります。それは、\n
です。つまり、前述の文字列を表すには次のように文字列リテラルを記述すればよいです。
var a = "foo\nbar";
この文字列をconsole.logなどで表示してみましょう。\n
が改行になっていることがわかるはずです。なお、ここで改行と呼んでいるのはLF (U+000A) です。
このように、\
を使って特殊な文字を表す方法をエスケープシーケンスと読んでいます。
主なエスケープシーケンスを次にまとめます。
エスケープシーケンス | 対応する文字 |
---|---|
\r | CR (U+000D) |
\n | LF (U+000A) |
\t | タブ (U+0009) |
\\ | バックスラッシュ \ (U+005C) |
\' | シングルクォート ' (U+0027) |
\" | ダブルクォート " (U+0022) |
後ろの2つは、\'
で'
を表し、\"
で"
を表せるということです。これは別にエスケープシーケンスを使う必要がないじゃないかと思うかもしれませんが、文字列リテラル中でクォーテーションマークを使いたい場合に必要になることがあります。例えば、次の文字列を表現したい場合を考えます。
He said "foo."
文字列中に"
が含まれています。これは、次のように文字列リテラルで記述することができます。
"He said \"foo.\""
ここでは、文字列中の"
を\"
としてエスケープシーケンスを利用することで、文字列リテラル自体の""
と区別しています。これは、エスケープシーケンスを使わずに"He said "foo.""
とすることはできません。なぜなら、文字列リテラルが"He said "
までで終了していると解釈されてしまうからです。
ただし、この場合は文字列リテラルを囲う文字を'
に変えて、次のようにしても解決できます。こちらのほうが見やすいため好むという人も多いようです。
'He said "foo."'
下から3つ目の\\
も同様に、文字列リテラル中では\
という文字はエスケープシーケンス用の特別な文字として扱われるため、\
という文字自体を表現するために存在します。
コードポイントによる指定
もうひとつの形のエスケープシーケンスとして、コードポイントを指定する方法があります。全ての文字にはコードポイントと呼ばれる番号が定まっており、その番号で文字を指定する方法です。(ただし、JavaScriptが扱うのはUnicodeなので、Unicodeに存在しない文字は表せません。)
コードポイントによる指定は、次のように\u
に続けて16進数4桁でコードポイントを指定します。
"foo\u28ffbar"
コードポイントU+28FFに対応する文字は⣿なので、この文字列リテラルは"foo⣿bar"
と書くのと同じ意味になります。世の中には変な文字もたくさんありますから、ソースコード中に直接書くと見難くなる場合などはこの方法で書くとよいでしょう。
上でも説明したようにJavaScriptの文字列はUTF-16コードユニット列なので、サロゲートペアで表される文字はサロゲートペアのコードユニットを記述する必要があります。
"\ud842\udfb7" === "𠮷"
ただし、ES2015ではサロゲートペアではなくコードポイントを直接記述できる次のような記法が用意されています。
"\u{20bb7}" === "𠮷"