3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Ruby】文字列とシンボルの違いについて

Last updated at Posted at 2020-01-07

はじめに

今回扱う対象は以下となります。

  • 文字列(リテラル)
  • シンボル(リテラル)

文字列から数値へ変換

最初の文字列が整数に変換できない場合は0を返します。

"100".to_i     #=> 100

"1.5".to_f     #=> 1.5

"7/2".to_r     #=> (7/2) Rationalオブジェクトに変換

"1+2i".to_c    #=> (1+2i) Complexオブジェクトに変換

"123ab45".to_i #=> 123 aの前までが変換対象

"123ab45".to_f #=> 123.0 aの前までが変換対象

"2.3".to_i     #=> 2 ピリオドの前までが変換対象

"3.5.6".to_f   #=> 3.5 2番目のピリオドの前までが変換対象

"a123".to_i    #=> 0

バックスラッシュ記法

指定する内容   生成される文字
\x xの文字そのもの
\n 改行
\r キャリッジリターン
\f 改ページ
\a ベル
\e エスケープ
\s 空白
\b バックスペース
\t タブ
\v 垂直タブ
\nnn 8進数表記 (nは0〜7)
\xnn 16進数表記 (nは0〜9、a〜f)
\cx または \C-x コントロール文字 (xはASCII文字)
\M-x メタx
\M-\C-x メタコントロールx
\unnnn Unicode文字 (nは0〜9、a〜f、A〜F)
\u{nnnn} Unicode文字列 (nは0〜9、a〜f、A〜F) nnnnは16進数で1〜6桁まで指定可能。スペースかタブ区切りで複数のUnicode文字を指定可能

ちなみに、表中に書かれている「キャリッジリターン」とは、文字コード体系において、カーソルを文頭へ戻すことを表す制御文字です。(引用元:Weblio)

8進数表記と16進数表記

# "@"の文字コードは8進数で100、16進数で40
p "\100" #=> @ 8進数表記
p "\x40" #=> @ 16進数表記

出力メソッドの種類と違い

メソッド 改行 出力内容の構築メソッド バックスラッシュ記法
p 引数ごとに改行 inspectメソッド そのまま出力
print 改行しない to_sメソッド 適用した結果を出力
puts 引数ごとに改行 to_sメソッド 適用した結果を出力

pのバックスラッシュ記法

p "\x61" #=> "a"
p "\x0a" #=> "\n"

バックスラッシュ記法をシングルクォートで囲んだ場合は、シングルクォートとバックスラッシュのエスケープのみ適用され、それ以外はそのまま出力されます。

ヒアドキュメント

改行コードを含む文字列を指定するためにヒアドキュメントを利用します。
ヒアドキュメントは、「<<」に続けて文字列の終端を示す任意の識別子を指定します。

ヒアドキュメントには、終端を示す識別子の前にはスペースなどの文字を記述してはいけないというルールがあります。なので、階層が深い場合でも、終端は行頭に指定します。

以下はヒアドキュメントの例です。
終端を示す識別子にSQLを使用しており、2行目と3行目が文字列になります。

query = <<SQL
  select *
     from animal_table;
SQL

query #=> " select *\n    from animal_table;\n"

階層が深い場合のヒアドキュメントでは、開始の識別子の頭にハイフンをつけます。

def foo
  <<-RESULT
    Ba
    nana
  RESULT
end

p foo #=> "    Ba\n    nana\n"

ヒアドキュメントにダブルクォートで囲んだ時と同じような式展開やバックスラッシュ記法の適用を無効とする場合は、識別子をシングルクォートで囲みます。終端の識別子は囲まないよう注意しましょう。

a = 1
s = <<'SAMPLE'
  #{a}
SAMPLE

p s #=> "  \#{a}\n"

識別子をダブルクォートで囲む事により、式展開が有効であることを明示的に示すことができます。

a = 1
s = <<"SAMPLE"
  #{a}
SAMPLE

p s #=> "  1\n"

パーセント記法

パーセント記法を用いる事で文字列を囲む記号をプログラマが指定できます。
文字列の中でダブルクォートを使う場合などに、エスケープしなくてもよいので便利(らしい)です。
文字列を囲む記号には、数値やアルファベット以外の文字が使用可能です。

# パーセント記法の例
a = %*cat* # 囲む記号に「*」を使用
p a        #=> "cat"

# エスケープなしでダブルクォートを文字列中に使用
a = %*"cat"*
p a        #=> "\"cat\""

# パーセント記号に[]なども使える
a = %[cat]
p a        #=> "cat"

文字列のシングルクォートの時と同じにするには「%q」を使います。
また、ダブルクォートの際は「%Q」を使います。

a = 1
%q!#{a + 2}! #=> "\#{a + 2}"
%Q?#{a + 2}? #=> "3"

以下パーセント記法を表にまとめました。

書式   生成される値
% ダブルクォート文字列
%Q ダブルクォート文字列 (%のみと同等)
%q シングルクォート文字列
%s シンボル
%W 要素がダブルクォート文字列となる配列、要素の区切りは空白文字
%w 要素がシングルクォート文字列となる配列、要素の区切りは空白文字
%x コマンド出力
%r 正規表現

異なる文字コード間での文字列操作

Ruby2.0以降デフォルトのスクリプトエンコーディングはUTF-8となり、明示的に文字コードを指定しない限りUTF-8となります。

