【问题标题】:How do I match every word in a string except for the last word?如何匹配字符串中除最后一个单词之外的每个单词?
【发布时间】:2016-09-30 23:33:00
【问题描述】:

我有以下字符串:

Chicago CPA
New York CPA
West Virginia Accountant

我如何始终只删除字符串中的最后一个单词(和前面的空格),保留最后一个单词之前的所有其他单词?

所以上述数据集的正确版本应该是:

Chicago
New York
West Virginia

另外,是否可以在 Rubular 上测试匹配组,或者是否有其他在线正则表达式编辑器/测试器可用于测试具有匹配组的正则表达式?

编辑 1

许多答案在理论上都很棒。我读了它们,我理解它们,我在香草弦上测试它们,它们似乎有效。但是当我在我的数据上尝试它时,它没有。我被难住了一会儿,我才明白为什么。

这是我正在处理的 HTML:

<h1 class="search-term">
   Chicago&nbsp;<strong>Cpa</strong>
</h1>

所以这是文本,我正在尝试对以下字符串进行操作:

Chicago&nbsp;<strong>Cpa</strong>

所以当我尝试以下每个答案时会发生这种情况。


@Darshan 的:

[56] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text
=> "Chicago Cpa"
[57] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.class
=> String
[58] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.match(/(.*) \w+\z/)[1]
NoMethodError: undefined method `[]' for nil:NilClass
from (pry):57:in `<class:PageCrawler>'
[59] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text[/.*(?=\s\w+\z)/]
=> nil

@Lucas 自己的:

[60] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text
=> "Chicago Cpa"
[61] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.class
=> String
[62] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.split()[0...-1].join(' ')
=> ""

@Eric 自己的:

[65] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text
=> "Chicago Cpa"
[66] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.class
=> String
[67] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.split().reverse.drop(1).reverse.join(" ")
=> ""

@Casimir 自己的(实际上这是迄今为止最好的):

[68] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text
=> "Chicago Cpa"
[69] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.class
=> String
[70] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.sub(/\W+\w+\W*$/, '')
=> "Chicago"

@Santosh 自己的:

[71] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text
=> "Chicago Cpa"
[72] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text.class
=> String
[73] pry(YPCrawler::PageCrawler)> @document.css('header h1.search-term').first.text[/(.*)\s/,1]
=> nil

我很抱歉没有早点这样做,但我没有预料到这是一个问题。

