文字列 (String)

JavaScriptでは、文字列はUTF-16で表現される文字の集合として管理されます。

JavaScriptにはCやC++のような文字型という概念はなく、すべて文字列として扱われます。

文字列リテラル (string literal)

文字列は単一引用符 (') または二重引用符 (") で囲んで表現します。いずれの引用符を使用するかは任意であり、意味の違いはありません。

エスケープシーケンス (escape sequence)

エスケープシーケンスを利用することで、特殊な文字を文字列リテラルに記述できます。いずれもバックスラッシュ ( \ ) に続ける形式で記述します。

シーケンス 説明
\' シングルクォーテーション (単一引用符) [\u0027]
\" ダブルクォーテーション (二重引用符) [\u0022]
\\ バックスラッシュ [\u005C]
\xXX Latin-1文字 (2桁の16進数で指定)
\XXX Latin-1文字 (3桁の8進数で指定) (非推奨)
\uXXXX ※1 Unicode文字 (4桁の16進数で指定)

※1 この形式がサポートされるのはJavaScript 1.3以降です。また任意の文字の\uXXXXの形式は、エンコードすることで調べられます。

文字列リテラルとStringオブジェクトは異なる

文字列リテラルはStringオブジェクトではないため、

'a' instanceof String

はtrueとはなりません。 文字列リテラルは String オブジェクトとは別物 - MDN

評価方法 サンプルコード 評価結果
instanceof
'a' instanceof String
false
new String( 'a' ) instanceof String
true
typeof
typeof 'a'
"string"
typeof new String( 'a' )
"object"
※Firefox 6 + Firebug 1.8による結果

途中での改行

文字列リテラルの途中で改行してはなりません。

var str = 'ABC  // ここでエラー
DEF;

これは「終了していない文字列型の定数です。」「SyntaxError: unterminated string literal」のようなエラーとなります。

同じ表現をするならば、エスケープシーケンス\nを用いて

var str = 'ABC\nDEF;

とします。ちなみにPHPではリテラルの途中での改行が認められており、改行文字がそのまま文字列の一部となります。

文字列と数値の変換

文字列から数値への変換

  • 文字列 - 0
    var n = string - 0;
  • Number()関数 (Numberオブジェクトを関数として利用)
    var n = Number( string );
  • parseInt()関数 … 基数を指定して整数に変換
    ※基数を指定しないときは、8進数または10進数と解釈されます。いずれで解釈されるかは実装に依存するため、必ず基数を指定します。
    var n = parseInt( string, 10 );
  • parseFloat()関数 … 整数か浮動小数点数に変換
    var n = parseFloat( string );

これらの方法で結果がどのように異なるかを、下表にまとめます。試行したのは次の5種類の文字列です。

  1. 数字だけの文字列
  2. 小数点を含む文字列
  3. 「0x」に数字が続く文字列
  4. 数字以外の文字が後続する文字列
  5. 数字以外の文字が先行する文字列
変換方法 (1) '10' (2) '1.5' (3) '0x10' (4) '10a' (5) 'a10'
文字列 - 0 10 1.5 16 NaN NaN
Number()
parseInt() 1 10
parseFloat() 1.5 0

数値から文字列への変換

  • 数値 + 文字列
    var s = number + '';
  • String()関数
    new演算子を付けるとStringオブジェクトのコンストラクタとなるため、異なる結果となります。
    var s = String( number );
  • toString()メソッド … 基数を指定して文字列に変換
    var s = number.toString( 10 );
  • toFixed()メソッド … 小数点以下の桁数を指定して文字列に変換
    var s = number.toFixed( 3 );
  • toExponential()メソッド … 小数点以下の桁数を指定して、指数形式の文字列に変換
    var s = number.toExponential( 3 );
  • toPrecision()メソッド … 有効桁数を指定して文字列に変換
    var s = number.toPrecision( 3 );
( 10 ).toString( 2 );  // "1010"
( 10 ).toString( 10 ); // "10"
( 10 ).toString( 16 ); // "a"

toFixed()などのメソッドでは、数値を整形して出力できます。

( 10.25 ).toFixed( 0 ); // "10"
( 10.25 ).toFixed( 1 ); // "10.3" ※丸められている
( 10.25 ).toFixed( 2 ); // "10.25"
( 10.25 ).toFixed( 3 ); // "10.250"

( 10.25 ).toExponential( 0 ); // "1e+1"
( 10.25 ).toExponential( 1 ); // "1.0e+1"
( 10.25 ).toExponential( 2 ); // "1.03e+1"
( 10.25 ).toExponential( 3 ); // "1.025e+1"

( 10.25 ).toPrecision( 0 ); // RangeError: precision 0 out of range
( 10.25 ).toPrecision( 1 ); // "1e+1"
( 10.25 ).toPrecision( 2 ); // "10"
( 10.25 ).toPrecision( 3 ); // "10.3"
( 10.25 ).toPrecision( 4 ); // "10.25"

応用

Stringオブジェクト

プロパティ
プロパティ 内容
length 文字列内の文字数
プロパティ - String - JavaScript | MDN
メソッド
分類 メソッド 説明
出現位置 indexOf 文字列の出現位置を先頭から検索
lastIndexOf 文字列の出現位置を後方から検索
取得 charAt 指定された位置の文字を取得
slice 文字列の一部を取得
substr 文字列の一部を取得 [非推奨]
substring 文字列の一部を取得
正規表現 match 正規表現に一致する文字列を取得
search 正規表現に一致する文字列の出現位置を検索
replace 正規表現または文字列に一致する文字列を置換
連結 concat 文字列に1つ以上の値を連結
分割 split 区切り文字列か正規表現で区切り、文字列の配列に分割
大文字/小文字 toLowerCase すべての文字を小文字に変換した文字列を取得
toUpperCase すべての文字を大文字に変換した文字列を取得
比較 localeCompare ロケール特有の順序で文字列を比較
エンコーディング charCodeAt 指定された位置の文字のエンコーディングを取得
  toString 基本型の文字列値を取得
valueOf 基本型の文字列値を取得
メソッド - String.prototype - JavaScript | MDN メソッド - String - JavaScript | MDN

これ以外にも文字列をエスケープ (エンコード) する関数が、グローバル関数に定義されています。

Stringオブジェクトの生成

Stringオブジェクトは他のオブジェクトと同様に、コンストラクタにnew演算子を付けて生成します。

var s = new String( 'abc' );

new演算子の省略

new演算子を省略して、

var s = String( 'abc' );

としたり、または引用符で囲んで

var s = 'abc';

とすることでも文字列を生成できますが、これらはデータ型としての文字列であって、Stringオブジェクトではありません。このことはtypeof演算子で確認できます。

typeof new String( 'abc' ); // "object" が返される
typeof String( 'abc' );     // "string"

ただしJavaScriptの文字列はStringオブジェクトのメソッドなどへアクセスできるため、

'abc'.split( '' );

のように、文字列からメソッドを呼び出せます。

文字数の取得 (lengthプロパティ)

new String( 'abc' ).length;   // 3 が返される
new String( 'あいう' ).length; // 3

コードポイントが0x010000以降の文字はサロゲートペアにより4バイトで表現されるため、返される文字数が2倍になります。

new String( '🚀🚁🚂' ).length; // 6 が返される

文字列の取得

指定位置の文字の取得 (charAt)

Stringオブジェクトから、指定位置の文字を返します。

String.charAt( index )

取得位置をindexで指定します。

new String( 'abc' ).charAt( 1 );   // 'b' が返される
new String( 'あいう' ).charAt( 1 ); // 'い'

サロゲートペアで表現される文字は上位または下位のサロゲートが返されることになり、その結果REPLACEMENT CHARACTER「�」(U+FFFD) の文字か、そのコードとなります。

new String( '🚀🚁🚂' ).charAt( 1 ); // '�' または '\ude80' が返される
配列の要素としてアクセス

Stringオブジェクトに配列のようにアクセスすると、指定位置の文字を取得できる環境もあります。しかしこれは非標準な方法のため、使用を避けるべきです。

var str = new String( 'abc' );

var a = str[ 1 ];        // 'b' が返される
var b = str.charAt( 1 ); // 'b'
文字へのアクセス - String - JavaScript | MDN

指定範囲の文字列の取得

slice
String.slice( beginslice[, endSlice] );
String.prototype.slice - JavaScript | MDN

beginsliceの位置から、endSliceの直前までの文字列を取り出します。

  • 開始位置だけ指定すると、その位置から末尾までを取り出します。
  • 開始位置と終了位置が等しいと、空文字となります。
  • 開始位置が終了位置以上だと、空文字となります。
  • 負数を指定すると、文字列の最後からのオフセットと解釈されます。
substring
String.substring( indexA[, indexB] )
String.substring - JavaScript | MDN

indexAの位置から、indexBの直前までの文字列を取り出します。

  • 開始位置だけ指定すると、その位置から末尾までを取り出します。
  • 開始位置と終了位置が等しいと、空文字となります。
  • 開始位置が終了位置より大きいと、それらが交換されて解釈されます。
  • 負数を指定すると、0と解釈されます。
sliceとsubstringの比較
slice substring
var str = new String( '01234' );

str.slice(0,3);   // '012'
str.slice(1,3);   //  '12'
str.slice(3,1);   // ''
str.slice(3,-1);  //    '3'
str.slice(3,0);   // ''

str.slice(0);     // '01234'
str.slice(1);     //  '1234'
str.slice(3);     //    '34'
str.slice(1,1);   // ''

str.slice(-1);    //     '4'
str.slice(-3);    //   '234'
str.slice(-3,3);  //   '2'
str.slice(-3,-1); //   '23'
str.slice(-1,3);  //   ''

str.slice(10);    // ''
str.slice(0,10);  // '01234'
str.slice(10,1);  // ''
var str = new String( '01234' );

str.substring(0,3);   // '012'
str.substring(1,3);   //  '12'
str.substring(3,1);   //  '12'
str.substring(3,-1);  // '012'
str.substring(3,0);   // '012'

str.substring(0);     // '01234'
str.substring(1);     //  '1234'
str.substring(3);     //    '34'
str.substring(1,1);   // ''

str.substring(-1);    // '01234'
str.substring(-3);    // '01234'
str.substring(-3,3);  // '012'
str.substring(-3,-1); // ''
str.substring(-1,3);  // '012'

str.substring(10);   // ''
str.substring(0,10); // '01234'
str.substring(10,1); //  '1234'

正規表現による取得 (match)

Stringオブジェクトから正規表現で文字列を検索し、一致した文字列を格納した配列を返します。取得が不要で一致するか調べるだけならばsearch()を用います。

String.match( regexp )
String.match - JavaScript | MDN

一致した場合の戻り値はg属性の有無によって異なりますが、一致しなかった場合には、つねにnullが返されます。

引数に未定義値を与えると、空文字を要素に持つ配列が返されます。よって戻り値がnullではないことで、一致したとは判断できません。

var x;
'a'.match( x ); // [ "" ]
g属性を指定しなかった場合
var str = new String( 'X abc123 bef456' );
var obj = str.match( /([a-z]+)([0-9]+)/ );

// obj[0] ... "abc123"
// obj[1] ... "abc"
// obj[2] ... "123"
// obj.length ... 3

// obj.index  ... 2
// obj.input  ... "X abc123 bef456"
g属性を指定した場合

グローバルマッチ (g属性) では部分正規表現を取得できないため、その必要があるならばRegExp.exec()を使用します。

var str = new String( 'X abc123 bef456' );
var obj = str.match( /([a-z]+)([0-9]+)/g );

// obj[0] ... "abc123"
// obj[1] ... "def456"
// obj.length ... 2

このように、かっこによるグループ化が無視され、正規表現全体に一致した結果がそれぞれ配列に格納されて返されます。

文字列の出現位置の検索 (search)

Stringオブジェクトから正規表現に一致する文字列を検索し、その出現位置を返します。一致しない場合には-1が返されます。

String.search( regexp )
String.search - JavaScript | MDN

一致するか調べるだけならばRegExp.test()を、検索に正規表現が不要ならばString.indexOf()を用います。

このメソッドはグローバルマッチ (g属性) を無視し、つねに最初のマッチング位置を返します。

引数の正規表現オブジェクトに未定義値を与えると、0が返されます。よって戻り値が-1ではないことで、一致したとは判断できません。Gecko 特有の注記 - String.prototype.search() - JavaScript | MDN

var x;
'a'.search( x ); // 0

一方でRegExp.test()では、同様の状況では例外が発生します。

var x;
x.test( 'a' ); // TypeError: x is undefined

検索パターンのregexpを文字列リテラルで指定した場合には、暗黙的にRegExpオブジェクトに変換されます。

'ab 12'.search( /[0-9]/ ); // 3
'ab 12'.search( '[0-9]' ); // 3

ただしバックスラッシュを記述する場合には、文字列リテラルではそれがエスケープシーケンスに用いられることに注意が必要です。

'ab 12'.search( /\d/ );  // 3
'ab 12'.search( '\d' );  // -1
'ab 12'.search( '\\d' ); // 3

空文字の正規表現を指定すると、最初の文字に一致したと見なされ0が返されます。ただし正規表現リテラルを空文字とすると、例外となります。

'abc'.search( new RegExp( '' ) ); // 0
'abc'.search( '' ); // 0
'abc'.search( // ); // SyntaxError: expected expression, got end of script

indexOf

Stringオブジェクトから指定文字列に一致する文字列を検索し、その最初の出現位置を返します。一致しない場合には-1が返されます。

String.indexOf( searchValue [, fromIndex ] )
String.indexOf - JavaScript | MDN

fromIndexは、Stringオブジェクトにおける検索を開始する位置です。

'abcabc'.indexOf( 'c' );    // 2 … 一致する文字列が2つあるが、その最初が返される
'abcabc'.indexOf( 'c', 2 ); // 2 … インデックスは0ベースのため、インデックス2は最初の'c'
'abcabc'.indexOf( 'c', 3 ); // 5 … 末尾の文字に一致したときは、String.lnegth-1の値となる

'abcabc'.indexOf( 'd' );    // -1
'abcabc'.indexOf( 'bc' );   // 1

indexOf()では、大文字と小文字は区別されます。

'a'.indexOf( 'a' ); // 0
'A'.indexOf( 'a' ); // -1

これを区別せずに検索するにはtoUpperCase()またはtoLowerCase()で、文字列を大文字/小文字のいずれかに統一してから判定します。

'a'.toLowerCase().indexOf( 'a' ); // 0
'A'.toLowerCase().indexOf( 'a' ); // 0

類似のメソッドにlastIndexOf()がありますが、こちらは最後の出現位置を返します。

'abcabc'.indexOf( 'c' );     // 2
'abcabc'.lastIndexOf( 'c' ); // 5

文字列にサロゲートペアの文字が含まれていると、見かけ上の位置とは一致しなくなります。

'あabc'.indexOf( 'a' ); // 1
'😀abc'.indexOf( 'a' ); // 2

'あabc'.charAt( 1 ); // "a"
'😀abc'.charAt( 2 ); // "a" … 返された位置の文字を確認すると問題ない

文字列の置換 (replace)

Stringオブジェクトを、正規表現または文字列のパターンで指定文字列に置換し、置換した新しい文字列を返します。一致しない場合は、元の文字列がそのまま返されます。

String.replace( regexp, replacement )
String.replace - JavaScript | MDN

第1引数のregexpには、RegExpオブジェクトまたは文字列を指定します。文字列を指定した場合はそれをテキストのパターン文字列として扱い、RegExpオブジェクトに変換することありません。この点はString.match()やString.search()とは異なります。

'ab.'.replace( '.', 'A' ); // "abA"
'ab.'.replace( /./, 'A' ); // "Ab."

regexpを文字列で指定したときには、最初に一致した1つの文字列しか置換されません。よって複数の文字列を置換するには、正規表現でg属性を指定します。

var str = new String( 'a a a' );

str.replace( 'a', 'A' );  // "A a a"
str.replace( /a/, 'A' );  // "A a a"
str.replace( /a/g, 'A' ); // "A A A"

第2引数のreplacementには、置換テキストを表す文字列または関数を指定します。

replacementに文字列を指定する場合

ドル記号 ($) が特別な意味を持ちます。

文字 置換文字列
$n n番目に一致したかっこで囲まれた部分文字列 (nは1~99)
$& 一致した部分文字列
$` 一致した部分文字列の左側のテキスト
$' 一致した部分文字列の右側のテキスト
$$ 文字としてのドル記号 ($)
var str = new String( 'aabbcc' );

str.replace( /(b+)(c+)/, '$2$1' );  // "aaccbb"
str.replace( /b+(c+)/,   '-$&-' );  // "aa-bbcc-"

str.replace( /b/,        '-$`-' );  // "aa-aa-bcc"
str.replace( /b/,        '-$\'-' ); // "aa-bcc-bcc"

replacementに関数を指定する場合

第2引数に関数を指定した場合は、パターンに一致するごとにその関数が呼び出されます。そしてこの関数が返す文字列が、置換テキストとして使用されます。何も返さないと、文字列'undefined'に置換されます。

function replacer( str, p1, p2, offset, s )
{
    // 以下のように呼び出された場合、引数はそれぞれ次の値となります
    // str:"bbcc"
    // p1:"bb"
    // p2:"cc"
    // offset:2
    // s:"aabbccdd"
}

new String( 'aabbccdd' ).replace( /(b+)(c+)/, replacer );
一致時に呼び出される関数の引数
引数 説明
str パターンに一致した文字列
p1,p2, ... パターン内のかっこで囲まれた部分正規表現に一致した文字列
※グループ化のかっこの数だけ存在し、かっこがなければこの位置に次の引数であるoffsetが割り当てられる
new String( 'abc' ).replace( /b/,
    function( str, offset, s )
    {
        // str:"b"
        // offset:1
        // s:"abc"
    } );
offset 一致した位置を示す整数
s 検索対象のstring自身

部分正規表現に一致した文字列を渡す引数 (p1,p2,...) は、0個以上存在します。つまりこの関数は、可変長の引数をとることに注意が必要です。

var text = 'abcde';
var newText = text.replace( /[bd]/g,
    function( str )
    {
        return str.toUpperCase();
    } );

// newTextは'aBcDe'となる

この方法はHTMLのタグを含む文字列で、タグ以外の文字列だけを置換するように、複雑な処理をする場合に有用です。またHTMLのマークアップ記号のエスケープなどの用法もあります。

文字列の連結 (concat)

Stringオブジェクトに、1つ以上の値を連結します。

concat( string2, string3[, ..., stringN] )
String.concat - JavaScript | MDN

同様の処理は連結演算子 (+) で代用できるため、通常はそれを使用します。もし連結する値が配列に文字列として格納されているならば、それはArray.join()で連結できます。

ところでArray.concat()は配列の要素を結合し、より大きな配列とします。

文字列の分割 (split)

Stringオブジェクトを区切り文字separatorで部分文字列に区切り、文字列の配列を返します。

split( [separator][, limit] );
String.split - JavaScript | MDN

separatorを省略すると文字列は分割されず、要素を1つ持つ配列が返されます。第2引数のlimitは結果配列の要素数の上限であり、それを超える要素は結果から除外されます。たとえば、

new String( 'a-b-c-d' ).split( '-', 2)

とすると結果は[ "a", "b" ]と返され、そこには["c", "d"]はありません。なお区切り文字のseparatorには、文字列の他に正規表現も指定できます。

これメソッドとは逆に、配列を文字列に連結するのがjoin()です。

操作 結果
サンプルコード 説明 戻り値 説明
new String( '' )
  .split( '' )
空文字を空文字で分割
[]
空配列
new String( '' )
  .split( 'a' )
空文字を文字で分割
[ "" ]
空文字を要素に持つ配列
new String( 'abc' )
  .split()
文字列を分割しない
[ "abc" ]
元の文字列を要素に持つ配列
new String( 'abc' )
  .split( '' )
文字列を空文字で分割
[ "a","b","c" ]
文字の配列
new String( 'abc' )
  .split( 'b' )
文字列を文字で分割
[ "a","c" ]
文字の配列 (区切り文字は含まれない)
new String( 'abc' )
  .split( 'z' )
文字列をそこに含まれない文字で分割
[ "abc" ]
元の文字列を要素に持つ配列
new String( 'a1b2c' )
  .split( /[0-9]/ )
文字列を正規表現で分割
[ "a","b","c" ]
文字の配列

反復した文字列の作成

ECMAScript 2015 (6th Edition) 以降に対応した環境ならば、repeat()で処理できます。String.prototype.repeat() - JavaScript | MDN

専用の関数は用意されていないため、少し手間をかける必要があります。単純なのはループでくり返し文字列を連結する方法です。

var str = '';
for( var i = 0; i < 3; i++ ) str += 'ABC';

簡素に行いたいならば複数の要素を持つ配列を生成し、その要素を文字列で連結します。このとき配列の要素数は、くり返し数より1つ大きくする点に注意が必要です。

new Array( 4 ).join( 'ABC' ); // "ABCABCABC"
new Array( 0 ).join( 'A' ); // ''
new Array( 1 ).join( 'A' ); // ''
new Array( 2 ).join( 'A' ); // 'A'
new Array( 3 ).join( 'A' ); // 'AA'

この方法を、配列の生成と連結で分けて考えると、

var a = new Array( 4 );
// このときaは、[ undefined, undefined, undefined, undefined ]

a.join( 'ABC' );
// "ABCABCABC"が返される

となります。ちなみにPHPでは同様のことを、str_repeat()で行えます。

文字数の上限

文字列の最大数はブラウザなどによって異なります。Javascript string size limit: 256 MB for me - is it the same for all browsers? - Stack Overflow

ブラウザ 文字数の上限 バイト数での換算 'a'.repeat(n) での例外メッセージ (new Array(Math.pow(2,n)-m)).join('a') での例外メッセージ
Internet Explorer 11 2,147,483,646 (231-2) 4,294,967,292 (非対応) メモリが不足しています。
Firefox 59 268,435,455 (228-1) 536,870,910 RangeError: repeat count must be less than infinity and not overflow maximum string size InternalError: allocation size overflow
Chrome 65 1,073,741,799 (230-25) 2,147,483,598 RangeError: Invalid string length RangeError: Invalid string length

文字コードの変換

Unicode (UTF-32)

組み込みのメソッドで変換できます。

Unicode (UTF-16)

エンコード (charCodeAt)

指定位置の文字の、Unicodeコードポイント (UTF-16) を取得します。

var codepoint = string.charCodeAt( index )
charCodeAt - JavaScript | MDN

戻り値は0~65,535 (0xFFFF) の範囲で、このうち127まではASCII文字に対応しています。

new String( 'AB' ).charCodeAt( 0 ); // 65
new String( 'AB' ).charCodeAt( 1 ); // 66
new String( 'AB' ).charCodeAt( 2 ); // NaN

new String( 'あ' ).charCodeAt( 0 ); // 12354 (0x3042)

Unicodeの範囲は0~1,114,111 (0x10FFFF) のため、charCodeAt()ではサロゲートペアのコードを上位と下位に分けて取得します。

// '𠀋' 0x2000B [UTF-32]

'𠀋'.charCodeAt( 0 ); // 55360 (0xDB40)
'𠀋'.charCodeAt( 1 ); // 56331 (0xDC0B)

codePointAt()がサポートされるならば、このような場合にも処理を分けることはありません。

'𠀋'.codePointAt( 0 ); // 131083 (0x2000B)
String.prototype.codePointAt() - JavaScript | MDN
ASCIIコードとの変換

0~127まではASCII文字に対応するため、

'A'.charCodeAt( 0 ); // 65

のようにすれば、指定文字のASCIIコードを取得できます。なお同様のことをPHPでは、ord()で行えます。

デコード (fromCharCode)

Unicodeコードポイントに対応する文字を取得します。

String.fromCharCode( num1 [, ...[, numN ] ] )
String.fromCharCode() - JavaScript | MDN
String.fromCharCode( 65 );   // 'A'
String.fromCharCode( 0x41 ); // 'A'

String.fromCharCode( 12354 );  // 'あ'
String.fromCharCode( 0x3042 ); // 'あ'

文字列リテラルで記述するならば、'\u3042'String.fromCharCode( 0x3042 )と等しい文字を返します。複数の引数を渡した場合には、個々の引数が変換された結果が連結されて返されます。

String.fromCharCode( 65, 66 ); // 'AB'

fromCharCode()は16ビットの数値しか受け入れないため、サロゲートペアの文字は上位と下位に分けて指定します。

String.fromCharCode( 55360 );        // '��'
String.fromCharCode( 56331 );        // '��'
String.fromCharCode( 55360, 56331 ); // '𠀋'

String.fromCharCode( 55360 ) + String.fromCharCode( 56331 ); // '𠀋'

Unicode (UTF-8)

エンコード

UTF-8へエンコードできるメソッドは用意されていないため、まずcodePointAt()でUnicodeのコードポイントを取得します。そしてそれをUTF-8のエンコーディングに従い変換します。

function Encode( text )
{
    var codePoint = text.codePointAt( 0 ); // ここでは1文字目だけを対象とする
    if( 0xd800 <= codePoint && codePoint <= 0xdfff ) return null; // サロゲートペアは対象外

    var bytes = [];
    if( codePoint < 0x80 )
    {
        bytes = [ codePoint ];
    }
    else if( codePoint < 0x800 )
    {
        bytes = [
            ( codePoint & 0b0000011111000000 ) >> 6 | 0b11000000,
            ( codePoint & 0b0000000000111111 ) | 0b10000000 ];
    }
    else if( codePoint < 0x10000 )
    {
        bytes = [
            ( codePoint & 0b1111000000000000 ) >> 12 | 0b11100000,
            ( codePoint & 0b0000111111000000 ) >> 6 | 0b10000000,
            ( codePoint & 0b0000000000111111 ) | 0b10000000 ];
    }
    else if( codePoint < 0x110000 )
    {
        bytes = [
            ( codePoint & 0b000111000000000000000000 ) >> 18 | 0b11110000,
            ( codePoint & 0b000000111111000000000000 ) >> 12 | 0b10000000,
            ( codePoint & 0b000000000000111111000000 ) >> 6 | 0b10000000,
            ( codePoint & 0b000000000000000000111111 ) | 0b10000000 ];
    }

    var hex = [];
    for( var i = 0; i < bytes.length; i++ )
    {
        hex.push( bytes[ i ].toString( 16 ) );
    }
    return hex.join( '' ); // 引数textが'あ'ならば、'e38182'が返される
}

なおTextEncoderがサポートされる環境ならば、それでもエンコードできます。

var textEncoder = new TextEncoder();
var array = textEncoder.encode( 'あ' ); // "227,129,130" (0xE3,0x81,0x82)

デコード

エンコードの逆です。

function Decode( hexStr )
{
    var num = parseInt( hexStr, 16 );

    var codePoint;
    if( num <= 0xff )
    {
        codePoint = num;
    }
    else if( num <= 0xffff )
    {
        codePoint = num & 0b111111
            | ( ( num >> 2 ) & 0b11111000000 );
    }
    else if( num <= 0xffffff )
    {
        codePoint = num & 0b111111
            | ( ( num >> 2 ) & 0b0000111111000000 )
            | ( ( num >> 4 ) & 0b1111000000000000 );
    }
    else if( num <= 0xffffffff )
    {
        codePoint = num & 0b111111
            | ( ( num >> 2 ) & 0b000000000111111000000 )
            | ( ( num >> 4 ) & 0b000111111000000000000 )
            | ( ( num >> 6 ) & 0b111000000000000000000 );
    }

    return String.fromCodePoint( codePoint ); // 引数hexStrが'e38182'ならば、'あ'が返される
}

TextDecoderがサポートされる環境では、次のように記述できます。

var textDecoder = new TextDecoder( 'utf-8', { fatal: true } );
var buffer = new Uint8Array( [ 0xE3, 0x81, 0x82 ] );

try
{
    var text = textDecoder.decode( buffer ); // 'あ'
}
catch( e )
{
    if( e.name == 'TypeError' ) // 無効なコード
}

Shift_JIS

デコード

function Decode( text )
{
    var codes = [];
    var match;

    var pattern = /\\x([0-9a-f][0-9a-f])/gi;
    while( match = pattern.exec( text ) )
    {
        var code = parseInt( match[ 1 ], 16 );
        codes.push( code );
    }

    var binary = new Uint8Array( codes ).buffer;
    var blob = new Blob( [ binary ] );

    var reader = new FileReader();
    reader.onloadend = function()
    {
        console.log( reader.result );
    }
    reader.readAsText( blob, 'shift_jis' );
}

TextDecoderならば、これは次のように記述できます。

var textDecoder = new TextDecoder( 'shift-jis' );
var buffer = new Uint8Array( [ 0x82, 0xA0 ] );

var text = textDecoder.decode( buffer ); // 'あ'

URLエンコード/デコード

文字列をURLエンコード/デコードする関数が、グローバル関数として定義されています。

  • URLエンコード (パーセントエンコード)
    • encodeURI()
    • encodeURIComponent()
  • URLデコード (パーセントデコード)
    • decodeURI()
    • decodeURIComponent()

URLに使用できない文字をエスケープする処理はURL encodingと呼ばれることが多いですが、JavaScriptの関数名としてはencodeURIです。

encodeURI( str )
encodeURI() - JavaScript | MDN
encodeURIComponent( str )
encodeURIComponent() - JavaScript | MDN

このencodeURI()とencodeURIComponent()では、エンコードの対象とする文字が異なります。この違いはdecodeURI()でも同様です。

2つの関数の相違
文字種 encodeURI
decodeURI
encodeURIComponent
decodeURIComponent
英数字 変換されない
予約文字1 (- _ . ! ~ * ' ( ))
予約文字2 (; , / ? : @ & = + $) 変換されない 変換される
番号記号 (#)
上記以外 変換される
encodeURI(';');          // ';'
encodeURIComponent(';'); // '%3B'

encodeURI('あ');          // '%E3%81%82'
encodeURIComponent('あ'); // '%E3%81%82'

ところで、ここで「あ」をエンコードした結果である「%E3%81%82」は、UTF-8での文字コードです。

PHPでのURLエンコード

base64

エンコード

var encodedData = window.btoa( stringToEncode )
WindowBase64.btoa() - Web APIs | MDN
window.btoa('A'); // "QQ=="

この関数はバイナリデータを対象としており、Unicode文字列を直接扱えません。window.btoa('あ')のようにUnicode文字を与えると、例外が発生します。The "Unicode Problem" - Base64 encoding and decoding - Web APIs | MDN

よって、先にUTF-8をエンコード/デコードする方法で処理した上で渡します。

window.btoa( unescape( encodeURIComponent( 'あ' ) ) ); // "44GC"

デコード

var decodedData = window.atob( encodedData )
WindowBase64.atob() - Web APIs | MDN
window.atob( 'QQ==' ); // "A"

デコードもエンコード同様に、Unicode文字列を直接扱えません。

window.atob( '44GC' ); // "あ"

よって次のようにします。

decodeURIComponent( escape( window.atob( '44GC' ) ) ); // "あ"

PHPでのbase64エンコード

類似文字

Unicodeがサポートされることで、類似の文字が多数存在します。これらは一見するだけでは区別できないため、注意が必要です。

さまざまな空白文字

  1. \u0009 (\t) … CHARACTER TABULATION
  2. \u000A (\n) … LINE FEED (LF)
  3. \u000B (\v) … LINE TABULATION
  4. \u000C (\f) … FORM FEED (FF)
  5. \u000D (\r) … CARRIAGE RETURN (CR)
  6. \u0020 … SPACE
  7. \u00A0 … NO-BREAK SPACE (NBSP)
  8. \u1680 … OGHAM SPACE MARK
  9. \u180E … MONGOLIAN VOWEL SEPARATOR
  10. \u2000 … EN QUAD
  11. \u2001 … EM QUAD
  12. \u2002 … EN SPACE
  13. \u2003 … EM SPACE
  14. \u2004 … THREE-PER-EM SPACE
  15. \u2005 … FOUR-PER-EM SPACE
  16. \u2006 … SIX-PER-EM SPACE
  17. \u2007 … FIGURE SPACE
  18. \u2008 … PUNCTUATION SPACE
  19. \u2009 … THIN SPACE
  20. \u200A … HAIR SPACE
  21. \u200B … ZERO WIDTH SPACE
  22. \u2028 … LINE SEPARATOR
  23. \u2029 … PARAGRAPH SEPARATOR
  24. \u202F … NARROW NO-BREAK SPACE
  25. \u205F … MEDIUM MATHEMATICAL SPACE
  26. \u3000 … IDEOGRAPHIC SPACE (全角スペース)
  27. \uFEFF … ZERO WIDTH NO-BREAK SPACE
Unicode spaces

これらは異なる文字のため、比較演算では偽と見なされます。

' ' == ' ';    // true
' ' == '\x20'; // true
' ' == '\xA0'; // false

'\x20' == '\xA0';   // false
'\x20' == '\u0020'; // true

このことは次のような場合に問題となります。

alert( a ); // "1 2"
alert( b ); // "1 2"

// これらの2つの変数は外見上は同じく表示されますが、比較すると一致しません。
alert( a == b ); // false

// このような場合にはエスケープすると違いを確認できます。
alert( escape( a ) ); // "1%202"
alert( escape( b ) ); // "1%A02"

この空白文字の問題は、正規表現/\s/を用いることで対応できます。

JavaScriptのドキュメントから検索