如何编写消除数字和冒号之间的 space 的正则表达式?
How do I write a regex that eliminates the space between a number and a colon?
我想替换一个或两个数字之间的 space 和后跟 space、数字或行尾的冒号。如果我有一个字符串,
line = " 0 : 28 : 37.02"
结果应该是:
" 0: 28: 37.02"
我试过如下:
line.gsub!(/(\A|[ \u00A0|\r|\n|\v|\f])(\d?\d)[ \u00A0|\r|\n|\v|\f]:(\d|[ \u00A0|\r|\n|\v|\f]|\z)/, ':')
# => " 0: 28 : 37.02"
好像匹配第一个":"
,但是第二个":"
不匹配。我不明白为什么。
问题
我将用注释定义您的正则表达式(在自由间距模式下)以显示它在做什么。
r =
/
( # begin capture group 1
\A # match beginning of string (or does it?)
| # or
[ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f"
) # end capture group 1
(\d?\d) # match one or two digits in capture group 2
[ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f"
: # match ":"
( # begin capture group 3
\d # match a digit
| # or
[ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f"
| # or
\z # match the end of the string
) # end capture group 3
/x # free-spacing regex definition mode
请注意 '|'
不是字符 class 中的特殊字符 ("or")。它被视为普通字符。 (即使 '|'
在字符 class 中被视为 "or",那也没有用,因为字符 classes 用于强制匹配其中的任何一个字符.)
假设
line = " 0 : 28 : 37.02"
然后
line.gsub(r, ':')
#=> " 0: 28 : 37.02"
#=> " "
#=> "0"
#=> " "
在捕获组 1 中,行的开头 (\A
) 不匹配,因为它不是字符,只有字符不匹配(虽然我不知道为什么不引发异常). "or" ('|'
) 的特殊字符导致正则表达式引擎尝试匹配字符串 " \u00A0|\r\n\v\f"
的一个字符。因此它将匹配字符串 line
.
开头的三个 space 之一
下一个捕获组 2 捕获 "0"
。为此,捕获组 1 必须捕获 line
的索引 2 处的 space。然后再匹配一个 space 和一个冒号,最后,捕获组 3 将冒号后面的 space
子串' 0 : '
因此被替换为':' #=> '0: '
,所以gsub
returns" 0: 28 : 37.02"
。请注意 '0'
之前的一个 space 被删除了(但应该保留)。
一个解决方案
以下是如何删除一个或多个 Unicode 白色字符中的最后一个 space 字符,这些字符前面有一个或两个数字(但不能更多),并且在字符串末尾跟一个冒号或冒号后跟白色 space 或数字。 (哇!)
def trim(str)
str.gsub(/\d+[[:space:]]+:(?![^[:space:]\d])/) do |s|
s[/\d+/].size > 2 ? s : s[0,s.size-2] << ':'
end
end
正则表达式为 "match one or more digits followed by one or more whitespace characters, followed by a colon (all these characters are matched), not followed (negative lookahead) by a character other than a unicode whitespace or digit"。如果匹配,我们检查开头有多少位数字。如果超过两个则返回匹配项(无变化),否则从匹配项中删除冒号前的白色 space 字符并返回修改后的匹配项。
trim " 0 : 28 : 37.02"
#=> " 0: 28: 37.02" xxx
trim " 0\v: 28 :37.02"
#=> " 0: 28:37.02"
trim " 0\u00A0: 28\n:37.02"
#=> " 0: 28:37.02"
trim " 123 : 28 : 37.02"
#=> " 123 : 28: 37.02"
trim " A12 : 28 :37.02"
#=> " A12: 28:37.02"
trim " 0 : 28 :"
#=> " 0: 28:"
trim " 0 : 28 :A"
#=> " 0: 28 :A"
如果像示例中那样,字符串中的唯一字符是数字、白色space和冒号,则不需要回顾。
您可以使用 Ruby 的 \p{}
构造 \p{Space}
代替 POSIX 表达式 [[:space:]]
。两者都匹配 class 个 Unicode 白色 space 字符,包括示例中显示的字符。
" 0 : 28 : 37.02".gsub!(/(\d)(\s)(:)/,'')
=> " 0: 28: 37.02"
排除第三个数字可以用负回溯来完成,但由于其他一两个数字的长度可变,你不能对该部分使用正回溯。
line.gsub(/(?<!\d)(\d{1,2}) (?=:[ \d$])/, '')
# => " 0: 28: 37.02"
我想替换一个或两个数字之间的 space 和后跟 space、数字或行尾的冒号。如果我有一个字符串,
line = " 0 : 28 : 37.02"
结果应该是:
" 0: 28: 37.02"
我试过如下:
line.gsub!(/(\A|[ \u00A0|\r|\n|\v|\f])(\d?\d)[ \u00A0|\r|\n|\v|\f]:(\d|[ \u00A0|\r|\n|\v|\f]|\z)/, ':')
# => " 0: 28 : 37.02"
好像匹配第一个":"
,但是第二个":"
不匹配。我不明白为什么。
问题
我将用注释定义您的正则表达式(在自由间距模式下)以显示它在做什么。
r =
/
( # begin capture group 1
\A # match beginning of string (or does it?)
| # or
[ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f"
) # end capture group 1
(\d?\d) # match one or two digits in capture group 2
[ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f"
: # match ":"
( # begin capture group 3
\d # match a digit
| # or
[ \u00A0|\r|\n|\v|\f] # match one of the characters in the string " \u00A0|\r\n\v\f"
| # or
\z # match the end of the string
) # end capture group 3
/x # free-spacing regex definition mode
请注意 '|'
不是字符 class 中的特殊字符 ("or")。它被视为普通字符。 (即使 '|'
在字符 class 中被视为 "or",那也没有用,因为字符 classes 用于强制匹配其中的任何一个字符.)
假设
line = " 0 : 28 : 37.02"
然后
line.gsub(r, ':')
#=> " 0: 28 : 37.02"
#=> " "
#=> "0"
#=> " "
在捕获组 1 中,行的开头 (\A
) 不匹配,因为它不是字符,只有字符不匹配(虽然我不知道为什么不引发异常). "or" ('|'
) 的特殊字符导致正则表达式引擎尝试匹配字符串 " \u00A0|\r\n\v\f"
的一个字符。因此它将匹配字符串 line
.
下一个捕获组 2 捕获 "0"
。为此,捕获组 1 必须捕获 line
的索引 2 处的 space。然后再匹配一个 space 和一个冒号,最后,捕获组 3 将冒号后面的 space
子串' 0 : '
因此被替换为':' #=> '0: '
,所以gsub
returns" 0: 28 : 37.02"
。请注意 '0'
之前的一个 space 被删除了(但应该保留)。
一个解决方案
以下是如何删除一个或多个 Unicode 白色字符中的最后一个 space 字符,这些字符前面有一个或两个数字(但不能更多),并且在字符串末尾跟一个冒号或冒号后跟白色 space 或数字。 (哇!)
def trim(str)
str.gsub(/\d+[[:space:]]+:(?![^[:space:]\d])/) do |s|
s[/\d+/].size > 2 ? s : s[0,s.size-2] << ':'
end
end
正则表达式为 "match one or more digits followed by one or more whitespace characters, followed by a colon (all these characters are matched), not followed (negative lookahead) by a character other than a unicode whitespace or digit"。如果匹配,我们检查开头有多少位数字。如果超过两个则返回匹配项(无变化),否则从匹配项中删除冒号前的白色 space 字符并返回修改后的匹配项。
trim " 0 : 28 : 37.02"
#=> " 0: 28: 37.02" xxx
trim " 0\v: 28 :37.02"
#=> " 0: 28:37.02"
trim " 0\u00A0: 28\n:37.02"
#=> " 0: 28:37.02"
trim " 123 : 28 : 37.02"
#=> " 123 : 28: 37.02"
trim " A12 : 28 :37.02"
#=> " A12: 28:37.02"
trim " 0 : 28 :"
#=> " 0: 28:"
trim " 0 : 28 :A"
#=> " 0: 28 :A"
如果像示例中那样,字符串中的唯一字符是数字、白色space和冒号,则不需要回顾。
您可以使用 Ruby 的 \p{}
构造 \p{Space}
代替 POSIX 表达式 [[:space:]]
。两者都匹配 class 个 Unicode 白色 space 字符,包括示例中显示的字符。
" 0 : 28 : 37.02".gsub!(/(\d)(\s)(:)/,'')
=> " 0: 28: 37.02"
排除第三个数字可以用负回溯来完成,但由于其他一两个数字的长度可变,你不能对该部分使用正回溯。
line.gsub(/(?<!\d)(\d{1,2}) (?=:[ \d$])/, '')
# => " 0: 28: 37.02"