如果存在,正则表达式匹配与字符串之前的内容相结合

时间:2018-03-15 14:49:59

标签: regex

我试图从字符串中获取子字符串 如:

测试字符串:

cat_zoo_New_York_US
dog_zoo_South_Carolina
dolphin_zoo_Montreal_Canada
pokemon_home_d_K2-155

返回子字符串:

cat, New_York
dog, South_Carolina
dolphin, Montreal
pokemon, d

我试过的正则表达式是

([\w]+)(?:(_zoo_|_home_))(((?!(_US|_Canada|_K2-155))\w)+)

我不认为它非常简洁,除了我需要的东西之外还返回其他子字符串。你有什么其他的建议? 谢谢!

一些更新

在第3只鸟的回答@ 03/15/2018之后

首先,我喜欢将([^_]+)(?:)同时用于示例字符串的不同部分。但是,让我扩展一些示例字符串。

cat_zoo_New_York_US
dog_zoo_South_Carolina
yellow_dolphin_zoo_Montreal_Canada
pokemon_home_d_K2-155
pokemon_home_zoo_d_K2-155

我实际上想要使用诸如'zoo','home'或'home_zoo'之类的锚字符串来分隔前后的字符,以及匹配(和丢弃)国家的最后部分(或任何指定的地方) ID),这使得这个问题不那么普遍(我喜欢使用_的想法,但让我更好地学习它)。 这里有两个问题

  1. 此处(?=).*的功能是什么? (?=(?:_US|_Canada|_K2-155|$)).*$?好像我用了 (?:_US|_Canada|_K2-155|$),它仍然可以......
  2. 因为我在锚字符串上稍微扩展了一下,让它支持 _,我用过:

    (.*?)(?:_*)(?:home_zoo|zoo|home)(?:_*)(.*?)(?:_*)(?:US|Canada|K2-155|$)

    似乎没问题,但如果我使用:

    (.*?)(?:_*)(?:home|zoo|home_zoo)(?:_*)(.*?)(?:_*)(?:US|Canada|K2-155|$)

    首先匹配home最后一个样本字符串。有没有 贪心算法捕获这个而不指定模式的顺序 字符串?

  3. 再说一次,我不想制作一长串的锚字符串,但我没有其他的想法,如果没有这样做就更普遍。 再次感谢!

2 个答案:

答案 0 :(得分:1)

好吧,我尝试了一种更简单的方法。如果您的数据比上面提供的样本更复杂,则可能会失败。否则,对于上面的文本,它工作正常。

这是我使用的表达式:

^([^_]*)_[^_]*_(.*)_.*$
1       23    45   67

基本上我做的是:

  1. 从行的开头开始对第一个不包含_的字符流进行分组。
  2. 然后有一个_跟随上述组
  3. 跟随一个任意长度的字符串,其中没有_&li;
  4. 然后是_
  5. 分组下一个任意长度的字符串
  6. 来和_之后
  7. 其余的字符串
  8. 将其替换为\ 1,\ 2(第一组,第二组)。

    你可以找到一个小提琴here

    如果您正在使用vim,您还可以使用以下命令在vim中实现相同的功能:

    :%s/^[^_]*_\([^_]*\)_\(.*\)_.*$/\1, \2/g
    

    <强>更新

    ^([^_]*)_[^_]*_(((?:South_)|(?:New_))*[^_]*)((?:_US)|(?:_Canada)|(?:_K2-155))*$
    

    你可以找到新的小提琴(这里)[https://regex101.com/r/qQ2dE4/273]

    这个与前一个有什么区别?

    现在,我作弊了一点,因此我寻找形容词,修改州名,如South_或New_。您可以在此处添加更多内容,例如East_,West_,Old_或其他任何内容,如果您的日期有案例。

    在某些情况下,数据会跳过国家/地区。加上看起来最后一行的最后一个标记不跟随模式。因此,我在表达式中明确列出了这些选项,如美国,加拿大等。您可能还需要在此处添加更多例外情况。

答案 1 :(得分:1)

你可以这样试试:

^([^_]+)_[^_]+_(.*?)(?=(?:_US|_Canada|_K2-155|$)).*$

这将捕获2组。例如,您可以使用此替代group1, group2

首先捕获以组{1}中的下划线结尾的第一部分,如cat_。然后匹配以zoo_home_等下划线结尾的第二部分。

从那一点开始捕获一个组,直到你使用前瞻(?=或字符串结尾遇到一个值。

那将匹配:

  • ^字符串的开头
  • ([^_]+)匹配的捕获组不是_一次或多次(组1)
  • _[^_]+_匹配_然后不是_一次或多次,后跟_
  • (.*?)在群组中捕获任何角色零次或多次贪婪(群组2)
  • (?=肯定右侧的正面前瞻性
    • (?:非捕获组
      • _US|_Canada|_K2-155|$您的值或字符串的结尾
    • )关闭群组
  • )关闭群组
  • .*$匹配任何字符零次或多次,直到字符串结尾

修改:更新后的问题,或许这符合您的要求:

^(.*?)_(?:home_zoo|zoo|home)(.*?)(?=(?:_US|_Canada|_K2-155|$))

这将匹配任何charcter零次或多次非贪婪(.*?),然后是下划线和非捕获组(?:home|zoo|home_zoo)来分隔前后的字符。