如何在Ruby中将字符串的第一个字母大写

169

upcase 方法可以使整个字符串变成大写,但我只需要将第一个字母大写。

另外,我需要支持德语和俄语等流行语言。

我该如何实现呢?


4
请注意,一些语言对首字母大写的要求有所不同。例如,在爱尔兰语中,你需要这样书写“i mBaile Átha Cliath”(意为“在都柏林”)——小写的“m”,大写的“B”。如果你想知道为什么爱尔兰语会这样做,以及这背后的逻辑,请参考 http://en.wikipedia.org/wiki/Consonant_mutation#Celtic_languages 。 - James Moore
4
请注意,#capitalize方法会将除首字母外的所有字母变为小写,这可能并不是你想要的效果。例如:['space', 'UFO', 'NASA'].collect{|w| w.capitalize} #=> ['Space', 'Ufo', 'Nasa'] - Huliax
10个回答

295

这取决于你使用的 Ruby 版本:

Ruby 2.4 及更高版本:

它可以正常工作,因为自从Ruby v2.4.0开始支持 Unicode 大小写映射。

"мария".capitalize #=> Мария

Ruby 2.3 及以下版本:

"maria".capitalize #=> "Maria"
"мария".capitalize #=> мария

问题是,它并没有按照你想要的那样输出Мария,而是输出了мария

如果你正在使用Rails,有一个简单的解决方法:

"мария".mb_chars.capitalize.to_s # requires ActiveSupport::Multibyte
否则,你将需要安装unicode gem,并像这样使用它:
require 'unicode'

Unicode::capitalize("мария") #=> Мария

Ruby 1.8:

请务必使用编码魔术注释:

#!/usr/bin/env ruby

puts "мария".capitalize

当使用非ASCII字符时,会出现invalid multibyte char (US-ASCII)的错误:

#!/usr/bin/env ruby
#coding: utf-8

puts "мария".capitalize

代码可以正常运行,但是请查看“Ruby 2.3及以下版本”部分来进行真正的大写处理。


38
请注意,显然"my API is great".capitalize会产生My api is great这样的结果,这可能是不期望的行为。因此,这个答案并没有真正回答问题,因为他只想将第一个字母转换为大写,而保持其他字母不变。 - Daniel A. R. Werner

74

将字符串的第一个单词的首字母大写

"kirk douglas".capitalize
#=> "Kirk douglas"

将每个单词的首字母大写

在Rails中:

"kirk douglas".titleize
=> "Kirk Douglas"

或者

"kirk_douglas".titleize
=> "Kirk Douglas"    

在Ruby中:

"kirk douglas".split(/ |\_|\-/).map(&:capitalize).join(" ") 
#=> "Kirk Douglas"

或者

require 'active_support/core_ext'
"kirk douglas".titleize

1
注意 "kirk-douglas".titleize 方法的副作用,会将其转换为 "Kirk Douglas" 并去除连字符。考虑使用 Rails 5+ 中提到的 upcase_first 方法,以保留连字符。 - folium

24

Rails 5+

从 Active Support 和 Rails 5.0.0.beta4 开始,您可以使用以下两种方法之一:String#upcase_firstActiveSupport::Inflector#upcase_first

"my API is great".upcase_first #=> "My API is great"
"мария".upcase_first           #=> "Мария"
"мария".upcase_first           #=> "Мария"
"NASA".upcase_first            #=> "NASA"
"MHz".upcase_first             #=> "MHz"
"sputnik".upcase_first         #=> "Sputnik"

查看 "Rails 5: New upcase_first 方法" 获取更多信息。


21

好的,我们只需要知道如何仅大写第一个字母并保留其余部分不变,因为有时这是所需的:

['NASA', 'MHz', 'sputnik'].collect do |word|
  letters = word.split('')
  letters.first.upcase!
  letters.join
end

 => ["NASA", "MHz", "Sputnik"]

调用 capitalize 方法会得到 ["Nasa", "Mhz", "Sputnik"]


谢谢,这正是我需要的。它对于将标题转换为“句子大小写”非常有用。 - Good Lux
3
word[0] = word[0].upcase - David
@David。不!那会改变在#collect上调用的数组中单词的值。这是一个不好的副作用。 - Huliax
我正在展示一种更简单的方法来大写单词的首字母,替换了这个解决方案内部的3行代码,我通过使用“word”变量来清楚地表明了这一点。当然,如果你有更多的单词,只需在所有单词上调用它们!;) words.map{|word| word[0] = word[0].upcase} - David
@David。你的代码相当于#capitalize!而不是#capitalize。后者返回一个新的字符串,而前者修改了方法的接收器(在这种情况下,接收器是word,方法是#[])。如果你在#collect块中使用你的代码,那么你最终会得到两个不同的数组,每个数组中都有相同的字符串对象(并且字符串已经被修改)。这不是你通常想要做的事情。即使你知道这一点,其他读者也应该理解这一点。 - Huliax

20

遗憾的是,机器无法正确地将单词变成大写/小写/首字母大写。计算机需要太多上下文信息才能理解。

这就是为什么 Ruby 的 String 类只支持 ASCII 字符的大小写转换,因为至少在那里它有一定程度的定义。

我所说的 "上下文信息" 是什么意思呢?

