如何将String:'Hello world!'
转换为保留所有空格的数组['Hello', ' ', 'world!']
?
我尝试使用具有不同参数的split
方法转换字符串,但没有找到正确的解决方案。
此外,我在文档中没有找到任何其他适合解决这个问题的方法(Class:String(Ruby 3.1.0((。
我刚刚想到,你可以使用scan
。假设您的字符串存储在变量s
中,并且您希望将空间区域和非空间区域分开,则可以执行
s.scan(/[ ]+|[^ ]+/)
在你的情况下会产生
["Hello", " ", "world!"]
使用String#scan而不是String#split
您不想使用String#split,因为这不会保留您的空间。您希望使用String#扫描或String#分区。使用Unicode字符属性,您可以扫描匹配:
'Hello world!'.scan /[p{Alnum}p{Punct}]+|p{Space}+/
#=> ["Hello", " ", "world!"]
如果您愿意,也可以使用POSIX字符类(在Ruby中发音为"括号表达式"(来做同样的事情。例如:
'Hello world!'.scan /[[:alnum:][:punct:]]+|[[:space:]]+/
#=> ["Hello", " ", "world!"]
这两种选项中的任何一种都将比仅依赖ASCII字符或文字空白原子的解决方案更强大,但如果您知道字符串不包括其他类型的字符或编码,那么这些解决方案也会起作用。
慎用元符求简洁
如果您希望正则表达式简洁,并且确信不需要关心Unicode字符或明确区分非空白字符和标点符号,那么也可以使用s
和S
元字符。例如:
'Hello world!'.scan /s+|S+/
#=> ["Hello", " ", "world!"]
这通常不如上面的字符属性或括号表达式健壮,但仍然清晰、简短且易于阅读。它适合您的示例,因此值得一提,但S
元字符可以匹配控制字符和其他意外的东西,因此您需要谨慎使用它,除非您真正了解您的数据。例如,您的字符串可能包含一个不可见的NUL或像CTRL-D这样的控制字符,在这种情况下,S
会捕获它并返回一个Unicode转义字符:
"x00".scan /S+/
#=> ["u0000"]
?C-D.scan /S+/
#=> ["u0004"]
这可能不是你所期望的,但考虑到更大的数据集,这种类型的事情不可避免地会发生。你越明确,你的生产数据可能出现的问题就越少。
使用String#分区
对于原始示例中非常简单的用例,您只有两个用空格分隔的单词。这意味着您还可以使用String#分区对连续的空白进行分区。这将把字符串分成三个元素,保留分隔单词的空白。例如:
'Hello world!'.partition /s+/
#=> ["Hello", " ", "world!"]
分区方法虽然更简单,但对于较长的字符串(如:(则不能很好地工作
'Goodbye cruel world!'.partition /s+/
#=> ["Goodbye", " ", "cruel world!"]
因此String#scan对于一般用例来说将是一种更好、更灵活的方法。然而,无论何时,只要您想将字符串拆分为三个元素,或者想保留分区元素本身,#partition都会非常方便。
您可以继续使用split
,并通过使用带有capture group
:的简单regex
来保留空间
"Hello World ! ".split(/( +)/)
#=> ["Hello", " ", "World", " ", "!", " "]
我所知道的唯一一个问题是,以空格开头的字符串将导致以空字符串开头的数组:
" Hello World ! ".split(/( +)/)
#=> ["", " ", "Hello", " ", "World", " ", "!", " "]
如果这是一个问题,您可以在混合物中添加类似drop_while
的东西:
" Hello World ! ".split(/( +)/).drop_while(&:empty?)
#=> [" ", "Hello", " ", "World", " ", "!", " "]