SlideShare a Scribd company logo
1 of 11
Download to read offline
第三章

字符串和区间
到目前为止,我已经在我的程序中大量使用了字符串。实际上,在这本书中最早的程序中就
使用到了字符串。就是这段代码:

   puts 'hello world'

在我的第一个程序中我使用单引号将字符串引起来,在我的第二个程序中我以另外一种方式,
那就是双引号将字符串引起来:

   print('Enter your name: ' )

   name = gets()

   puts( "Hello #{name}" )
                                                                1strings.rb

双引号能做的事情比单引号要多。特别是它们能在代码段中进行赋值,即使都是在引号内的
代码。为了能对变量赋值,你需要使用花括号将变量括起来并在前面加#字符。

在上面的例子里,#{name}在一个双引号字符串中,它将告诉 Ruby 取得 name 变量的值并
将它的值插入该字符串。如果 name 等于”Fred”,那么将打印出”Hello Fred”。
1Strings.rb 示范程序还提供了一些其他的关于双引号字符串内嵌赋值的示例。

一个双引号字符串不仅仅能给像 ob.name 这样的属性或者变量赋值,还能在双引号字符串
中添加表达式如 2*3 和一些代码段如 ob.ten(ten 是一个方法名),以及转义字符如
”n”和”t”分别表示换行和制表符。

一个单引号字符串不能做这样的赋值。然而单引号字符串中可以使用反斜杠来指明下一个字
符按字面意义进行取值。当一个单引号字符串中包含了单引号的时候,这个特性还是有用的,
如下:

   'It's my party'

