Ruby字符串《转》

Ruby很强大,可是相关资料少而不详细。本文是个人学习总结,测试环境是windows xp sp3 + NetBeans6.7.1(JRuby
1.2.0),主要结论来自于互联网、"Programming Ruby"2e、对于源代码的分析和实测代码。

双引号字符串和单引号字符串

都能表示字符串对象,区别在于双引号字符串能够支持更多的转义字符。下面的代码在字符串中增加了‘符号。 str=‘he‘lo’ puts str
显示结果为he‘lo。
单引号仅支持// => / 和 /‘ => ‘
下表是ruby中双引号字符串支持的转义字符:

分界符


所有不是字母或者数字的单字节字符都可以成为String的分界符。注意,通常他们都是成对出现的,比如<和>,!和!,{和}等。

构造字符串字面量

方法一: 最简单的使用单引号或者双引号括起来的字符串,比如"hello"。
方法二: 使用%q配合分界符,%q代表单引号
str=%q!he/lo!
方法三: 使用%Q配合分界符,%Q代表双引号 str=%Q{he/lo}
方法四: here
document构建字符串,该方法比较适合用于多行字符串的创建。由<<和边界字符串作为开头,由边界字符串作为结尾,比如下列代码: str =
<<END_OF_STRING1   We are here now,   where are you?
END_OF_STRING1 puts str 输出结果为:   We are here now,   where are
you?
较为复杂的是允许多个边界字符串对出现。 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
输出结果为:   We are here now,   where are you?   I will leave now,
  would you like to go with me?

字面量与copy-on-write技术

在Java中,如果两个String对象a和b的值都是"abcdef",如下: String a="abcdef";
String b="abcdef"; 那
么,JVM只会创建一个常量对象"abcdef",让a和b都指向它。但是在ruby中,采用了智能指针(熟悉c++的朋友清楚)的一个高级技术
copy-on-write,一开始也是共享同一个字符常量,但是一旦之后某个对象(比如b对象)进行了修改操作,则"abcdef"将产生一个副本,b
的修改操作在这个副本上进行。    
更详细的讨论请参考http://developer.51cto.com/art/200811/98630.htm。

和Java的一些其他区别

Java的String每次执行修改操作,都不会改变自身,而是创建一个新的String对象,而Ruby每次的修改操作都会修改自身。

计算长度

puts "hello".length    
该句输出5,是字符个数,不要和C函数搞混,C函数经常用0结束字符串,因此长度经常为实际字符个数+1,Ruby中没有这个习惯。

查找


从左向右查找第一个

index方法有三种重载,分别是: str.index(substring [, offset]) =>
fixnum or nil str.index(fixnum [, offset]) => fixnum or nil
str.index(regexp [, offset]) => fixnum or nil

    第二个参数offset是可选参数,不用的话则从索引0的字符开始查找。 puts "hello".index("el")
输出为1 ,注意这里的‘el‘也可以。也可以只查一个字符比,如puts "hello".index(101) 输出为1,这时候第一个参数为‘e‘的二进制码。
也可以使用正则表达式进行查找,比如puts "hello".index(/[az]/)
输出为nil,因为"hello"不包含a或者z。[]是正则表达式的运算符,代表里面的a和z有一个找到即可。 puts "hello".index(/lo/)
这个没有[]符号,因此是查找子字符串lo,结果为3.    
我个人觉得尽量熟练使用正则表达式查找是最好的选择,既可以完成简单查找,也可以完成难度查找。不过需要付出不少努力去学习。    
下面这个例子puts "hello".index(‘o‘, -1) 证明了第二个参数可以为负数,虽然这没有什么意义,因为功能和为0等价。
    如果查找不到,返回nil。

逆向查找(从左向右查找最后一个还是从右向左查找第一个)

