【问题标题】:Converting string to proper title case将字符串转换为正确的标题大小写
【发布时间】:2015-02-02 19:39:19
【问题描述】:

我有这个练习:

编写一个用字符串初始化的Title 类。

它有一个方法——fix——它应该返回字符串的标题大小写版本:

Title.new("a title of a book").fix = 书名
您需要使用条件逻辑 - ifelse 语句 - 来完成这项工作。
请务必仔细阅读测试规范,以便了解要实现的条件逻辑。

您将要使用的一些方法:

String#downcase String#capitalize Array#include?

另外,这是 Rspec,我应该包括:

describe "Title" do
describe "fix" do
it "capitalizes the first letter of each word" do
  expect( Title.new("the great gatsby").fix ).to eq("The Great Gatsby")
end
it "works for words with mixed cases" do
  expect( Title.new("liTTle reD Riding hOOD").fix ).to eq("Little Red Riding Hood")
end
it "downcases articles" do
  expect( Title.new("The lord of the rings").fix ).to eq("The Lord of the Rings")
  expect( Title.new("The sword And The stone").fix ).to eq("The Sword and the Stone")
  expect( Title.new("the portrait of a lady").fix ).to eq("The Portrait of a Lady")
end
it "works for strings with all uppercase characters" do
  expect( Title.new("THE SWORD AND THE STONE").fix ).to eq("The Sword and the Stone")
end
end
end

谢谢@simone,我采纳了你的建议:

class Title
attr_accessor :string

def initialize(string)
@string = string
end

IGNORE = %w(the of a and)

def fix
s = string.split(' ')
s.map do |word|
  words = word.downcase
  if IGNORE.include?(word)
    words
  else
    words.capitalize
  end
end
s.join(' ')
end
end

虽然我在运行代码时仍然遇到错误:

expected: "The Great Gatsby"
 got: "the great gatsby"

(compared using ==)