假设 ten 方法返回值为 10,你可以像下面这样写代码:

   puts( "Here's a tabta new linena calculation #{2*3} and a method-call
#{ob.ten}" )

由于这是一个双引号字符串,内嵌元素都会进行赋值,打印结果如下:

   Here's a tab       a new line
a calculation 6 and a method-call 10

现在,让我们来看看当使用单引号字符串的时候会发生什么:

    puts( 'Here's a tabta new linena calculation #{2*3} and a    method-call
#{ob.ten}' )

这次,没有内嵌赋值,它的打印如下:

    Here's a tabta new linena calculation #{2*3} and a method- call #{ob.ten}


用户自定义字符分隔符(界定符)
如果出于某种原因,单引号和双引号字符串都不是很方便你使用的话,例如,你的字符串包
含了他多的引号,然而你又不想在每个引号前加反斜杠,你也可以通过其他很多方法来分隔
你的字符串。
                                                                   2strings.rb

标准的可代替双引号作为字符串界定符的是%Q 和/或者%/和/,而/q 和/可替代单引号。因
此可以这么写:

    %Q/This is the same as a double-quoted string./

    %/This is also the same as a double-quoted string./

    %q/And this is the same as a single-quoted string/

你甚至可以定义你自己的字符界定符。当然其必须是非字母数字的字符,可以包含非打印字
符如换行符,或者其他很多通常在 Ruby 中有特殊意义的字符,如哈希符(#)。你选择的字符
应该跟随在%q 或者%Q 后面,同时你还得保证使用相同的字符来结束字符串。如果你的界
定符是一个开放的括号,那么你必须在字符串的末尾使用相对应的关闭括号进行界定,如下:

%Q[This is a string]                                               3strings.rb

在 3strings.rb 程序中,你将发现大量的用户精选的字符串界定符的示例。无需多说,在很
多时候使用一些隐秘的字符,如换行符,星号等来进行字符串的界定是很有用的,但是在很
多情况下,实际使用中由此引发的的弊(不仅仅是精神上的痛苦和混乱)总是大于利的。


反引号
另一种字符值得引起特殊的重视,那就是由反引号引起来的字符串,这个引号键在我们键盘
的左手上方的角落里,是一个常被我们所忽视的键。

在使用 print 或者 puts 方法时,Ruby 将这些由反引号引起来的字符串当成是系统可执行的
命令。你可能已经猜到 Ruby 提供了多种实现方法。%x/some command/和`some
command`,%x{some command}的效果是一样的。在 Windows 操作系统中,下面的三
行代码都会将 dir 命令传递给操作系统,然后打印出一个目录列表:

                                       4backquotes.rb



  puts(`dir`)

  puts(%x/dir/)

  puts(%x{dir})

你也可以在双引号字符串中内嵌命令行,如下:

  print(“Goodbye #{%x{calc}}”)

在你这么做的时候请一定要注意。命令行是最到被赋值的。你的 Ruby 程序将一直等待,直
到启动的进程中止后才会继续运行。在当前的这个例子中,计算器会弹出。此时你可以随意
地进行计算了。只有当你关闭计算器,程序才会将”Goodbye”字符串打印出来。


字符处理
在我们离开字符串这个主题之前,我们将快速地学习一些通用的字符串操作。


连接

你可以使用<<或者+,甚至就使用一个空格符来连接两个字符串。这里有三个字符串连接
的例子,在每一个例子中,s 都被赋值为”Hello world”:

  s = “Hello” << “world”

  s = “Hello” + “world”

  s = “Hello” “world”

注意,即使当你使用<<方法来连接字符串,你仍然可以在字符串后面追加一个有范围限制
的整数(0 到 255 之间)而不需要首先将其转化为字符串;使用+或者空格,这些整数必须使
用 to_s 方法。
那么逗号呢?
字符串赋值
   你可以已经多次看到在 Ruby 代码中使用逗号来隔字符串和其他数据类型。在一些情况下,

Ruby 中的 String 类提供了很多有用的字符串处理方法。这些方法中的绝大部分都创建了新
     这些逗号就是用来连接字符串的。例如,下面的代码中,一眼看去,像是创建并打印一个由
的字符串对象。所以,例如,在下面的代码中,第二行中赋值左边的 s 和右边的 s 并不是同
    三个子字符串和一个整型数组成的字符串:
一个对象。
    s4 = “This” , “is” , “not a string!” , 10
  s = "hello world"
    print(“print(s4):” , s4 ,”n”)
  s = s + "!"
    事实上,由逗号隔开的一个列表创建了一个数组——一个原始字符串有序列表。
                                  string_assign.rb
    string_concat.rb 程序包含了证明这一点的例子。
只有很少的方法是真正地修改字符串本身,而并没有创建新的对象。这些方法一般都以感叹
号结束(例如 capitalize!方法)。
   请注意当你传递一个数组给一个像 puts 这样的方法时,数组中的每一个元素都将单独处

如果还有疑问的话,你可以使用 object_id 方法来检查一个对象的标识。我在
   理。你可以传递数组 x ,给 puts 方法,如下:

string_assign.rb 程序中已经提供了一些操作,其中有一些创建了新的对象有的没有。运行
      puts(x)
该程序,然后在每一个字符串操作之后检查 s 的 object_id。
    这样打印的结果如下:

字符串内部索引
   This

   is
你可以将一个字符串当成一个字符数组,并且通过方括号[]使用索引来查找在指定索引值上
的字符。字符串和数组在 Ruby 中都是从 0 开始索引的。所以,例如,替换当前字符
   not a string!
串'Hello world'中的'e'为'a',你将给索引 1 处的字符赋值为'a':
    10
  s[1] = 'a'
    我们将在下一章深入讨论数组。
然而,如果你想索引字符串内某指定位置的字符,Ruby 并不是返回字符自身,而是返回它
的 ASCII 值:

  s = “Hello world”

  puts(s[1])     #prints out 101 – 'e'的 ASCII 码值

为了能获得真正的字符,你可以这么做:

  s = “Hello world”

  puts(s[1,1])   #prints out 'e'

这样将告诉 Ruby 素引字符串中索引为 1 的值,然后返回一个字符。如果你想返回从 1 开始
3 个字符,你可以这么做:

  puts(s[1,3])   #prints 'ell'
这样将告诉 Ruby 从位置 1 开始,返回之后的 3 个字符。另外一种方式是,你可以使用两点
区间符(..):

   puts(s[1..3])    #also print 'ell'

      如需了解更多关于范围符的知识,详见本章末尾的深入探讨一节。



字符串还能使用负值进行索引,-1 表示最后的字符位置,同样你可以指定你需要返回的字
符数:

   puts(s[-1,1])    #prints 'd'

   puts(s[-5,1])    #prints 'w'

   puts(s[-5,5])    #prints 'world'

                                                                string_index.rb



当使用负值进行索引时,如果使用区间符,你必须在区间的开始和结尾均使用负值:

   puts(s[-5..5])       #这个将打印一个空字符串

   puts(s[-5..-1]) #prints 'world'
                                                              string_methods.rb

最终,你可能想实验使用一些标准方法来操纵字符串。其中包含转换字符串大小写,插入子
字符串,删除重复字符等等方法。我在 string_methods.rb 中提供了一些例子。


删除换行符----CHOP 和 CHOMP
有两个很方便的处理方法值得特别注意。那就是 chop 和 chomp 方法可以用来删除字符串
末尾的字符。chop 方法将删除字符串中最后一个字符,如果字符串最后有换行符,则删除
换行符,然后返回删除后字符串。chomp 方法将删除回车符或换行符(如果同时存在两个,
就将两个都删除),然后返回修改后字符串。
  记录分隔符
这些方法在你需要从用户输入或文件读取的字符串中删除换行和回车符的时候特别有用。举
   Ruby 预定义了一个变量 $/ 作为一个记录分隔符。这个变量由 gets 和 chomp 方法使用。
例来说,当你使用 gets 方法来读取一个文本文件中一行,它返回一行包含记录分隔符的字
   gets 方法读入一个字符串,并包含记录分隔符。 chomp 方法返回一个删除了记录分隔符的
符串,默认为换行符。
   字符串 ( 如果存在的话 ) ,否则它将返回未修改的原始字符串。你可以重定义你想要的记录
   分隔符,如下:
       $/=”*” #”*” 字符现在为记录分隔符
   当你重定义记录分隔符的时候,该新字符 ( 或者字符串 ) 将会由 gets 和 chomp 方法使用。
   例如:
       $/=”world”
       S = gets()        #user enters “Had we but world enough and time...”
       puts(s)           #displays “Had we but world”
你可以使用 chop 或者 chomp 方法来删除换行符。在大多数情况下,chomp 是首选方法,
它不会删除字符串中最后一个字符,除非它是记录分隔符(换行符),而 chop 不论什么情况
都将删除最后的字符。这里有一些例子:

                                                 chop_chomp.rb

  #Note:s1 包含了一个回车和换行

  s1 = “Hello world”

  s2 = “Hello world”

  s1.chop     #returns “Hello world”

  s1.chomp    #returns   “Hello world”

  s2.chop     #returns “Hello worl” – 注意'd'不见了

  s2.chomp    #returns “Hello world”

chomp 方法可以让你指定一个字符或者字符串来作为分隔符:

  s2.chomp(“rld”) #returns “Hello wo”


格式化字符串
Ruby 提供了 printf 方法来打印一个包含由百分号%打头的分类符的格式化字符串。格式化
字符串后可以跟随多个由逗号隔开的数据项,数据项列表应该和格式化分类符的数目和类型
相匹配。实际的数据项替代字符串中的分类符,并且相应进行格式化编排。这些是一些常用
的格式化分类符:

  %d – decimal number
%f – floating point number

  %o – octal number

  %p – inspect object

  %s – string

  %x – hexadecimal number

你可以在%f 分类符中 f 前加上一个小数,通过小数中控制浮点数的精度。例如,下面的代
码将浮点数打印为精确到两位小数的形式:

  printf(“%0.02f”,10.12945)            #displays 10.13

  printf(“%0.000000002f”,10.12945) #displays 10.13
  printf(“%.2f”,10。12945)                 #displays 10.13

译者注:在 Ruby 中,使用格式化字符串中%f 分类符控制浮点数的精度,小数点前的数用
于控制整数部分位数,而小数点后数字用于控制小数点后位数。整数部分不够指定精度数,
在高位补空。小数部分,如果精度数小于浮点数原始精度四舍五入,若精度数高于原始精度,
低位补零。但是受限制于长度,如果你使用 printf(“%100.100f”,12.12345)打印出来的结
果会是这样的

12.12345000000000005968558980384841561317443847656250000000000000000
00000000000000000000000000000000000

而使用 printf("%100.1f",12.12345)打印出来的结果是这样的:

                                               12.1
在数字 12.1 前均为空字符。


深入探讨

区间
在 ruby 中,一个区间是一个类,用于描述一组由一个起点和终点值定义的值。典型的区间
是使用整型数进行定义的,但是它同样能使用其他有序的值来定义,例如浮点数或者字符。
值可以是负值,但是你得注意你的起点值是否小于终点值!

                                                            ranges.rb

这里有一些例子:

  a = (1..10)
b = (-10..-1)

  c = (-10..10)

  d = ('a'..'z')

你也可以使用三点代替两点来声明区间:这将会使得区间忽略最后一个值

  d = ('a'..'z')       #该区间由'a'到'z'

  e = ('a'...'z')      #该区间由'a'到'y'

你可以使用一个由区间定义的值通过 to_a 方法来创建一个数组,如下:

  (1..10).to_a

请注意,to_a 方法并没有针对浮点数进行重载,因为我们知道两个浮点数之间的区间是不
确定的无限的。
                                        str_range.rb

你甚至可以创建一个字符串的区间,但是你需要注意的是你这么做会导致区间比你预想的要
大很多很多。例如,你能想象出下面的声明中变量 str_range 的值是什么吗

  str_range = ('abc'..'def')

乍看,从'adc'到'def'的区间看上去一点都不大。但事实上,该语句定义了一个超过 2110 个
值的区间。它们是这么排列的:'abc','abd','abe'知道以'a'开头的组合的末尾,然后从'b'开
始,'baa','bab','bac'等等。不过可以肯定的说,这种使用需求极其少,在使用的时候一定
要万分小心,最好是不要用它。


区间的迭代
你可以使用区间来迭代,其范围为区间的起始值到终止值。比如,这里是一个用来打印从 1
到 10 之间所有整数的例子:

                                          for_to.rb

  for i in(1..10) do

      puts(i)

  end


文本声明符(HEREDOCS)
虽然你可以在单引号和双引号之间写入很长并跨行书写的字符串,但是很多的 Ruby 程序员
更倾向于使用一种名为”heredoc”的方式来替代这种书写方式。一个 heredoc 就是一个
以某个指定的终止标志开始的字符串,这个标志你可以简单地定义。这里,我指定 EODOC
为其终止标志:

                                              heredoc.rb

  hdoc1 = <<EODOC

该语句会告诉 Ruby,该语句行后面的是一个字符串,在出现终止符处中止。该字符串将被
赋值给变量 hdoc1。这里有一个完整的 heredoc 赋值的示例:

  hdoc1 = <<EODOC

  I wandered lonely as a #{"cloud".upcase},

  That floats on high o'er vale and hill...

  EODOC

默认情况下,heredocs 跟双引号字符串一般,像#{“cloud”.upcase}这样的表达式是会被
计算赋值的。如果你想一个 heredoc 像单引号字符串一般,你可以在指定终止符的时候使
用单引号将终止符括起来:

  hdoc2 = <<'EODOC'

  I wandered lonely as a #{"cloud".upcase},

  That floats on high o'er vale and hill...

  EODOC

一个 heredoc 的终止符在默认情况下必须左对齐。如果你想使用缩进的话,你需要在声明
终止符的时候使用<<-而不是<<:

  hdoc3 = <<-EODOC

  I wandered lonely as a #{"cloud".upcase},

  That floats on high o'er vale and hill...

  EODOC

这主要取决于是选择一个合适的终止符。甚至连使用一个保留关键字都是合法的(即使这是
非常的不明智的):

  hdoc4 = <<def

  I wandered lonely as a #{"cloud".upcase},

  That floats on high o'er vale and hill...
def

一个由 heredoc 赋值的变量能像其他字符串变量一般使用:

  puts(hdoc1)


字符串字面值
如本章前面介绍的,你可以有选择性的使用%q/和/来界定单引号字符串,使用%Q/和/或者
%/和/来界定双引号字符串。

Ruby 提供了类似的用于界定反引号字符串,正则表达式,符号和单引号或者双引号字符串
数组。这种能定义字符串数组的能力是非常有实际用处的,因为它使得我们没有必要在输入
每个字符串时都输入字符串界定符。这里有一些关于这些字符串字面界定符的参考:

  %q/ /

  %Q/ /

  %/ /

  %w/ /

  %W/ /

  %r| |

  %s/ /

  %x/ /

请注意你将会选择使用的那个界定符。除了在界定正则表达式时 ,我都使用/作为界定符(因
为/是标准的正则表达式界定符),但是我也同样使用方括号,星号,&号或者其他的符号(如:
%W*dog cat #{1+2}*或%s&dog&)。这里是一个关于这些字面值使用的例子:

                                                       literals.rb



  p %q/dog cat #{1+2}/ #=> "dog cat #{1+2}"

  p %Q/dog cat #{1+2}/ #=> "dog cat 3"

  p %/dog cat #{1+2}/ #=> "dog cat 3"

  p %w/dog cat #{1+2}/ #=> ["dog", "cat", "#{1+2}"]

  p %W/dog cat #{1+2}/ #=> ["dog", "cat", "3"]

  p %r|^[a-z]*$|   #=> /^[a-z]*$/
p %s/dog/   #=> :dog

p %x/vol/   #=> " Volume in drive C is OS [etc...]”

More Related Content

What's hot

Js的国(转载)
Js的国(转载)Js的国(转载)
Js的国(转载)Leo Hui
 
6, awk
6, awk6, awk
6, awkted-xu
 
系統程式 -- 第 8 章 編譯器
系統程式 -- 第 8 章 編譯器系統程式 -- 第 8 章 編譯器
系統程式 -- 第 8 章 編譯器鍾誠 陳鍾誠
 
第3章算法与控制语句
第3章算法与控制语句第3章算法与控制语句
第3章算法与控制语句summerfeng
 
系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言鍾誠 陳鍾誠
 
C++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesC++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesPeien Luo
 
数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点thinkinlamp
 
竞赛中C++语言拾遗
竞赛中C++语言拾遗竞赛中C++语言拾遗
竞赛中C++语言拾遗乐群 陈
 
系統程式 -- 第 4 章 組譯器
系統程式 -- 第 4 章 組譯器系統程式 -- 第 4 章 組譯器
系統程式 -- 第 4 章 組譯器鍾誠 陳鍾誠
 
Javascript Training
Javascript TrainingJavascript Training
Javascript Trainingbeijing.josh
 
系統程式 -- 第 12 章 系統軟體實作
系統程式 -- 第 12 章 系統軟體實作系統程式 -- 第 12 章 系統軟體實作
系統程式 -- 第 12 章 系統軟體實作鍾誠 陳鍾誠
 

What's hot (19)

Ch 8
Ch 8Ch 8
Ch 8
 
Js的国(转载)
Js的国(转载)Js的国(转载)
Js的国(转载)
 
6, awk
6, awk6, awk
6, awk
 
Ch10
Ch10Ch10
Ch10
 
系統程式 -- 第 8 章 編譯器
系統程式 -- 第 8 章 編譯器系統程式 -- 第 8 章 編譯器
系統程式 -- 第 8 章 編譯器
 
Work with Vim
Work with VimWork with Vim
Work with Vim
 
第3章算法与控制语句
第3章算法与控制语句第3章算法与控制语句
第3章算法与控制语句
 
Example
ExampleExample
Example
 
第6章指针
第6章指针第6章指针
第6章指针
 
系統程式 -- 第 3 章
系統程式 -- 第 3 章系統程式 -- 第 3 章
系統程式 -- 第 3 章
 
第四章
第四章第四章
第四章
 
系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言
 
C++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New FeaturesC++11综述/新特性描述/Overview of C++11 New Features
C++11综述/新特性描述/Overview of C++11 New Features
 
数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点
 
C語言列舉與聯合
C語言列舉與聯合C語言列舉與聯合
C語言列舉與聯合
 
竞赛中C++语言拾遗
竞赛中C++语言拾遗竞赛中C++语言拾遗
竞赛中C++语言拾遗
 
系統程式 -- 第 4 章 組譯器
系統程式 -- 第 4 章 組譯器系統程式 -- 第 4 章 組譯器
系統程式 -- 第 4 章 組譯器
 
Javascript Training
Javascript TrainingJavascript Training
Javascript Training
 
系統程式 -- 第 12 章 系統軟體實作
系統程式 -- 第 12 章 系統軟體實作系統程式 -- 第 12 章 系統軟體實作
系統程式 -- 第 12 章 系統軟體實作
 

Similar to 第三章

来自 Google 的 r 语言编码风格指南
来自 Google 的 r 语言编码风格指南来自 Google 的 r 语言编码风格指南
来自 Google 的 r 语言编码风格指南学峰 司
 
Learning notes ruby
Learning notes rubyLearning notes ruby
Learning notes rubyRoger Xia
 
Ruby 使用手冊 (Part 1)
Ruby 使用手冊 (Part 1)Ruby 使用手冊 (Part 1)
Ruby 使用手冊 (Part 1)Drake Huang
 
第9章 Shell 編程
第9章 Shell 編程第9章 Shell 編程
第9章 Shell 編程kidmany2001
 
Cypher 查询语言
Cypher 查询语言Cypher 查询语言
Cypher 查询语言zernel
 
Java易犯错误
Java易犯错误Java易犯错误
Java易犯错误yiditushe
 
Python learn guide
Python learn guidePython learn guide
Python learn guiderobin yang
 
Free Marker中文文档
Free Marker中文文档Free Marker中文文档
Free Marker中文文档yiditushe
 
Ecmascript基础
Ecmascript基础Ecmascript基础
Ecmascript基础enmaai
 
C 02 c语言的基本数据类型与表达式
C 02 c语言的基本数据类型与表达式C 02 c语言的基本数据类型与表达式
C 02 c语言的基本数据类型与表达式1138177709
 
3. java basics
3. java basics3. java basics
3. java basicsnetdbncku
 
正则表达式傻瓜式宝典
正则表达式傻瓜式宝典正则表达式傻瓜式宝典
正则表达式傻瓜式宝典yiditushe
 
JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能Joseph Kuo
 
一种基于拟合函数的图形识别算法
一种基于拟合函数的图形识别算法一种基于拟合函数的图形识别算法
一种基于拟合函数的图形识别算法Lixun Peng
 

Similar to 第三章 (20)

来自 Google 的 r 语言编码风格指南
来自 Google 的 r 语言编码风格指南来自 Google 的 r 语言编码风格指南
来自 Google 的 r 语言编码风格指南
 
Learning notes ruby
Learning notes rubyLearning notes ruby
Learning notes ruby
 
Ruby 使用手冊 (Part 1)
Ruby 使用手冊 (Part 1)Ruby 使用手冊 (Part 1)
Ruby 使用手冊 (Part 1)
 
第9章 Shell 編程
第9章 Shell 編程第9章 Shell 編程
第9章 Shell 編程
 
Cypher 查询语言
Cypher 查询语言Cypher 查询语言
Cypher 查询语言
 
Java易犯错误
Java易犯错误Java易犯错误
Java易犯错误
 
Python learn guide
Python learn guidePython learn guide
Python learn guide
 
Hi Haskell
Hi HaskellHi Haskell
Hi Haskell
 
SCJP ch03
SCJP ch03SCJP ch03
SCJP ch03
 
Free Marker中文文档
Free Marker中文文档Free Marker中文文档
Free Marker中文文档
 
第二章
第二章第二章
第二章
 
Ecmascript基础
Ecmascript基础Ecmascript基础
Ecmascript基础
 
C 02 c语言的基本数据类型与表达式
C 02 c语言的基本数据类型与表达式C 02 c语言的基本数据类型与表达式
C 02 c语言的基本数据类型与表达式
 
3. java basics
3. java basics3. java basics
3. java basics
 
Scala
ScalaScala
Scala
 
正则表达式傻瓜式宝典
正则表达式傻瓜式宝典正则表达式傻瓜式宝典
正则表达式傻瓜式宝典
 
Ruby basic
Ruby basicRuby basic
Ruby basic
 
Ch03
Ch03Ch03
Ch03
 
JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能
 
一种基于拟合函数的图形识别算法
一种基于拟合函数的图形识别算法一种基于拟合函数的图形识别算法
一种基于拟合函数的图形识别算法
 

第三章

  • 1. 第三章 字符串和区间 到目前为止,我已经在我的程序中大量使用了字符串。实际上,在这本书中最早的程序中就 使用到了字符串。就是这段代码: puts 'hello world' 在我的第一个程序中我使用单引号将字符串引起来,在我的第二个程序中我以另外一种方式, 那就是双引号将字符串引起来: print('Enter your name: ' ) name = gets() puts( "Hello #{name}" ) 1strings.rb 双引号能做的事情比单引号要多。特别是它们能在代码段中进行赋值,即使都是在引号内的 代码。为了能对变量赋值,你需要使用花括号将变量括起来并在前面加#字符。 在上面的例子里,#{name}在一个双引号字符串中,它将告诉 Ruby 取得 name 变量的值并 将它的值插入该字符串。如果 name 等于”Fred”,那么将打印出”Hello Fred”。 1Strings.rb 示范程序还提供了一些其他的关于双引号字符串内嵌赋值的示例。 一个双引号字符串不仅仅能给像 ob.name 这样的属性或者变量赋值,还能在双引号字符串 中添加表达式如 2*3 和一些代码段如 ob.ten(ten 是一个方法名),以及转义字符如 ”n”和”t”分别表示换行和制表符。 一个单引号字符串不能做这样的赋值。然而单引号字符串中可以使用反斜杠来指明下一个字 符按字面意义进行取值。当一个单引号字符串中包含了单引号的时候,这个特性还是有用的, 如下: 'It's my party' 假设 ten 方法返回值为 10,你可以像下面这样写代码: puts( "Here's a tabta new linena calculation #{2*3} and a method-call #{ob.ten}" ) 由于这是一个双引号字符串,内嵌元素都会进行赋值,打印结果如下: Here's a tab a new line
  • 2. a calculation 6 and a method-call 10 现在,让我们来看看当使用单引号字符串的时候会发生什么: puts( 'Here's a tabta new linena calculation #{2*3} and a method-call #{ob.ten}' ) 这次,没有内嵌赋值,它的打印如下: Here's a tabta new linena calculation #{2*3} and a method- call #{ob.ten} 用户自定义字符分隔符(界定符) 如果出于某种原因,单引号和双引号字符串都不是很方便你使用的话,例如,你的字符串包 含了他多的引号,然而你又不想在每个引号前加反斜杠,你也可以通过其他很多方法来分隔 你的字符串。 2strings.rb 标准的可代替双引号作为字符串界定符的是%Q 和/或者%/和/,而/q 和/可替代单引号。因 此可以这么写: %Q/This is the same as a double-quoted string./ %/This is also the same as a double-quoted string./ %q/And this is the same as a single-quoted string/ 你甚至可以定义你自己的字符界定符。当然其必须是非字母数字的字符,可以包含非打印字 符如换行符,或者其他很多通常在 Ruby 中有特殊意义的字符,如哈希符(#)。你选择的字符 应该跟随在%q 或者%Q 后面,同时你还得保证使用相同的字符来结束字符串。如果你的界 定符是一个开放的括号,那么你必须在字符串的末尾使用相对应的关闭括号进行界定,如下: %Q[This is a string] 3strings.rb 在 3strings.rb 程序中,你将发现大量的用户精选的字符串界定符的示例。无需多说,在很 多时候使用一些隐秘的字符,如换行符,星号等来进行字符串的界定是很有用的,但是在很 多情况下,实际使用中由此引发的的弊(不仅仅是精神上的痛苦和混乱)总是大于利的。 反引号 另一种字符值得引起特殊的重视,那就是由反引号引起来的字符串,这个引号键在我们键盘 的左手上方的角落里,是一个常被我们所忽视的键。 在使用 print 或者 puts 方法时,Ruby 将这些由反引号引起来的字符串当成是系统可执行的
  • 3. 命令。你可能已经猜到 Ruby 提供了多种实现方法。%x/some command/和`some command`,%x{some command}的效果是一样的。在 Windows 操作系统中,下面的三 行代码都会将 dir 命令传递给操作系统,然后打印出一个目录列表: 4backquotes.rb puts(`dir`) puts(%x/dir/) puts(%x{dir}) 你也可以在双引号字符串中内嵌命令行,如下: print(“Goodbye #{%x{calc}}”) 在你这么做的时候请一定要注意。命令行是最到被赋值的。你的 Ruby 程序将一直等待,直 到启动的进程中止后才会继续运行。在当前的这个例子中,计算器会弹出。此时你可以随意 地进行计算了。只有当你关闭计算器,程序才会将”Goodbye”字符串打印出来。 字符处理 在我们离开字符串这个主题之前,我们将快速地学习一些通用的字符串操作。 连接 你可以使用<<或者+,甚至就使用一个空格符来连接两个字符串。这里有三个字符串连接 的例子,在每一个例子中,s 都被赋值为”Hello world”: s = “Hello” << “world” s = “Hello” + “world” s = “Hello” “world” 注意,即使当你使用<<方法来连接字符串,你仍然可以在字符串后面追加一个有范围限制 的整数(0 到 255 之间)而不需要首先将其转化为字符串;使用+或者空格,这些整数必须使 用 to_s 方法。
  • 4. 那么逗号呢? 字符串赋值 你可以已经多次看到在 Ruby 代码中使用逗号来隔字符串和其他数据类型。在一些情况下, Ruby 中的 String 类提供了很多有用的字符串处理方法。这些方法中的绝大部分都创建了新 这些逗号就是用来连接字符串的。例如,下面的代码中,一眼看去,像是创建并打印一个由 的字符串对象。所以,例如,在下面的代码中,第二行中赋值左边的 s 和右边的 s 并不是同 三个子字符串和一个整型数组成的字符串: 一个对象。 s4 = “This” , “is” , “not a string!” , 10 s = "hello world" print(“print(s4):” , s4 ,”n”) s = s + "!" 事实上,由逗号隔开的一个列表创建了一个数组——一个原始字符串有序列表。 string_assign.rb string_concat.rb 程序包含了证明这一点的例子。 只有很少的方法是真正地修改字符串本身,而并没有创建新的对象。这些方法一般都以感叹 号结束(例如 capitalize!方法)。 请注意当你传递一个数组给一个像 puts 这样的方法时,数组中的每一个元素都将单独处 如果还有疑问的话,你可以使用 object_id 方法来检查一个对象的标识。我在 理。你可以传递数组 x ,给 puts 方法,如下: string_assign.rb 程序中已经提供了一些操作,其中有一些创建了新的对象有的没有。运行 puts(x) 该程序,然后在每一个字符串操作之后检查 s 的 object_id。 这样打印的结果如下: 字符串内部索引 This is 你可以将一个字符串当成一个字符数组,并且通过方括号[]使用索引来查找在指定索引值上 的字符。字符串和数组在 Ruby 中都是从 0 开始索引的。所以,例如,替换当前字符 not a string! 串'Hello world'中的'e'为'a',你将给索引 1 处的字符赋值为'a': 10 s[1] = 'a' 我们将在下一章深入讨论数组。 然而,如果你想索引字符串内某指定位置的字符,Ruby 并不是返回字符自身,而是返回它 的 ASCII 值: s = “Hello world” puts(s[1]) #prints out 101 – 'e'的 ASCII 码值 为了能获得真正的字符,你可以这么做: s = “Hello world” puts(s[1,1]) #prints out 'e' 这样将告诉 Ruby 素引字符串中索引为 1 的值,然后返回一个字符。如果你想返回从 1 开始 3 个字符,你可以这么做: puts(s[1,3]) #prints 'ell'
  • 5. 这样将告诉 Ruby 从位置 1 开始,返回之后的 3 个字符。另外一种方式是,你可以使用两点 区间符(..): puts(s[1..3]) #also print 'ell' 如需了解更多关于范围符的知识,详见本章末尾的深入探讨一节。 字符串还能使用负值进行索引,-1 表示最后的字符位置,同样你可以指定你需要返回的字 符数: puts(s[-1,1]) #prints 'd' puts(s[-5,1]) #prints 'w' puts(s[-5,5]) #prints 'world' string_index.rb 当使用负值进行索引时,如果使用区间符,你必须在区间的开始和结尾均使用负值: puts(s[-5..5]) #这个将打印一个空字符串 puts(s[-5..-1]) #prints 'world' string_methods.rb 最终,你可能想实验使用一些标准方法来操纵字符串。其中包含转换字符串大小写,插入子 字符串,删除重复字符等等方法。我在 string_methods.rb 中提供了一些例子。 删除换行符----CHOP 和 CHOMP 有两个很方便的处理方法值得特别注意。那就是 chop 和 chomp 方法可以用来删除字符串 末尾的字符。chop 方法将删除字符串中最后一个字符,如果字符串最后有换行符,则删除 换行符,然后返回删除后字符串。chomp 方法将删除回车符或换行符(如果同时存在两个, 就将两个都删除),然后返回修改后字符串。 记录分隔符 这些方法在你需要从用户输入或文件读取的字符串中删除换行和回车符的时候特别有用。举 Ruby 预定义了一个变量 $/ 作为一个记录分隔符。这个变量由 gets 和 chomp 方法使用。 例来说,当你使用 gets 方法来读取一个文本文件中一行,它返回一行包含记录分隔符的字 gets 方法读入一个字符串,并包含记录分隔符。 chomp 方法返回一个删除了记录分隔符的 符串,默认为换行符。 字符串 ( 如果存在的话 ) ,否则它将返回未修改的原始字符串。你可以重定义你想要的记录 分隔符,如下: $/=”*” #”*” 字符现在为记录分隔符 当你重定义记录分隔符的时候,该新字符 ( 或者字符串 ) 将会由 gets 和 chomp 方法使用。 例如: $/=”world” S = gets() #user enters “Had we but world enough and time...” puts(s) #displays “Had we but world”
  • 6. 你可以使用 chop 或者 chomp 方法来删除换行符。在大多数情况下,chomp 是首选方法, 它不会删除字符串中最后一个字符,除非它是记录分隔符(换行符),而 chop 不论什么情况 都将删除最后的字符。这里有一些例子: chop_chomp.rb #Note:s1 包含了一个回车和换行 s1 = “Hello world” s2 = “Hello world” s1.chop #returns “Hello world” s1.chomp #returns “Hello world” s2.chop #returns “Hello worl” – 注意'd'不见了 s2.chomp #returns “Hello world” chomp 方法可以让你指定一个字符或者字符串来作为分隔符: s2.chomp(“rld”) #returns “Hello wo” 格式化字符串 Ruby 提供了 printf 方法来打印一个包含由百分号%打头的分类符的格式化字符串。格式化 字符串后可以跟随多个由逗号隔开的数据项,数据项列表应该和格式化分类符的数目和类型 相匹配。实际的数据项替代字符串中的分类符,并且相应进行格式化编排。这些是一些常用 的格式化分类符: %d – decimal number
  • 7. %f – floating point number %o – octal number %p – inspect object %s – string %x – hexadecimal number 你可以在%f 分类符中 f 前加上一个小数,通过小数中控制浮点数的精度。例如,下面的代 码将浮点数打印为精确到两位小数的形式: printf(“%0.02f”,10.12945) #displays 10.13 printf(“%0.000000002f”,10.12945) #displays 10.13 printf(“%.2f”,10。12945) #displays 10.13 译者注:在 Ruby 中,使用格式化字符串中%f 分类符控制浮点数的精度,小数点前的数用 于控制整数部分位数,而小数点后数字用于控制小数点后位数。整数部分不够指定精度数, 在高位补空。小数部分,如果精度数小于浮点数原始精度四舍五入,若精度数高于原始精度, 低位补零。但是受限制于长度,如果你使用 printf(“%100.100f”,12.12345)打印出来的结 果会是这样的 12.12345000000000005968558980384841561317443847656250000000000000000 00000000000000000000000000000000000 而使用 printf("%100.1f",12.12345)打印出来的结果是这样的: 12.1 在数字 12.1 前均为空字符。 深入探讨 区间 在 ruby 中,一个区间是一个类,用于描述一组由一个起点和终点值定义的值。典型的区间 是使用整型数进行定义的,但是它同样能使用其他有序的值来定义,例如浮点数或者字符。 值可以是负值,但是你得注意你的起点值是否小于终点值! ranges.rb 这里有一些例子: a = (1..10)
  • 8. b = (-10..-1) c = (-10..10) d = ('a'..'z') 你也可以使用三点代替两点来声明区间:这将会使得区间忽略最后一个值 d = ('a'..'z') #该区间由'a'到'z' e = ('a'...'z') #该区间由'a'到'y' 你可以使用一个由区间定义的值通过 to_a 方法来创建一个数组,如下: (1..10).to_a 请注意,to_a 方法并没有针对浮点数进行重载,因为我们知道两个浮点数之间的区间是不 确定的无限的。 str_range.rb 你甚至可以创建一个字符串的区间,但是你需要注意的是你这么做会导致区间比你预想的要 大很多很多。例如,你能想象出下面的声明中变量 str_range 的值是什么吗 str_range = ('abc'..'def') 乍看,从'adc'到'def'的区间看上去一点都不大。但事实上,该语句定义了一个超过 2110 个 值的区间。它们是这么排列的:'abc','abd','abe'知道以'a'开头的组合的末尾,然后从'b'开 始,'baa','bab','bac'等等。不过可以肯定的说,这种使用需求极其少,在使用的时候一定 要万分小心,最好是不要用它。 区间的迭代 你可以使用区间来迭代,其范围为区间的起始值到终止值。比如,这里是一个用来打印从 1 到 10 之间所有整数的例子: for_to.rb for i in(1..10) do puts(i) end 文本声明符(HEREDOCS) 虽然你可以在单引号和双引号之间写入很长并跨行书写的字符串,但是很多的 Ruby 程序员 更倾向于使用一种名为”heredoc”的方式来替代这种书写方式。一个 heredoc 就是一个
  • 9. 以某个指定的终止标志开始的字符串,这个标志你可以简单地定义。这里,我指定 EODOC 为其终止标志: heredoc.rb hdoc1 = <<EODOC 该语句会告诉 Ruby,该语句行后面的是一个字符串,在出现终止符处中止。该字符串将被 赋值给变量 hdoc1。这里有一个完整的 heredoc 赋值的示例: hdoc1 = <<EODOC I wandered lonely as a #{"cloud".upcase}, That floats on high o'er vale and hill... EODOC 默认情况下,heredocs 跟双引号字符串一般,像#{“cloud”.upcase}这样的表达式是会被 计算赋值的。如果你想一个 heredoc 像单引号字符串一般,你可以在指定终止符的时候使 用单引号将终止符括起来: hdoc2 = <<'EODOC' I wandered lonely as a #{"cloud".upcase}, That floats on high o'er vale and hill... EODOC 一个 heredoc 的终止符在默认情况下必须左对齐。如果你想使用缩进的话,你需要在声明 终止符的时候使用<<-而不是<<: hdoc3 = <<-EODOC I wandered lonely as a #{"cloud".upcase}, That floats on high o'er vale and hill... EODOC 这主要取决于是选择一个合适的终止符。甚至连使用一个保留关键字都是合法的(即使这是 非常的不明智的): hdoc4 = <<def I wandered lonely as a #{"cloud".upcase}, That floats on high o'er vale and hill...
  • 10. def 一个由 heredoc 赋值的变量能像其他字符串变量一般使用: puts(hdoc1) 字符串字面值 如本章前面介绍的,你可以有选择性的使用%q/和/来界定单引号字符串,使用%Q/和/或者 %/和/来界定双引号字符串。 Ruby 提供了类似的用于界定反引号字符串,正则表达式,符号和单引号或者双引号字符串 数组。这种能定义字符串数组的能力是非常有实际用处的,因为它使得我们没有必要在输入 每个字符串时都输入字符串界定符。这里有一些关于这些字符串字面界定符的参考: %q/ / %Q/ / %/ / %w/ / %W/ / %r| | %s/ / %x/ / 请注意你将会选择使用的那个界定符。除了在界定正则表达式时 ,我都使用/作为界定符(因 为/是标准的正则表达式界定符),但是我也同样使用方括号,星号,&号或者其他的符号(如: %W*dog cat #{1+2}*或%s&dog&)。这里是一个关于这些字面值使用的例子: literals.rb p %q/dog cat #{1+2}/ #=> "dog cat #{1+2}" p %Q/dog cat #{1+2}/ #=> "dog cat 3" p %/dog cat #{1+2}/ #=> "dog cat 3" p %w/dog cat #{1+2}/ #=> ["dog", "cat", "#{1+2}"] p %W/dog cat #{1+2}/ #=> ["dog", "cat", "3"] p %r|^[a-z]*$| #=> /^[a-z]*$/
  • 11. p %s/dog/ #=> :dog p %x/vol/ #=> " Volume in drive C is OS [etc...]”