【问题讨论】:

    标签: ruby regex


    【解决方案1】:

    首先我会说我对正则表达式不是特别擅长,而且我不确定(我也不倾向于基准测试或认真思考)这是否会成为比@LucasP 的非正则表达式方法效率更高或更低。但这是我想到的明显方法:

    s.match(/(.*) \w+\z/)[1]
    

    匹配字符串末尾的一个或多个单词字符,前面有一个空格,然后将之前的所有内容放入一个组中,然后您可以抓取。

    data = ['Chicago CPA',
            'New York CPA',
            'West Virginia Accountant']
    
    data.map{|s| s.match(/(.*) \w+\z/)[1]}
    # => ["Chicago", "New York", "West Virginia"]
    

    编辑:@CarySwoveland 建议的这种方法的一个变体是使用前瞻表达式来忽略我们想要丢弃的部分,而不是我最初将我们想要的部分放入的方法然后我们访问的捕获组。这是该方法的一个版本:

    data.map{|s| s[/.*(?=\s\w+\z)/]}
    # => ["Chicago", "New York", "West Virginia"]
    

    编辑 2: 有了您添加的信息,现在很明显您面临的问题是您有不间断的空格,即使使用 \s 也不匹配 (\s只匹配 ASCII 空格,相当于[ \t\r\n\f])。因此,使用 POSIX 括号表达式 [[:space:]] 或显式匹配 \u00A0 来获取不间断空格字符是有效的,假设所有都是不间断空格。我更喜欢前者,因为有时您可能会有其他空白:

    data.map{|s| s[/.*(?=[[:space:]]\w+\z)/]}
    

    【讨论】:

    • 我知道你要去哪里,但这个正则表达式似乎与 Rubular.com 的 Chicago CPA 不匹配。它只匹配
    • @marcamillion 再次,它在我的机器和 rubular.com 上都适用于我。我会将你的数据集转储到一个十六进制编辑器中,看看你的空白是怎么回事。
    • 我发帖时没有注意到您的回答。两者非常接近,所以我杀了我的。
    • @CarySwoveland 是的,看起来我们有相同的通用方法,谢谢。我喜欢你的前瞻版本,它忽略了我们想要丢弃的部分,而不是对我们想要的部分进行分组并抓住组。
    • 我一直在努力弄清楚为什么您的建议对我的实际数据不起作用,但是当我尝试使用香草字符串时它起作用了。我更新了这个问题,提供了有关我的数据实际外观的更多详细信息。你能相应地更新你的答案吗?谢谢!
    【解决方案2】:

    实现此目的的一种方法如下:

    myString.split()[0...-1].join(' ')
    

    myString 是您要对其执行此操作的每个字符串。

    1. 首先,您从字符串拆分为包含每个单词的列表。

    2. 然后选择包含除最后一个以外的所有元素的子列表。

    3. 最后你从列表返回到字符串。

    【讨论】:

    • 所以我最初尝试过类似的事情,但是当我尝试拆分这些字符串时发生了最奇怪的事情。 &gt; "Chicago Cpa".split =&gt; ["Chicago Cpa"]。它不会为每个单词创建一个新元素……我觉得这很奇怪。这可能是什么原因造成的?
    • @marcamillion 当我复制并粘贴它时,我按预期得到["Chicago", "Cpa"]。你的字符串中有可能有一些奇怪的空格吗?
    • 这很奇怪。不知道为什么我的没有正确分裂。我没有看到任何奇怪的空格。
    • 你不是说[0..-2]吗?
    • [0..-2] 或 [0...-1],都是一样的。 Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.
    【解决方案3】:

    假设您有多个单词,您可以使用替换:

    'West Virginia Accountant'.sub(/\W+\w+\W*$/, '')
    

    【讨论】:

    • 使用这种方法有什么缺点?在哪些情况下会引发误报?
    • @marcamillion:这取决于您所说的“单词”,例如,此模式会因带有重音字母的单词(但这可以通过一些更改轻松解决)或名称而失败包含“Scarlett O'Hara”=>“Scarlett O”之类的引号或带有首字母缩略词“born in the U.S.A”=>“born in the U.S.”,但您可以将模式更改为 /\p{Z}+\P{Z}+\p{Z}*$/(其中 \p{Z} 匹配所有 unicode 分隔符)。
    【解决方案4】:
    "New York Accountant".split().reverse.drop(1).reverse.join(" ")
    

    【讨论】:

    • split[0..-2] 不是比split().reverse.drop(1).reverse 更直接吗?
    • 我一直在努力弄清楚为什么您的建议对我的实际数据不起作用,但是当我尝试使用香草字符串时它起作用了。我更新了这个问题,提供了有关我的数据实际外观的更多详细信息。
    【解决方案5】:

    尝试关注。

    str = ['Chicago CPA', 'New York CPA', 'West Virginia Accountant']
    
    str.map{|s| s[0...s.rindex(' ')]}
    

    输出:["Chicago", "New York", "West Virginia"]

    使用正则表达式。

    str2 = "West Virginia Accountant"
    p str2[/(.*)\s/,1]
    

    输出:"West Virginia"

    【讨论】:

    • 我一直在努力弄清楚为什么您的建议对我的实际数据不起作用,但是当我尝试使用香草字符串时它起作用了。我更新了这个问题,提供了有关我的数据实际外观的更多详细信息。
    • @marcamillion 在您的实际字符串中包含“ ”。所以我的正则表达式无法检测到这一点。所以你可以使用正则表达式/\W+\w+\W*$/。你能过去你的实际字符串吗?
    【解决方案6】:

    您可以使用正则表达式 /^(.*)\s+\w+\s*$/ 来捕获除最后一个单词之外的所有内容:

    例子:

    str =  <<~EOF
            Chicago CPA
            New York CPA
            West Virginia Accountant
    EOF
    
    str.each_line do |line|
            puts line.match(/^(.*)\s+\w+\s*$/).captures.first
    end
    

    输出:

    Chicago
    New York
    West Virginia
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-18
      • 1970-01-01
      • 2023-02-03
      • 2021-01-11
      • 2015-04-08
      • 1970-01-01
      • 2023-02-07
      • 1970-01-01
      相关资源
      最近更新 更多