x = "モンキー"
x.encoding           #=> #<Encoding:UTF-8>
y = x.encode("SJIS") # "モンキー"をWindows-31Jに変換
y.encoding           #=> #<Encoding:Windows-31J>
x + y                #=> Encoding::CompatibilityError

sprintfによるフォーマット指定

桁数を揃える場合にsprintfという組み込み関数を使います。
sprintfは、第一引数にフォーマット、第二引数以降にフォーマットしたい値を指定します。

# 進数の指定
sprintf("result: %#b", 16)        #=> "result: 0b10000"
sprintf("result: %#o", 16)        #=> "result: 020"
sprintf("result: %#x", 16)        #=> "result: 0x10"
sprintf("result: %#X", 16)        #=> "result: 0X10"

# 桁数の指定
sprintf("result: %02d", 1)        #=> "result: 01"
sprintf("result: %03d", 1)        #=> "result: 001"
sprintf("result: %05.2f", 1.1111) #=> "result: 01.11"

ここで重要なのはsprintf関数はStringクラスの%演算と同じ結果が得られるということです。

"result: %02d" % 1        #=> "result: 01"
"result: %03d" % 1        #=> "result: 001"
"result: %05.2f" % 1.1111 #=> "result: 01.11"

シンボル

# シンボルの記述例
foo1 = :"foo1"        #=> :foo1
foo2 = :"#{foo1}foo2" #=> :foo1foo2
foo3 = :'foo3'        #=> :foo3
foo4 = :foo4          #=> :foo4

パーセント記法を使ってシンボル生成することもできます。
この場合、「%s」とすることで実現できます。(以下例)

%s?foo1? #=> :foo1
%s{foo2} #=> :foo2

生成された値はSymbolクラスのインスタンスとなります。
文字列からシンボルを生成するには「to_sym」を使います。

v1 = "foo1"    #=> "foo1"
v2 = v1.to_sym #=> :foo1
v3 = v2.to_s   #=> "foo1" シンボルから文字列に変換

文字列とシンボルの違い

作成したシンボルは、文字の並びが同じであれば、同一のオブジェクトを参照しますが、文字列リテラルは文字の並びが同一でも、指定するごとに新たなStringオブジェクトが生成されます。
これを確かめるにはオブジェクトID(object_idメソッド)を使います。

文字列リテラルでは、毎回新しくオブジェクトを生成するのでオブジェクトIDが変わります。
一方、シンボルは同じオブジェクトを参照するのでオブジェクトIDは変化しません。
また、nilやtrueも、一つのオブジェクトを参照するので、シンボルと同様にオブジェクトIDは変化しません。

p "foo1".object_id #=> 70311790346380
p "foo1".object_id #=> 70311790401140

p :foo1.object_id  #=> 1537628
p :foo1.object_id  #=> 1537628
p :foo2.object_id  #=> 1537948

Ruby 2.3以降では、「#frozen_string_literal:true」というマジックコメントを記述した場合、文字列リテラルで生成される文字列は値が変更できないようにfreezeされ、同じ内容の文字列リテラルは同一の文字列オブジェクトを返すようになります。

「equal?」メソッドは2つのオブジェクトが同一かどうか(等しいかどうかではない)を論理値で返し、サブクラスではオーバーライドしない慣習となっている(そうです)。
等価演算子「==」は、2つのオブジェクトが等しいかどうかを判定します。
これは多くのクラスでオーバーライドされています。

「==」と同様の動作をするメソッドに「eql?」メソッドがあります。
「eql?」メソッドは値の比較だけでなく、型の比較も行うため、値が等しくても型が違えばfalseを返します。
つまり、「eql?」メソッドは「==」よりも厳密に値の比較を行うと考えても良いでしょう。

# オブジェクトの同値性・同一性判定
"foo1" == "foo1"     #=> true
"foo1".equal? "foo1" #=> false 互いにobject_idが異なるため

:foo1 == :foo1       #=> true
:foo1.equal? :foo1   #=> true

# eql?メソッド
"foo1".eql? "foo1"  #=> true
1.0 == 1            #=> true
(1.0).eql? 1        #=> false
(1.0).eql? 1.0      #=> true

破壊的メソッド

破壊的メソッド「!」は、自分自身の内容を変更するだけでなく、他の変数の参照先にも影響を与えるので気を付けなければなりません。

以下の例に出てくる「chop」メソッドは、最後の文字を取り除いた文字列を返すメソッドです。

# 破壊メソッドの例
a = "foo1"
b = a                # aとbは同じ文字列"foo"を参照
p a.chop             # "foo" aの参照先は変更されない
p b                  #=> "foo1"
p a.chop!            #=> "foo" aの参照先が変更される("foo"に上書きされる)
p b                  #=> "foo"

破壊的メソッドに「!」を付けるのは、同じメソッド名で破壊的でないメソッドもある場合に限った習慣(らしい)です。
「concat」メソッドのように破壊的であっても「!」が付いていないメソッドも存在します。

まとめ

変数に文字列を代入していくと、たとえ同じ値だとしてもそれぞれ異なるオブジェクトを示しています。
一方シンボルでは、変数に代入しても同じオブジェクトを示しています。

x = "foo"
y = "foo"
z = "foo"
# x, y, zはそれぞれ別のオブジェクト
p x.object_id #=> 70311790464220
p y.object_id #=> 70311790482240
p z.object_id #=> 70311790500280

x = :foo 
y = :foo 
z = :foo 
# x, y, zは同一のオブジェクト
p x.object_id #=> 1538908
p y.object_id #=> 1538908
p z.object_id #=> 1538908
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?