例如,要正确地将字母 i 变成大写形式,你需要知道文本所使用的语言。比如英语中只有两个字母 i:没有点的大写 I 和有点的小写 i。但土耳其语中则有四个字母 i:没有点的大写 I、有点的大写 İ、没有点的小写 ı、有点的小写 i。因此,在英语中 'i'.upcase # => 'I',而在土耳其语中 'i'.upcase # => 'İ'。换句话说,由于 'i'.upcase 可以返回两个不同的结果,取决于语言,所以在不知道单词所用语言的情况下,正确地将其首字母大写是不可能的。

而 Ruby 并不知道语言,它只知道编码。因此,使用 Ruby 内置的功能正确地将字符串首字母大写是不可能的。

更糟糕的是,即使知道了语言,有时也无法正确地进行大小写转换。例如,在德语中,'Maße'.upcase # => 'MASSE'('Maße' 是 'Maß'(意为“测量”)的复数形式)。然而,'Masse'.upcase # => 'MASSE'(意为“质量”)。那么 'MASSE'.capitalize 是什么呢?换句话说:正确的首字母大写需要一种完全发展的人工智能。

因此,Ruby选择有时不提供任何答案,而不是有时给出错误答案,这就是为什么在downcase / upcase / capitalize操作中简单地忽略非ASCII字符的原因。 (当然,这也会导致错误的结果,但至少很容易检查。)

4
抱歉,但是你的论点站不住脚。Ruby不选择不给出答案,Ruby总是会给出答案,只是经常是错误的 - 例如,“мария”.upcase 永远不应该返回“мария”,这在任何情况下都是不正确的。关于需要AI的你离题了 - 没有任何阻止 upcase 返回一个数组,比如 ['I','İ'] 对于 'i'.upcase,并让调用者决定在特定情况下哪种大小写是相关的。目前Ruby在大写和小写之间的转换处理有问题,就是这样。 - michau
2
返回翻译后的文本:-1是因为有一个大写的Eszett。使用一些非完全形式化的领域不能作为仅依靠AI解决问题的证明。 - Mike

6
使用capitalize方法。从String文档中可以得知:

返回一个字符串的副本,其中第一个字符转换为大写,其余字符转换为小写。

"hello".capitalize    #=> "Hello"
"HELLO".capitalize    #=> "Hello"
"123ABC".capitalize   #=> "123abc"

只有在您希望更改原始字符串时才使用感叹号。 - Magnar
5
OP明确提到了德语和俄语文本,这意味着存在非ASCII字符。String#upcase(以及String#downcase)仅适用于ASCII字符。 - Jörg W Mittag
2
今天使用 Ruby 2.5.0,String#upcase 在非 ASCII 字符上似乎可以正常工作。 2.5.0 :001 > "мария".upcase => "МАРИЯ" - Huliax
1
@Huliax 如已接受的答案所述,这只是自 Ruby 2.4.0(于2016年发布)以来的情况。 - nisetama

3
您可以使用mb_chars。这个方法会考虑到特殊字符,例如德语中的umlaut(变音符号):
class String

  # Only capitalize first letter of a string
  def capitalize_first
    self[0] = self[0].mb_chars.upcase
    self
  end

end

例子:

"ümlaute".capitalize_first
#=> "Ümlaute"

3

2
以下是另一种将字符串中每个单词首字母大写的方法。 \w 无法匹配 Cyrillic 字符或带有变音符号的拉丁字符,但 [[:word:]] 可以。在 Ruby 2.4.0 发布于2016年之前,upcasedowncasecapitalizeswapcase 不适用于非 ASCII 字符。"最初的回答"
"aAa-BBB ä мария _a a_a".gsub(/\w+/,&:capitalize)
=> "Aaa-Bbb ä мария _a A_a"
"aAa-BBB ä мария _a a_a".gsub(/[[:word:]]+/,&:capitalize)
=> "Aaa-Bbb Ä Мария _a A_a"

"最初的回答":

[[:word:]] 可以匹配以下类别中的字符:

Ll (Letter, Lowercase)
Lu (Letter, Uppercase)
Lt (Letter, Titlecase)
Lo (Letter, Other)
Lm (Letter, Modifier)
Nd (Number, Decimal Digit)
Pc (Punctuation, Connector)

[[:word:]] 可以匹配 "标点符号, 连接符" (Pc) 类别中的所有10个字符:

"最初的回答" 翻译成英文是 "Original Answer"。

005F _ LOW LINE
203F ‿ UNDERTIE
2040 ⁀ CHARACTER TIE
2054 ⁔ INVERTED UNDERTIE
FE33 ︳ PRESENTATION FORM FOR VERTICAL LOW LINE
FE34 ︴ PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
FE4D ﹍ DASHED LOW LINE
FE4E ﹎ CENTRELINE LOW LINE
FE4F ﹏ WAVY LOW LINE
FF3F _ FULLWIDTH LOW LINE

这是将字符串的第一个字符转换为大写字母的另一种方法:

这是将字符串的第一个字符转换为大写字母的另一种方法:

"striNG".sub(/./,&:upcase)
=> "StriNG"

0
如果你想在 Ruby 中仅将字符串的第一个字符大写,就像作者所请求的那样,你应该只需按照这样做:
word[0] = word[0].upcase

这适用于所有语言,只涉及整个字符串的第一个字母。

word = "мария"
# => "мария"
word[0] = word[0].upcase
# => "М"
word
# => "Мария"

它不需要Rails或任何其他库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接