str.rindex(substring [, fixnum]) =>
fixnum or nil str.rindex(fixnum [, fixnum]) => fixnum or nil
str.rindex(regexp [, fixnum]) => fixnum or nil
   
   
第一个参数和index相同,第二个参数是可选,如果不用则默认为字符串尾部。如果为0呢?则从第一个字符开始向右查找。如果为负数呢?这时候很奇怪,居然能查到。通过看C的实现代码,发现当fixnum<0时,会执行这个运算:fixnum+=substring.length,然后就能找到。逻辑上可以理解为当fixnum<0时,将从最右边开始向左移动abs(fixnum)-1个位置,并作为最后查找范围,然后开始从左至右进行查找。字符串最右边的字符的位置被-1代表。
下面两行代码结果都是nil: puts "hlloe".rindex(‘e‘, -2) puts "hlloe".rindex(‘e‘,
3)
下面两行代码结果都是1: puts "hello".rindex(‘e‘, -2) puts "hello".rindex(‘e‘, 3)

   
注意,以上的代码理解是我个人观察代码后的猜测,因为我还不会调试运行ruby的C代码,所以不一定正确。代码摘录如下:(代码是ruby网站公布的C代码,但是我所用的平台其实NetBeans6.7.1,因此真正代码应该是Java实现的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;
     
}

通常我们理解为从右边开始查找,但是注释却表明是从左向右查找,并返回最后一个找到的目标的位置。究竟内幕如何,只能看代码。 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 }
   
通过看代码,发现s--;因此,是从右向左进行匹配,找到的第一个就返回。写注释的人应该枪毙!虽然看上去意思一样,但是算法的时间复杂度大不一样。从左到右的查找总是O(n),而从右到左的最坏事件复杂度才是O(n)。

大小写不区分查找

puts
"hello".upcase.index("H"),利用downcase或者upcase全部转换成小写或者大写,然后再查找。

正则表达式匹配查找

operator =~ 将返回匹配的模式开始位置,如果没有找到则返回nil。 puts "abcde789" =~ /d/ 输出5.

提取子字符串

str="hello" puts str[0,2] 第一个参数是子字符串首字母的Index,第二个是长度(不能为负数)。 结果为he。
第一个参数可以为负数,会把最右边的字符作为-1,然后向左增加-1的方式查找起始位置,比如: str="hello" puts str[-2,2]
输出为lo,这种情况我们在rindex方法中已经看到过了。
也可以使用正则表达式进行提取,这真的很强大。 str="hello" puts
str[/h..l/] 输出为hell。
符号.代表一个字符,两个.代表两个字符。两个/里面的内容就是正则表达式。.*代表可以有无数个字符,比如
str="hello" puts str[/h.*o/] 输出为hello。

字符计数

String#count用来计算我们参数中给出的字符集中字符出现的总次数,比如最简单的情况: str = "hello,world" puts
str.count "w"  “w"
参数代表的是一个字符结合,里面只有一个字符w,count方法计算出w出现在"hello,world"的次数是1,因此输出为1。
下面我们的参数里面包含了三个字符: str = "hello,world" puts str.count "wld"
输出为5,w出现1次,l出现3次,d出现1次,正好5次。

也可以传递多个参数,每个参数代表一个字符集合,这时候这些字符集合的交集作为count计算的条件: str = "hello,world" puts
str.count "lo","o" 输出为2。 str = "hello,world" puts str.count "lo","o"," "
输出为0,因为三个集合的交集为空,所以计算结果为0.
注意,如果参数^o,代表o出现的次数不计算。

删除末尾分隔符

String#chomp方法有一个字符串参数,指定了要在末尾删除的子字符串。如果不用这个参数,则会将字符串末尾的n,r和rn删除(如果有的话)。

压缩重复字符

String#squeeze方法如果不用参数,则会将字符串中的任何连续重复字符变成单一字符,如下: str = "helllloo" puts
str.squeeze 输出:helo。
如果传递字符串参数,含义同count方法的参数一样,代表了一个字符集合,则将符合条件(1,在字符集合中出现;2,在字符串中连续出现)的子字符串压缩成的单一字符
实例代码如下: str = "helllloo" puts str.squeeze(‘l‘) puts str.squeeze(‘a-l‘) puts
str.squeeze(‘lo‘) 输出为: heloo heloo helo
   
参数也可以用a-z方式表示在某个字符集合区间内。
一个很常用的功能是利用squeeze(" ")对字符串内重复的空白字符进行压缩。

字符串删除

delete方法


