问题
str[i].upcase!
将 Array 值中的单个字符变为大写字符。但是,至少在 Ruby 2.7.1 上,它实际上不会更改原始 String 对象的内容,直到您将元素重新分配回要修改的 String 索引。例如:
str[i] = str[i].upcase
但是,上述方法不适用于冻结字符串,这在某些核心方法、库和框架中相当常见。因此,您可能会遇到使用索引分配方法的 FrozenError 异常。
解决方案
解决这个问题的方法不止一种,但一种方法是:
- 将您的 String 对象拆分为一个字符数组,
- 修改所需索引处的字母,
- 将字符重新连接成一个字符串,然后
- 将修改后的字符串重新分配给您的原始变量。
例如,显示一些中间步骤:
# convert String to Array of characters
str = "foobar"
chars = str.chars
# contents of your chars Array
chars
#=> ["f", "o", "o", "b", "a", "r"]
# - convert char in place at given index in Array
# - don't rely on the return value of the bang method
# to be a letter
# - safe navigation handles various nil-related errors
chars[3]&.upcase!
#=> "B"
# re-join Array of chars into String
chars.join
#=> "fooBar"
# re-assign to original variable
str = chars.join
str
#=> "fooBar"
如果需要,您可以在重新加入元素之前对chars数组的多个索引执行相同的操作。这应该会产生您正在寻找的结果。
更简洁:
str = "foobar"
chars = str.chars
chars[3]&.upcase!
p str = chars.join
#=> "fooBar"
就个人而言,我发现对字符数组进行操作比通过重复分配原始字符串中的索引来进行就地更改更直观且更容易排除故障。此外,它避免了在尝试修改冻结字符串中的索引时引发的异常。但是,您的设计选择可能会有所不同。