Ruby學(xué)習(xí)1-字符串
操作1:判定字符串中是否含有字串/子模式
string[substring]
string[/pattern/]
string[/pattern/, position] #position之后的子串中是否含有/pattern/
如果存在返回子串/子模式串,否則返回nil
“hello world"["hello"]==="hello"
"hello world"[/.*lo/]==="hello"
"hello world"[/en/]===nil
操作2:使用索引截取子串
string[position] #注意返回的是ASCII碼而不是字符
string[start, length]
string[start..end]
string[start...end]
2,比較:
== #比較字符串是否相等
eql? #??好像沒有區(qū)別
<=> #用來比較字符串的大小,大于返回 1,小于返回 -1, 否則返回0
3,字符串的運(yùn)算
downcase #改變字符串為全部小寫
upcase #改變字符串為全部大寫
swapcase#反寫
capitalize #改變字符串為首字母大寫
* #重復(fù)字符串
insert num, string #在num位置插入串string(insert沒有!,因?yàn)閕nsert會(huì)直接改變?cè)?br /> delete(!) string1 (,string2) #刪除string1交string2的字符
gsub find, replace #將串中的find,替換為replace. find可以是正則表達(dá)式,replace很顯然不可以。注意:是所有相同的都替換,相當(dāng)與sed中的s/pattern/string/g
replace string #將字符串替換為string, 這是對(duì)象沒有變,只是其中的內(nèi)容發(fā)生了變化。
利用切片來改變字符串(silce!, [ ]=)
"hello"["ello"]= "w" # "hw"
"hello"[1]="wan" # "hwanllo"
“hello"[1..3]= "wrd" #"hwrdo"
"hello"[1...3]= "wr" #"hwrlo"
"hello"[1,3]="wrd" #"hwrdo"
"hello"[/el/]= "wr" #"hwrlo"
chomp(!) 用來摘除字符串末尾的換行符(如果不是換行符返回空)#注意只能是換行符,空格都不行
chop(!)用來摘除字符串末尾的最后一個(gè)字符
reverse(!)首尾倒置
split(/pattern/)將字符串分割成數(shù)組,分隔符是/pattern/(注意帶!的方法不能用來改變類,所以split沒有!)
字符串長(zhǎng)度
string.length
string.size
字符串對(duì)齊
string.ljust num, char #用char來填充string,不足num的部分。注意左對(duì)齊是右填充。如果字符串長(zhǎng)度比char大,忽略
string.rjust num, char
string.center num, char
string.lstring #trim字符串,去除左邊空格
string.rstring
string.strip
..........那么如何去掉所有的空格呢? 很簡(jiǎn)單,使用gsub,進(jìn)行替換
string.next/succ #string+1 不是+1這么簡(jiǎn)單。"a".next == "zz"
string1.upto(stringn) #string1, string2 ....stringn
字符串遍歷:
string.each #分割不同項(xiàng)的必須是\n "hello\nworld".each {|e| puts e << ","}===
hello,
world,
"hello world".each{|e| puts e << ","}===
hello world,
string.each_byte #以字節(jié)為單位遍歷
求字串的索引位置
string.index substring #正則表達(dá)式也可以
正則表達(dá)式專用
string.grep /pattern/ #如果不是正則表達(dá)式搜索不到任何東西,如果是且匹配返回包含整個(gè)字符串的一個(gè)數(shù)組
string =~ /pattern/ #pattern第一次出現(xiàn)的位置
string !~ /pattern/ #如果沒有找到/pattern返回true(注意!)
uby很強(qiáng)大,可是相關(guān)資料少而不詳細(xì)。本文是個(gè)人學(xué)習(xí)總結(jié),測(cè)試環(huán)境是windows xp sp3 + NetBeans6.7.1(JRuby 1.2.0),主要結(jié)論來自于互聯(lián)網(wǎng)、"Programming Ruby"2e、對(duì)于源代碼的分析和實(shí)測(cè)代碼。
雙引號(hào)字符串和單引號(hào)字符串
都能表示字符串對(duì)象,區(qū)別在于雙引號(hào)字符串能夠支持更多的轉(zhuǎn)義字符。下面的代碼在字符串中增加了'符號(hào)。
str=‘he'lo’
puts str
顯示結(jié)果為he'lo。
單引號(hào)僅支持\\ => \ 和 \' => '
下表是ruby中雙引號(hào)字符串支持的轉(zhuǎn)義字符:
分界符
所有不是字母或者數(shù)字的單字節(jié)字符都可以成為String的分界符。注意,通常他們都是成對(duì)出現(xiàn)的,比如<和>,!和!,{和}等。
構(gòu)造字符串字面量
方法一:
最簡(jiǎn)單的使用單引號(hào)或者雙引號(hào)括起來的字符串,比如"hello"。
方法二:
使用%q配合分界符,%q代表單引號(hào)
str=%q!he\lo!
方法三:
使用%Q配合分界符,%Q代表雙引號(hào)
str=%Q{he\lo}
方法四:
here document構(gòu)建字符串,該方法比較適合用于多行字符串的創(chuàng)建。由<<和邊界字符串作為開頭,由邊界字符串作為結(jié)尾,比如下列代碼:
str = <<END_OF_STRING1
We are here now,
where are you?
END_OF_STRING1
puts str
輸出結(jié)果為:
We are here now,
where are you?
較為復(fù)雜的是允許多個(gè)邊界字符串對(duì)出現(xiàn)。
str = <<END_OF_STRING1,<<END_OF_STRING2
We are here now,
where are you?
END_OF_STRING1
I will leave now,
would you like to go with me?
END_OF_STRING2
puts str
輸出結(jié)果為:
We are here now,
where are you?
I will leave now,
would you like to go with me?
字面量與copy-on-write技術(shù)
在Java中,如果兩個(gè)String對(duì)象a和b的值都是"abcdef",如下:
String a="abcdef";
String b="abcdef";
那
么,JVM只會(huì)創(chuàng)建一個(gè)常量對(duì)象"abcdef",讓a和b都指向它。但是在ruby中,采用了智能指針(熟悉c++的朋友清楚)的一個(gè)高級(jí)技術(shù)
copy-on-write,一開始也是共享同一個(gè)字符常量,但是一旦之后某個(gè)對(duì)象(比如b對(duì)象)進(jìn)行了修改操作,則"abcdef"將產(chǎn)生一個(gè)副本,b
的修改操作在這個(gè)副本上進(jìn)行。
更詳細(xì)的討論請(qǐng)參考http://developer.51cto.com/art/200811/98630.htm。
和Java的一些其他區(qū)別
Java的String每次執(zhí)行修改操作,都不會(huì)改變自身,而是創(chuàng)建一個(gè)新的String對(duì)象,而Ruby每次的修改操作都會(huì)修改自身。
計(jì)算長(zhǎng)度
puts "hello".length
該句輸出5,是字符個(gè)數(shù),不要和C函數(shù)搞混,C函數(shù)經(jīng)常用0結(jié)束字符串,因此長(zhǎng)度經(jīng)常為實(shí)際字符個(gè)數(shù)+1,Ruby中沒有這個(gè)習(xí)慣。
查找
從左向右查找第一個(gè)
index方法有三種重載,分別是:
str.index(substring [, offset]) => fixnum or nil
str.index(fixnum [, offset]) => fixnum or nil
str.index(regexp [, offset]) => fixnum or nil
第二個(gè)參數(shù)offset是可選參數(shù),不用的話則從索引0的字符開始查找。
puts "hello".index("el") 輸出為1 ,注意這里的'el'也可以。也可以只查一個(gè)字符比,如puts "hello".index(101) 輸出為1,這時(shí)候第一個(gè)參數(shù)為'e'的二進(jìn)制碼。
也可以使用正則表達(dá)式進(jìn)行查找,比如puts "hello".index(/[az]/) 輸出為nil,因?yàn)?hello"不包含a或者z。[]是正則表達(dá)式的運(yùn)算符,代表里面的a和z有一個(gè)找到即可。
puts "hello".index(/lo/) 這個(gè)沒有[]符號(hào),因此是查找子字符串lo,結(jié)果為3.
我個(gè)人覺得盡量熟練使用正則表達(dá)式查找是最好的選擇,既可以完成簡(jiǎn)單查找,也可以完成難度查找。不過需要付出不少努力去學(xué)習(xí)。
下面這個(gè)例子puts "hello".index('o', -1) 證明了第二個(gè)參數(shù)可以為負(fù)數(shù),雖然這沒有什么意義,因?yàn)楣δ芎蜑?等價(jià)。
如果查找不到,返回nil。
逆向查找(從左向右查找最后一個(gè)還是從右向左查找第一個(gè))
str.rindex(substring [, fixnum]) => fixnum or nil
str.rindex(fixnum [, fixnum]) => fixnum or nil
str.rindex(regexp [, fixnum]) => fixnum or nil
第一個(gè)參數(shù)和index相同,第二個(gè)參數(shù)是可選,如果不用則默認(rèn)為字符串尾部。如果為0呢?則從第一個(gè)字符開始向右查找。如果為負(fù)數(shù)呢?這時(shí)候很奇怪,居
然能查到。通過看C的實(shí)現(xiàn)代碼,發(fā)現(xiàn)當(dāng)fixnum<0時(shí),會(huì)執(zhí)行這個(gè)運(yùn)算:fixnum+=substring.length,然后就能找到。邏
輯上可以理解為當(dāng)fixnum<0時(shí),將從最右邊開始向左移動(dòng)abs(fixnum)-1個(gè)位置,并作為最后查找范圍,然后開始從左至右進(jìn)行查找。
字符串最右邊的字符的位置被-1代表。
下面兩行代碼結(jié)果都是nil:
puts "hlloe".rindex('e', -2)
puts "hlloe".rindex('e', 3)
下面兩行代碼結(jié)果都是1:
puts "hello".rindex('e', -2)
puts "hello".rindex('e', 3)
注意,以上的代碼理解是我個(gè)人觀察代碼后的猜測(cè),因?yàn)槲疫€不會(huì)調(diào)試運(yùn)行ruby的C代碼,所以不一定正確。代碼摘錄如下:(代碼是ruby網(wǎng)站公布的C代
碼,但是我所用的平臺(tái)其實(shí)NetBeans6.7.1,因此真正代碼應(yīng)該是Java實(shí)現(xiàn)的JRuby1.2.0,這里的C代碼僅供參考)
static VALUE
rb_str_rindex_m(argc, argv, str)
int argc;
VALUE *argv;
VALUE str;
{
VALUE sub;
VALUE position;
long pos;
if (rb_scan_args(argc, argv, "11", ⊂, &position) == 2) {
pos = NUM2LONG(position);
if (pos < 0) {
pos += RSTRING(str)->len;
if (pos < 0) {
if (TYPE(sub) == T_REGEXP) {
rb_backref_set(Qnil);
}
return Qnil;
}
}
if (pos > RSTRING(str)->len) pos = RSTRING(str)->len;
}
else {
pos = RSTRING(str)->len;
}
switch (TYPE(sub)) {
case T_REGEXP:
if (RREGEXP(sub)->len) {
pos = rb_reg_adjust_startpos(sub, str, pos, 1);
pos = rb_reg_search(sub, str, pos, 1);
}
if (pos >= 0) return LONG2NUM(pos);
break;
case T_STRING:
pos = rb_str_rindex(str, sub, pos);
if (pos >= 0) return LONG2NUM(pos);
break;
case T_FIXNUM:
{
int c = FIX2INT(sub);
unsigned char *p = (unsigned char*)RSTRING(str)->ptr + pos;
unsigned char *pbeg = (unsigned char*)RSTRING(str)->ptr;
if (pos == RSTRING(str)->len) {
if (pos == 0) return Qnil;
--p;
}
while (pbeg <= p) {
if (*p == c) return LONG2NUM((char*)p - RSTRING(str)->ptr);
p--;
}
return Qnil;
}
通常我們理解為從右邊開始查找,但是注釋卻表明是從左向右查找,并返回最后一個(gè)找到的目標(biāo)的位置。究竟內(nèi)幕如何,只能看代碼。
01161 static
long
01162
rb_str_rindex
(str, sub, pos)
01163 VALUE
str, sub;
01164 long
pos;
01165 {
01166 long
len = RSTRING
(sub)->len;
01167 char
*s, *sbeg, *t;
01168
01169 /* substring longer than string */
01170 if
(RSTRING
(str)->len < len) return
-1;
01171 if
(RSTRING
(str)->len - pos < len) {
01172 pos = RSTRING
(str)->len - len;
01173 }
01174 sbeg = RSTRING
(str)->ptr;
01175 s = RSTRING
(str)->ptr + pos;
01176 t = RSTRING
(sub)->ptr;
01177 if
(len) {
01178 while
(sbeg <= s) {
01179
if
(
rb_memcmp
(s, t, len) == 0) {
01180
return
s -
RSTRING
(str)->ptr;
01181 }
01182 s--;
01183 }
01184 return
-1;
01185 }
01186 else
{
01187 return
pos;
01188 }
01189 }
通過看代碼,發(fā)現(xiàn)s--;因此,是從右向左進(jìn)行匹配,找到的第一個(gè)就返回。寫注釋的人應(yīng)該槍斃!雖然看上去意思一樣,但是算法的時(shí)間復(fù)雜度大不一樣。從左到右的查找總是O(n),而從右到左的最壞事件復(fù)雜度才是O(n)。
大小寫不區(qū)分查找
puts "hello".upcase.index("H"),利用downcase或者upcase全部轉(zhuǎn)換成小寫或者大寫,然后再查找。
正則表達(dá)式匹配查找
operator =~ 將返回匹配的模式開始位置,如果沒有找到則返回nil。
puts "abcde789" =~ /d/
輸出5.
提取子字符串
str="hello"
puts str[0,2]
第一個(gè)參數(shù)是子字符串首字母的Index,第二個(gè)是長(zhǎng)度(不能為負(fù)數(shù))。
結(jié)果為he。
第一個(gè)參數(shù)可以為負(fù)數(shù),會(huì)把最右邊的字符作為-1,然后向左增加-1的方式查找起始位置,比如:
str="hello"
puts str[-2,2]
輸出為lo,這種情況我們?cè)趓index方法中已經(jīng)看到過了。
也可以使用正則表達(dá)式進(jìn)行提取,這真的很強(qiáng)大。
str="hello"
puts str[/h..l/]
輸出為hell。
符號(hào).代表一個(gè)字符,兩個(gè).代表兩個(gè)字符。兩個(gè)/里面的內(nèi)容就是正則表達(dá)式。.*代表可以有無數(shù)個(gè)字符,比如
str="hello"
puts str[/h.*o/]
輸出為hello。
字符計(jì)數(shù)
String#count用來計(jì)算我們參數(shù)中給出的字符集中字符出現(xiàn)的總次數(shù),比如最簡(jiǎn)單的情況:
str = "hello,world"
puts str.count "w"
“w" 參數(shù)代表的是一個(gè)字符結(jié)合,里面只有一個(gè)字符w,count方法計(jì)算出w出現(xiàn)在"hello,world"的次數(shù)是1,因此輸出為1。
下面我們的參數(shù)里面包含了三個(gè)字符:
str = "hello,world"
puts str.count "wld"
輸出為5,w出現(xiàn)1次,l出現(xiàn)3次,d出現(xiàn)1次,正好5次。
也可以傳遞多個(gè)參數(shù),每個(gè)參數(shù)代表一個(gè)字符集合,這時(shí)候這些字符集合的交集作為count計(jì)算的條件:
str = "hello,world"
puts str.count "lo","o"
輸出為2。
str = "hello,world"
puts str.count "lo","o"," "
輸出為0,因?yàn)槿齻€(gè)集合的交集為空,所以計(jì)算結(jié)果為0.
注意,如果參數(shù)^o,代表o出現(xiàn)的次數(shù)不計(jì)算。
刪除末尾分隔符
String#chomp方法有一個(gè)字符串參數(shù),指定了要在末尾刪除的子字符串。如果不用這個(gè)參數(shù),則會(huì)將字符串末尾的n,r和rn刪除(如果有的話)。
壓縮重復(fù)字符
String#squeeze方法如果不用參數(shù),則會(huì)將字符串中的任何連續(xù)重復(fù)字符變成單一字符,如下:
str = "helllloo"
puts str.squeeze
輸出:helo。
如果傳遞字符串參數(shù),含義同count方法的參數(shù)一樣,代表了一個(gè)字符集合,則將符合條件(1,在字符集合中出現(xiàn);2,在字符串中連續(xù)出現(xiàn))的子字符串壓縮成的單一字符
實(shí)例代碼如下:
str = "helllloo"
puts str.squeeze('l')
puts str.squeeze('a-l')
puts str.squeeze('lo')
輸出為:
heloo
heloo
helo
參數(shù)也可以用a-z方式表示在某個(gè)字符集合區(qū)間內(nèi)。
一個(gè)很常用的功能是利用squeeze(" ")對(duì)字符串內(nèi)重復(fù)的空白字符進(jìn)行壓縮。
字符串刪除
delete方法
可以接收多個(gè)參數(shù),每個(gè)參數(shù)代表一個(gè)字符集合,類似count方法。如果有多個(gè)參數(shù),取交集,然后從字符串中刪除所有出現(xiàn)在交集中的字符。
"hello".delete "l","lo" #=> "heo"
"hello".delete "lo" #=> "he"
"hello".delete "aeiou", "^e" #=> "hell"
"hello".delete "ej-m" #=> "ho"
利用sub和gsub
參見后面的sub用法,使用''進(jìn)行替換即可。
字符串拆分
String#split接收兩個(gè)參數(shù),第一個(gè)參數(shù)總是被作為間隔符來拆分字符串,并且不會(huì)出現(xiàn)在結(jié)果中。
第一個(gè)參數(shù)如果是正則表達(dá)式的話,如果為空,則每個(gè)字符都被拆開,返回一個(gè)字符數(shù)組。例子代碼如下:
str = "hello"
puts str.split(//)
輸出為:
h
e
l
l
o
如果正則表達(dá)式不為空,則根據(jù)匹配的情況進(jìn)行拆分。例子代碼如下:
str = "hello"
puts str.split(/h/)
結(jié)果為:
ello
拆分成了兩個(gè)數(shù)組,第一個(gè)為"",第二個(gè)為ello,用h進(jìn)行拆分的。
第一個(gè)參數(shù)的另一種用法很簡(jiǎn)單,只是一個(gè)字符串,用于作為間隔符進(jìn)行拆分,就不舉例子了。我更傾向于使用強(qiáng)大的正則表達(dá)式。
第二個(gè)參數(shù)是一個(gè)整數(shù),用于對(duì)拆分的結(jié)果數(shù)組的元素個(gè)數(shù)進(jìn)行限制,這個(gè)功能有多大用處,我現(xiàn)在到?jīng)]有體會(huì),一般情況下不用即可。
大小寫轉(zhuǎn)換
如前面出現(xiàn)的,利用downcase或者upcase方法即可。
數(shù)組操作
使用[],里面填上Index,就可以獲取第Index個(gè)元素。
和數(shù)值類型的相互轉(zhuǎn)換
獲取單字節(jié)字符的二進(jìn)制碼
puts ?e
?運(yùn)算符用于中文是非法的。
字符串迭代
Ruby迭代器的設(shè)計(jì)不在這里討論,我會(huì)專門有一篇文章描述。
each_char
迭代每個(gè)字符,下面是示例代碼:
require 'jcode' #NetBeans6.7.1和JRuby1.2.0需要,否則下面代碼找不到方法
"hello".each_char(){ |c| print c,' ' } #()可以不寫
|c| 代表字符串中的當(dāng)前字符。
each
迭代每個(gè)子字符串,如果不傳遞seperator參數(shù),則默認(rèn)用n作為seperator。
"hellonworld".each { |c| puts c }
輸出為:
hello
world
如果傳遞了有效的字符串作為seperator參數(shù),那么就以這個(gè)seperator代替n進(jìn)行子字符串的迭代:
"hellonworld".each('l') { |s| p s }
輸出為:
"hel"
"l"
"onworl"
"d"
each_byte
用法和each_char類似,不過迭代的對(duì)象是char,因此輸出的是二進(jìn)制數(shù)值。
"hellonworld".each_byte { |s| print s," " }
輸出:
104 101 108 108 111 10 119 111 114 108 100
each_line
用法和前面相同,只是用換行符分割子字符串進(jìn)行迭代:
"hellonworld".each_line do |s|
print s
end
注意,這是另一種寫法,用do/end替換了{(lán)/}對(duì)。
輸出為:
hello
world
只所以輸出為兩行,是因?yàn)榈谝粋€(gè)子字符串是"hellon"輸出后自動(dòng)換行。
字符串拼接
使用operator +操作
str1="hello,"
str2="world"
str3=str1+str2
puts str3
輸出為hello,world
使用operator <<操作
str1="hello,"
str2="world"
str1<
puts str1
輸出為hello,world
concat方法
concat方法可以在字符串后面加上一個(gè)二進(jìn)制值為[0,255]的字符,用法如下:
str1="hello,world"
str1.concat(33)#33是!的二進(jìn)制值
puts str1
輸出為hello,world!
concat也可以接一個(gè)object,比如另一個(gè)String對(duì)象
是否為空
String#empty? 方法 如果為空返回true,否則返回false
字符串比較
operator<=>操作
str1<=>str2
如果str1小于str2,返回-1;
如果str1等于str2,返回0;
如果str1大于str2,返回1。
官方注釋寫反了。
operator==操作
兩個(gè)比較對(duì)象必須都為String,否則返回false;
如果都是String對(duì)象,則調(diào)用operator <=> 操作符進(jìn)行比較,比較結(jié)果為0時(shí),返回true,否則返回false
字符串替換
replace方法
和operator = 功能相同,字符串內(nèi)容的完全替換,沒什么作用。
sub方法
str.sub(pattern, replacement) => new_str
str.sub(pattern) {|match| block } => new_str
在str副本上將找到的第一個(gè)匹配字符(串)用replacement替換,并返回。比如:
puts "abcde789".sub(/d/, "000")
輸出為:abcde00089
第二種重載形式允許執(zhí)行一段代碼,比如:
puts "abcde789".sub(/d/){|c| 'a'}
找到的字符用|c|表示,可以替換成a字符
輸出為:abcdea89
gsub方法
和sub的區(qū)別在于所有匹配的地方都會(huì)被替換,而不只是第一個(gè)。posted on 2010-01-03 16:22 草原上的駱駝 閱讀(4066) 評(píng)論(0) 編輯 收藏 所屬分類: Ruby