exercise_spec.rb:6:in `block (3 levels) in <top (required)>' 

从我初学者的角度来看,我看不出我做错了什么?

最后的编辑:我只是想感谢大家早先为我提供的帮助所付出的所有努力。我将展示我能够生成的最终工作代码:

class Title
attr_accessor :string

def initialize(string)
@string = string
end

def fix
word_list = %w{a of and the}

a = string.downcase.split(' ')
b = []

a.each_with_index do |word, index|
  if index == 0 || !word_list.include?(word)
    b << word.capitalize
  else
    b << word
  end
end
b.join(' ')
end
end

【问题讨论】:

  • 测试规范是什么?
  • 我不喜欢这种“再次发明圈子”的任务。这就是为什么我们有apidock.com/rails/String/titleize
  • 您确定include? 是您想要的吗?例如,Theodore 应该是大写还是小写?现在你说的是小写。
  • 除了titleize不是他所追求的
  • 不要用你的答案代替问题,这会使问题变得无用

标签: ruby string title-case


【解决方案1】:

这是一个可能的解决方案。

class Title
  attr_accessor :string

  IGNORES = %w( the of a and )

  def initialize(string)
    @string = string
  end

  def fix
    tokens = string.split(' ')
    tokens.map do |token|
      token = token.downcase

      if IGNORES.include?(token)
        token
      else
        token.capitalize
      end
    end.join(" ")
  end

end

Title.new("a title of a book").fix

你的出发点很好。以下是一些改进:

  • 比较总是小写。这将简化 if 条件
  • 被忽略的项目列表被放入一个数组中。这将简化 if 条件,因为您不需要为每个忽略的字符串添加 if(它们可能是数百个)
  • 我使用地图来替换标记。使用带有枚举的块来循环项目是一种常见的 Ruby 模式

【讨论】:

  • 此解决方案无法始终将标题的第一个单词大写。
  • 对于@Ajedi32 的评论,我建议first_word, remaining_words = string.splitfirst_word 分开处理。您也可以使用索引作为单词的偏移量,但我认为我的建议更简单明了。
  • 感谢@Ajedi32 的留言。如果是这种情况,这很容易解决,正如 Cary Swoveland 所建议的那样。我将把修复它的练习留给用户。
  • 我忘记了 splat:*remaining_words.
【解决方案2】:

有两种方法可以解决这个问题:

  • 将字符串分解为单词,可能修改每个单词并将单词重新组合在一起;或
  • 使用正则表达式。

我会谈谈后者,但我相信你的练习涉及到前者——这是你所采取的方法——所以我会集中精力。

将字符串拆分为单词

您使用String#split(' ') 将字符串拆分为单词:

str = "a title of a\t   book"
a = str.split(' ')
  #=> ["a", "title", "of", "a", "book"] 

这很好,即使有额外的空格,但通常会这样写:

str.split
  #=> ["a", "title", "of", "a", "book"] 

两种方式都一样

str.split(/\s+/)
  #=> ["a", "title", "of", "a", "book"] 

请注意,我使用变量a 来表示返回了一个数组。有些人可能觉得描述性不够,但我相信它比s 更好,这有点令人困惑。 :-)

创建枚举器

接下来你发送Enumerable#each_with_index方法来创建一个枚举器:

enum0 = a.each_with_index
  # => #<Enumerator: ["a", "title", "of", "a", "book"]:each_with_index> 

要查看枚举器的内容,请将enum0 转换为数组:

enum0.to_a
  #=> [["a", 0], ["title", 1], ["of", 2], ["a", 3], ["book", 4]] 

您使用each_with_index 是因为第一个词——索引为0 的词——将与其他词区别对待。没关系。

到目前为止,一切都很好,但是此时您需要使用Enumerable#mapenum0 的每个元素转换为适当的值。例如,第一个值["a", 0]要转换为“A”,下一个要转换为“Title”,第三个要转换为“of”。

因此,需要将方法Enumerable#map发送给enum0

enum1 = enum.map
  #=> #<Enumerator: #<Enumerator: ["a", "title", "of", "a",
        "book"]:each_with_index>:map> 
enum1.to_a
  #=> [["a", 0], ["title", 1], ["of", 2], ["a", 3], ["book", 4]] 

如您所见,这会创建一个新的枚举器,可以将其视为“复合”枚举器。

enum1 的元素将通过Array#each 传递到块中。

调用枚举器并加入

您希望将第一个单词和除以文章开头的单词之外的所有其他单词大写。因此,我们必须定义一些文章:

articles = %w{a of it} # and more
  #=> ["a", "of", "it"]

b = enum1.each do |w,i|
  case i
  when 0 then w.capitalize
  else articles.include?(w) ? w.downcase : w.capitalize
  end
end
  #=> ["A", "Title", "of", "a", "Book"] 

最后我们加入数组,每个单词之间有一个空格:

b.join(' ')
  => "A Title of a Book" 

查看计算详情

让我们回到b的计算。 enum1 的第一个元素被传递到块中并分配给块变量:

w, i = ["a", 0] #=> ["a", 0] 
w               #=> "a" 
i               #=> 0 

所以我们执行:

case 0
when 0 then "a".capitalize
else articles.include?("a") ? "a".downcase : "a".capitalize
end

返回"a".capitalize =&gt; "A"。同样,当enum1 的下一个元素传递给块时:

w, i = ["title", 1] #=> ["title", 1] 
w               #=> "title" 
i               #=> 1 

case 1
when 0 then "title".capitalize
else articles.include?("title") ? "title".downcase : "title".capitalize
end

articles.include?("title") =&gt; false 起返回“标题”。下一个:

w, i = ["of", 2] #=> ["of", 2] 
w               #=> "of" 
i               #=> 2 

case 2
when 0 then "of".capitalize
else articles.include?("of") ? "of".downcase : "of".capitalize
end

articles.include?("of") =&gt; true开始返回“of”。

链式操作

综合起来,我们有:

str.split.each_with_index.map do |w,i|
  case i
  when 0 then w.capitalize
  else articles.include?(w) ? w.downcase : w.capitalize
  end
end
  #=> ["A", "Title", "of", "a", "Book"] 

替代计算

不使用each_with_index 的另一种方法是这样的:

first_word, *remaining_words = str.split
first_word
  #=> "a" 
remaining_words
  #=> ["title", "of", "a", "book"] 

"#{first_word.capitalize} #{ remaining_words.map { |w|
  articles.include?(w) ? w.downcase : w.capitalize }.join(' ') }"
   #=> "A Title of a Book" 

使用正则表达式

str = "a title of a book"

str.gsub(/(^\w+)|(\w+)/) do
  $1 ? $1.capitalize :
    articles.include?($2) ? $2 : $2.capitalize
end
  #=> "A Title of a Book" 

正则表达式“捕获”[(...)] 字符串 [(^\w+)] 开头的单词或 [|] 不一定在字符串 [(\w+)] 开头的单词。两个捕获组的内容分别赋值给全局变量$1$2

因此,单步执行字符串的单词,第一个单词"a" 被捕获组#1 捕获,因此不会评估(\w+)。每个后续单词都不会被捕获组#1 捕获(所以$1 =&gt; nil),而是被捕获组#2 捕获。因此,如果$1 不是nil,我们将(句子的)(第一个)单词大写;否则,如果该词不是一篇文章,我们将大写$2,如果它是一篇文章,则保持不变。

【讨论】:

  • 你帮不上忙。对于像我这样的初学者来说,这真是一个彻底的解释!
【解决方案3】:
def fix
   string.downcase.split(/(\s)/).map.with_index{ |x,i| 
     ( i==0 || x.match(/^(?:a|is|of|the|and)$/).nil? ) ? x.capitalize : x 
   }.join
end

满足所有条件:

  1. aisofthe全部小写
  2. 将所有其他单词大写
  3. 所有第一个单词都大写

说明

  1. string.downcase 调用一个操作来使您正在使用的字符串全部小写
  2. .split(/(\s)/) 采用小写字符串并将其在空格(空格、制表符、换行符等)上拆分为一个数组,使每个单词成为数组的元素;括在括号中的\s(分隔符)也将其保留在返回的数组中,因此我们在重新加入时不会丢失那个空白字符
  3. .map.with_index{ |x,i| 迭代返回的数组,其中x 是值,i 是索引号;每次迭代都返回一个新数组的元素;循环完成后,您将拥有一个新数组
  4. ( i==0 || x.match(/^(?:a|is|of|the|and)$/).nil? ) 如果它是数组中的第一个元素(索引为 0),或者单词匹配 a,is,of,theand——即匹配不是nil -- 然后是x.capitalize(大写单词),否则(它确实匹配忽略的单词)所以只返回单词/值,x
  5. .join 取出我们的新数组并将所有单词再次组合成一个字符串

附加

  • 通常,正则表达式中括号内的内容被视为捕获组,这意味着如果匹配其中的模式,则特殊变量将在正则表达式操作完成后保留该值。在某些情况下,例如\s,我们想要捕获该值,因为我们重用它,在其他情况下,例如我们忽略的单词,我们需要匹配,但不需要捕获它们。为避免捕获匹配项,您可以在捕获组的开头调整?:,以告诉正则表达式引擎不要保留该值。这样做的许多好处超出了此答案的范围。

【讨论】:

  • @ptd 希望您也对另一个答案投反对票,因为这也“无法回答问题”;)问题也已更改-没有问题。我建议不要投反对票,因为这是一个看起来不正确的合法答案。
  • 考虑到 OP 说,“从我初学者的角度来看......”,你不认为你需要在你的答案中提供一些解释吗?
  • 在标题情况下,动词(包括 is、was、be)总是大写。
  • @Ubik 可能是,我没有查规则,他也没有给出规范。答案不是一成不变的,他可以根据自己的需要进行更改,但我会尽可能更新,或者您也可以;)
【解决方案4】:

这是解决问题的另一种可能的方法

class Title
  attr_accessor :str
  def initialize(str)
   @str = str
  end

  def fix
    s = str.downcase.split(" ") #convert all the strings to downcase and it will be stored in an array
    words_cap = []
    ignore = %w( of a and the ) # List of words to be ignored
    s.each do |item|
      if ignore.include?(item) # check whether word in an array is one of the words in ignore list.If it is yes, don't capitalize. 
        words_cap << item

      else
        words_cap << item.capitalize
      end  
    end
    sentence = words_cap.join(" ") # convert an array of strings to sentence
    new_sentence =sentence.slice(0,1).capitalize + sentence.slice(1..-1) #Capitalize first word of the sentence. Incase it is not capitalized while checking the ignore list.
  end


end    

【讨论】:

    猜你喜欢
    • 2010-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    相关资源
    最近更新 更多