String字元串 字元串由String類提供,除了直接使用單雙引號或其它字面量創建字元串,也可以使用String.new()方法來創建。 Ruby中的 字元串是可變對象 。 字元串的連接 直接連接即可: 單雙引號 這和Perl一樣,和Shell也類似。單引號是強引用,雙引號是弱引用。 雙引號的一個 ...
String字元串
字元串由String類提供,除了直接使用單雙引號或其它字面量創建字元串,也可以使用String.new()方法來創建。
a = "hello"
b = String.new("world")
Ruby中的字元串是可變對象。
字元串的連接
直接連接即可:
>> "a""b"
=> "ab"
>> "a" "b"
=> "ab"
>> "a" "b"
=> "ab"
單雙引號
這和Perl一樣,和Shell也類似。單引號是強引用,雙引號是弱引用。
雙引號的一個特性是支持表達式、變數內插,使用#符號即可。在Ruby中,#
首碼可以看作是一種對某對象的引用、調用之義。例如:
- 內插全局變數
#$var
- 內插實例變數
#@var
- 內插類變數
#@@var
- 但是對於普通的不帶首碼符號的局部變數或表達式,為了避免歧義,通常在
#
後加上{}
。例如#{name}
、#{3+4}
、#{func("hello")}
所以,在雙引號中如果#
字元後面的是$ @ @@ {
,需要對#
轉義,其它時候,不需要對它轉義。
格式化字元串內插
Ruby顯然也是支持printf、sprintf的,但是Ruby除了表達式或變數內插,還支持格式化字元串內插。
sprintf "pi is about %.4f",Math::PI
=> "pi is about 3.1416"
"pi is about %.4f" % Math::PI # 單個格式化字元串
=> "pi is about 3.1416"
"%s: %f" % ["pi", Math::PI] # 多個格式化字元串
=> "pi: 3.141593"
"xiaomage = %{age}" % {:age => "23"}
=> "xiaomage = 23"
正如上面的示例,需要進行格式化的字元使用%
標識,並使用%
連接字元串和待替換的值。如果要內插多個字元串,則值部分使用中括弧包圍(即放進數組)或放進hash。
%q和%Q和%
這和Perl里的q() qq()
是一樣的,也是分別充當單引號、雙引號的角色。%q()
被解析成單引號,單個%
或%Q
被解析成雙引號。
%q %Q
後面的()
是引號的起始、終止定界符,定界符可以替換成其他成對或相同的符號。例如,下麵是等價的:
# 以下等價,內部的單引號不需要反斜線轉義
%q(hello'world)
%q[hello'world]
%q{hello'world}
%q!hello'world!
%q#hello'world#
# 以下等價
%Q(hello'world)
%Q[hello'world]
%{hello'world} # 單個%是一樣的
%!hello'world!
%#hello'world#
如果使用的是成對的定界符,那麼在定界符內只要合理的配對,就可以包含定界符字面符號。例如:
%Q(hello(hello world)world)
%<<book>Ruby</book>>
%((1+(2*3)) = #{(1+(2*3))})
%(A mismatched paren \( must be escaped) # 不配對的括弧需要轉義
關於字元串的可變性
對於Ruby來說,字元串是可變的。所以,無法使用單個對象來引用內容相同的兩個字元串,如果能引用的話,其中一個修改了就表示另一個字元串也會修改,但這已經表示同一個對象了。
所以,只要Ruby遇到一個字元串,都會新創建一個字元串對象。這意味著,如果在一個迴圈中使用了字元串常量,那麼這個常量字元串對象也會在每次迴圈過程中新創建,而這是可以避免的消耗性能的一種方式。
>> 10.times {puts "test".object_id}
12046480
12046340
12046280
12046220
12046160
12046080
12046000
12045880
12045780
12045680
單字元
使用一個問號作為下一個字元的首碼,這個字元將稱為字元字面量。例如:
?A # 代表一個大寫字母A
?? # 代表一個問號
?" # 代表一個雙引號
在Ruby1.8及之前的版本,這是一個單字元序列,會轉換成ASCII碼存放,在Ruby 1.9之後,單字元等價於只有一個字元的字元串。
>> ?A == 'A'
=> true
擴展字元串:+ * <<
想要連接兩個字元串,直接不使用任何連接符或使用"+"就可以。但註意,它們不會自動將其它類型轉換成字元串類型,需要手動調用to_s方法來轉換。
>> "A""B"
=> "AB"
>> "A"+"B"
=> "AB"
>> "A"2 # SyntaxError:
>> "A"+2 # TypeError
>> "A"+2.to_s
=> "A2"
可使用"<<"將多個字元串追加到某個字元串的尾部,它同樣不會自動轉換成字元串。這時候字元串就像是一個字元數組一樣,但需要知道,Ruby字元串不是字元數組,只是實現了一些好用的操作字元串的方法:
>> "abc" << "hello" <<"world"
=> "abchelloworld"
<<
可以直接追加整數,整數被當作ASCII或其它編碼字元進行轉換,使得追加到字元串里的是字元。
>> "xyz" << 65
=> "xyzA"
只是需要註意的是,使用+
或直接相連擴展字元串的方式時會自動創建一個新的字元串對象,原始字元串不變,也就是說在得到擴展的結果前拷貝了一些數據進行創建新字元串對象。而使用<<
的方式,因為修改的是字元數組,所以是原地修改的。
>> a="xyz"
=> "xyz"
>> a + "XYZ"
=> "xyzXYZ"
>> a # a沒有變
=> "xyz"
>> a << "XYZ"
=> "xyzXYZ"
>> a # a已經變了
=> "xyzXYZ"
*
號重覆字元串N次。於是,可以簡單地寫出等長的萬惡分割線。例如:
>> "ab" * 3
=> "ababab"
>> '-' * 40
=> "----------------------------------------"
字元串的索引屬性
字元串可變、可索引子串、設置子串、插入子串、刪除子串等等。
字元串[]()搜索和賦值
通過[]
可以對字元串進行搜索和賦值,賦值時是原處修改字元串的。索引方式有多種,且支持負數索引號。
# 1.根據索引,搜索或賦值單元素
str[index] → new_str or nil
str[index] = new_str
# 2.根據索引和給定長度,搜索或賦值0或多個元素
str[start, length] → new_str or nil
str[start, length] = new_str
# 3.根據索引範圍,搜索或賦值0或多個元素
str[range] → new_str or nil
str[range] = aString
# 4.根據正則模式(斜線包圍正則表達式),搜索或賦值匹配到的元素
str[regexp] → new_str or nil
str[regexp] = new_str
# 5.根據正則模式(包含分組匹配),返回給定分組內容
# capture可以是分組名,也可以是分組索引號(即反向引用)
# 分組索引號為0表示regexp匹配的所有內容
# 如果是賦值操作,則替換給定分組的內容
str[regexp, capture] → new_str or nil
str[regexp, integer] = new_str
str[regexp, name] = new_str
# 6.根據給定字元串精確搜索或賦值
str[match_str] → new_str or nil
str[other_str] = new_str
可以說,Ruby對字元串的索引操作支持的是相當的豐富、完善。下麵是一些例子:
a = "hello there"
a[1] #=> "e"
a[2, 3] #=> "llo"
a[2..3] #=> "ll"
a[-3, 2] #=> "er"
a[7..-2] #=> "her"
a[-4..-2] #=> "her"
a[-2..-4] #=> ""
a[11, 0] #=> ""
a[11] #=> nil
a[12, 0] #=> nil
a[12..-1] #=> nil
a[/[aeiou](.)\1/] #=> "ell"
a[/[aeiou](.)\1/, 0] #=> "ell" 等價於上面方式
a[/[aeiou](.)\1/, 1] #=> "l" 第一個分組內容
a[/[aeiou](.)\1/, 2] #=> nil 第二個分組
a[/(?<vowel>[aeiou])(?<non_vowel>[^aeiou])/, "non_vowel"] #=> "l"
a[/(?<vowel>[aeiou])(?<non_vowel>[^aeiou])/, "vowel"] #=> "e"
a["lo"] #=> "lo"
a["bye"] #=> nil
s = "hello"
while(s["l"]) # 將所有的l替換成L
s["l"] = "L"
end
與索引搜索、索引賦值(包括替換、插入、刪除元素)、索引檢查元素是否存在等操作有一些相對應的String方法。如下。
include?()
字元串中是否存在某子串。
include? other_str → true or false
"hello".include? "lo" #=> true
"hello".include? "ol" #=> false
"hello".include? ?h #=> true
index()
搜索某子串或匹配的子串的索引位置。
index(substr[, offset]) → int or nil
index(regexp[, offset]) → int or nil
例如:
"hello".index('e') #=> 1
"hello".index('lo') #=> 3
"hello".index('a') #=> nil
"hello".index(?e) #=> 1
"hello".index(/[aeiou]/, -3) #=> 4
replace()
替換字元串為另一個字元串,它會替換掉全部內容。它會原處修改字元串。
replace(other_str) → str
s = "hello" #=> "hello"
s.replace "world" #=> "world"
insert()
在字元串中某個位置處開始插入另一個字元串。它會原處修改字元串。
insert(index, other_str) → str
"abcd".insert(0, 'X') #=> "Xabcd"
"abcd".insert(3, 'X') #=> "abcXd"
"abcd".insert(4, 'X') #=> "abcdX"
"abcd".insert(-3, 'X') #=> "abXcd"
"abcd".insert(-1, 'X') #=> "abcdX"
字元串的迭代
因為字元串支持索引操作,所以可以直接通過索引的方式進行迭代。
a="hello"
i=0
while(i<a.length) do
puts a[i]
i += 1
end
0.upto(a.size - 1 ) do |x|
print a[x]
end
可以將字元串split成數組,然後通過each迭代。註意,Ruby 1.9里字元串不能直接通過each迭代了,它不再max-in Enumerable中的each。
a="hello"
a.split("").each do |x|
puts x
end
Ruby的字元串類中還定義了4個迭代方式:each_byte、each_char、each_line、each_codepoint。最常用的,顯然是each_char。
a = "hello"
a.each_char do |x|
puts x
end
each_byte、each_codepoint迭代時,得到的是各字元的ASCII碼或Unicode代碼點。
"我".each_byte.to_a #=> [230, 136, 145]
"我是單身狗".each_byte.to_a
#=> [230, 136, 145, 230, 152, 175, 229, 141,
# 149, 232, 186, 171, 231, 139, 151]
"我".each_codepoint.to_a #=> [25105]
"我是單身狗".each_codepoint.to_a
#=> [25105, 26159, 21333, 36523, 29399]
按塊讀取數據(或按段落讀取)時,很可能會用上each_line來迭代緩衝中的一大段數據的每一行。
each_line(separator=$/ [, getline_args]) {|substr| block } → str
每迭代,都將該行傳遞到代碼塊中。
其中:
separator
指定記錄(行)分隔符,預設的是\n
、\r
或\r\n
。
getline_args
指定讀取到每一行時的所作的操作,目前支持的是:chomp
選項,表示將每行尾部的換行符移除掉。這是非常方便的功能,這樣在迭代每一行的時候,不需要手動在代碼塊中使用chomp()了。
str="a line\nb line\nc line\n"
str.each_line {|x| puts "#{x}"}
# 輸出:
=begin
a line
b line
c line
=end
下麵是使用chomp
參數的功能。
str="a line\nb line\nc line\n"
str.each_line(chomp: true) {|x| puts "#{x}: #{x.length}"}
# 輸出:
=begin
a line: 6
b line: 6
c line: 6
=end
# 等價於
str.each_line {|x| x.chomp!();puts "#{x}: #{x.length}" }
# 輸出:
=begin
a line: 6
b line: 6
c line: 6
=end
下麵是指定行分隔符的用法。
str="a line\nb line\nc line\n"
str.each_line("e") {|x| puts "-#{x}-"}
# 輸出:
=begin
-a line-
-
b line-
-
c line-
-
-
=end
# 指定分隔符和chomp參數的時候
str.each_line("e", chomp:true) {|x| puts "-#{x}-"}
-a lin-
-
b lin-
-
c lin-
-
-
字元串的比較
Ruby中典型的幾種方法:<=>、eql?、equal?、==、===
。當然,因為實現了<=>
,所以也送了< > <= >= between?
這幾個比較方法。
其中,對於純字元串對象,==、===、eql?
等價,都用於判斷字元串內容是否相同。==
是判斷對象的內容是否完全一致(或者說是相等);===
是智能判斷,對於字元串而言,等價於==
;eql?
是根據計算對象hash值進行比較的,對於字元串的hash()方法來說,它是根據字元串長度、內容、編碼三個屬性來生成hash值的。
equal?
則最嚴格,只有雙方引用的是同一對象時,才返回true。
說明下eql?
比較。下麵是eql?()
只當不同編碼時的比較過程。
>> "我".encode("utf-8").eql?( "我".encode("utf-16") )
=> false
>> "hello".encode("utf-8").eql?( "hello".encode("utf-16") )
=> false
>> "hello".encode("utf-8").eql?( "hello".encode("gbk") )
=> true
此外,String類還實現了casecmp
和casecmp?
。前者是無視大小寫的<=>
,後者是無視大小寫的等值比較。編碼不同或一方不是字元串時,返回nil。
casecmp(other_str) → -1, 0, +1, or nil
casecmp?(other_str) → true, false, or nil
例如:
"1234abc".casecmp("1234ABC") #=> 0
"我".casecmp("我") #=> 0
"\u{c4 d6 dc}" #=> "ÄÖÜ"
"\u{e4 f6 fc}" #=> "äöü"
"\u{c4 d6 dc}".casecmp("\u{e4 f6 fc}") #=> -1
"\u{c4 d6 dc}".casecmp?("\u{e4 f6 fc}") #=> true