可以接收多个参数,每个参数代表一个字符集合,类似count方法。如果有多个参数,取交集,然后从字符串中删除所有出现在交集中的字符。 "hello".delete "l","lo" #=> "heo"
"hello".delete "lo" #=> "he" "hello".delete "aeiou", "^e" #=> "hell"
"hello".delete "ej-m" #=> "ho"

利用sub和gsub

参见后面的sub用法,使用‘‘进行替换即可。

字符串拆分

String#split接收两个参数,第一个参数总是被作为间隔符来拆分字符串,并且不会出现在结果中。
第一个参数如果是正则表达式的话,如果为空,则每个字符都被拆开,返回一个字符数组。例子代码如下: str = "hello" puts
str.split(//) 输出为: h e l l o
   
如果正则表达式不为空,则根据匹配的情况进行拆分。例子代码如下: str = "hello" puts str.split(/h/)
结果为:
ello
拆分成了两个数组,第一个为"",第二个为ello,用h进行拆分的。
第一个参数的另一种用法很简单,只是一个字符串,用于作为间隔符进行拆分,就不举例子了。我更倾向于使用强大的正则表达式。

第二个参数是一个整数,用于对拆分的结果数组的元素个数进行限制,这个功能有多大用处,我现在到没有体会,一般情况下不用即可。

大小写转换

如前面出现的,利用downcase或者upcase方法即可。

数组操作

使用[],里面填上Index,就可以获取第Index个元素。

和数值类型的相互转换

获取单字节字符的二进制码 puts ?e ?运算符用于中文是非法的。

字符串迭代

Ruby迭代器的设计不在这里讨论,我会专门有一篇文章描述。

each_char

迭代每个字符,下面是示例代码: require ‘jcode‘ #NetBeans6.7.1和JRuby1.2.0需要,否则下面代码找不到方法
"hello".each_char(){ |c| print c,‘ ‘ } #()可以不写
|c| 代表字符串中的当前字符。

each

迭代每个子字符串,如果不传递seperator参数,则默认用n作为seperator。 "hellonworld".each { |c| puts c }
输出为: hello world
如果传递了有效的字符串作为seperator参数,那么就以这个seperator代替n进行子字符串的迭代:
"hellonworld".each(‘l‘) { |s| p s } 输出为: "hel" "l" "onworl" "d"

each_byte

用法和each_char类似,不过迭代的对象是char,因此输出的是二进制数值。 "hellonworld".each_byte { |s| print
s," " } 输出: 104 101 108 108 111 10 119 111 114 108 100

each_line

用法和前面相同,只是用换行符分割子字符串进行迭代: "hellonworld".each_line do |s|   print s end
    注意,这是另一种写法,用do/end替换了{/}对。 输出为: hello world
只所以输出为两行,是因为第一个子字符串是"hellon"输出后自动换行。

字符串拼接


使用operator +操作

str1="hello," str2="world" str3=str1+str2 puts str3 输出为hello,world

使用operator <<操作

str1="hello," str2="world" str1< puts str1 输出为hello,world

concat方法

concat方法可以在字符串后面加上一个二进制值为[0,255]的字符,用法如下: str1="hello,world"
str1.concat(33)#33是!的二进制值 puts str1 输出为hello,world!

concat也可以接一个object,比如另一个String对象

是否为空

String#empty? 方法 如果为空返回true,否则返回false

字符串比较


operator<=>操作

str1<=>str2 如果str1小于str2,返回-1; 如果str1等于str2,返回0;
如果str1大于str2,返回1。
官方注释写反了。

operator==操作

两个比较对象必须都为String,否则返回false; 如果都是String对象,则调用operator <=>
操作符进行比较,比较结果为0时,返回true,否则返回false

字符串替换


replace方法

和operator = 功能相同,字符串内容的完全替换,没什么作用。

sub方法

str.sub(pattern, replacement) =>
new_str str.sub(pattern) {|match| block } => new_str


在str副本上将找到的第一个匹配字符(串)用replacement替换,并返回。比如: puts "abcde789".sub(/d/, "000")
输出为:abcde00089
第二种重载形式允许执行一段代码,比如: puts "abcde789".sub(/d/){|c| ‘a‘}
找到的字符用|c|表示,可以替换成a字符 输出为:abcdea89

gsub方法

和sub的区别在于所有匹配的地方都会被替换,而不只是第一个。

未完待续,后面会谈到Array#pack和String#unpack以及编码问题。

时间: 2024-12-22 11:21:28

Ruby字符串《转》的相关文章

ruby 字符串学习2

在一个ruby字符串中包含表但是或者变量.想使用不同的值替换表达式或者变量 1 类似java 或者python的printf-style方式 template = 'Oceania has always been at war with %s.' template % 'Eurasia' # => "Oceania has always been at war with Eurasia." template % 'Eastasia' # => "Oceania h

ruby字符串相关方法

构造字符串字面量 方法一:最简单的使用单引号或者双引号括起来的字符串,比如"hello". 方法二:使用%q配合分界符,%q代表单引号str=%q!he/lo! 方法三:使用%Q配合分界符,%Q代表双引号str=%Q{he/lo} 方法四:here document构建字符串,该方法比较适合用于多行字符串的创建.由<<和边界字符串作为开头,由边界字符串作为结尾,比如下列代码:str = <<END_OF_STRING1  We are here now,  wh

雷林鹏分享:Ruby 字符串(String)

Ruby 字符串(String) Ruby 中的 String 对象存储并操作一个或多个字节的任意序列,通常表示那些代表人类语言的字符. 最简单的字符串是括在单引号(单引号字符)内.在引号标记内的文本是字符串的值: 'This is a simple Ruby string literal' 如果您需要在单引号字符串内使用单引号字符,那么需要在单引号字符串使用反斜杠,这样 Ruby 解释器就不会认为这个单引号字符会终止字符串: 'Won\'t you read O\'Reilly\'s book

Ruby字符串(1):String基本用法

String字符串 字符串由String类提供,除了直接使用单双引号或其它字面量创建字符串,也可以使用String.new()方法来创建. a = "hello" b = String.new("world") Ruby中的字符串是可变对象. 字符串的连接 直接连接即可: >> "a""b" => "ab" >> "a" "b" =>

ruby 字符串

字符串处理函数 1.返回字符串的长度 str.length => integer 2.判断字符串中是否包含另一个串 str.include? other_str => true or false "hello".include? "lo"#=> true"hello".include? "ol"#=> false"hello".include? ?h #=> true 3.字符

ruby字符串处理

字符串处理函数1.返回字符串的长度 str.length => integer 2.判断字符串中是否包含另一个串 str.include? other_str => true or false"hello".include? "lo"   #=> true"hello".include? "ol"   #=> false"hello".include? ?h     #=> t

Ruby快速入门

Rb是什么 ? 交互式Ruby(IRB)为实验提供了一个shell.内置IRB shell,你可以立即一行行查看表达式的结果.该工具自带Ruby安装,所以你必须做一些额外的IRB工作无关.只需键入在命令提示符IRB和交互式Ruby会话将启动. Ruby语法: Ruby代码一般忽略空白字符,如空格和制表符,除非当他们出现在字符串. Ruby的解释分号作为语句的结尾换行符.但是,如果ruby遇到运算符,如+, - ,或在一行的末尾的反斜杠,他们的声明中表示延续. 标识符名称的变量,常量和方法. Ru

Ruby中的chop和chomp用法辨析

      还没开始系统性的学习Ruby,最近在看metasploit框架的exploit会涉及到Ruby脚本,也就硬着头皮一遍查阅资料一遍做些笔记吧.       Ruby字符串中存在chop和chomp的内置函数.我在http://www.w3cschool.cc/ruby/ruby-string.html中得到的关于Ruby字符串chop和chomp的用法介绍如下: str.chomp 从字符串末尾移除记录分隔符($/),通常是 \n.如果没有记录分隔符,则不进行任何操作. str.cho

《python源码剖析》笔记 python中的字符串对象

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.      PyStringObject --> 变长不可变对象 typedef struct{ PyObject_VAR_HEAD//ob_size变量保存着对象中维护的可变长度内存的大小 longob_shash; //缓存该对象的hash值,用于dict的查询 intob_sstate; //标志该对象是否经过intern机制的处理 char ob_sval[1];// 字符指针