【问题标题】:Why do dashes mess up regex search?为什么破折号会弄乱正则表达式搜索?
【发布时间】:2021-02-14 00:40:15
【问题描述】:

我有 150 个文件,每个文件的标题都与这个类似

---
layout: page
title:  "WE07S-AWE"
date:  2018-10-21 01:31:26.000000000 -0600
---

Lots of text here...

我想在哪里提取标题WE07S-AWE 以及第二行破折号之后的所有内容。

但是破折号会以某种方式混淆搜索。

$ ruby -pe 's/---.*title:  "(.*?)".*---(.*)/m' test
-e:1: syntax error, unexpected '.'
s/---.*title:  "(.*?)".*---(.*)/m
-e:1: syntax error, unexpected '.'
s/---.*title:  "(.*?)".*---(.*)/m

问题

谁能弄清楚我做错了什么?

【问题讨论】:

  • 即使有这些修复,我也会遇到同样的错误...用你的版本更新了 OP
  • header明明是YAML,为什么不用YAML解析器来解析而不是自己写呢?
  • 语法错误是由/ 之前的杂散s 引起的,这使得它不是 正则表达式文字,但实际上是“s 划分了一些东西”,并且“某事”在语法上是不合法的。 Ruby 期望像s/---1 这样的东西 是合法的并且等价于s./(1.-@().-@().-@()),即“s 除以减-减-减一”。

标签: regex ruby


【解决方案1】:

你可以使用

ruby -0777 -pe 'sub(/---.*title:\s*"(.*?)".*---(.*)/m, "\\1 \\2")' test
ruby -0777 -ne 'print $1 + $2 if /---.*title:\s*"(.*?)".*---(.*)/m' test

详情

  • -0777 会将文件转换成一个字符串
  • m 修饰符使 . 也匹配换行符,默认情况下它不匹配
  • title:" 之间没有单个文字空格,而是使用 \s* 匹配零个或多个空格
  • sub 会将匹配替换为 Group 1 + Group 2 的内容,\1 \2
  • -n 假设 'while gets(); ...结束脚本循环
  • 如果正则表达式匹配,print $1 + $2 if /---.*title:\s*"(.*?)".*---(.*)/m 将打印第 1 组 + 第 2 组的值。

【讨论】:

    【解决方案2】:

    恕我直言,您应该尽可能避免使用.* 模式。这不仅会带来高性能风险,而且通常无法真正描述您正在寻找的内容。

    例如,如果您的 title 从未转义 " 字符,则更好的方法可能是:

     /title:[\s]+"([^"]+)".*---(.*)/m
    

    其余的细节更好answered by Wiktor Stribiżew(谁的回答我会投票)。

    【讨论】:

      【解决方案3】:

      假设str = File.read(<filename>),在哪里

      str = <<~BITTER_END
      ---
      layout: page
      title:  "WE07S-AWE"
      date:  2018-10-21 01:31:26.000000000 -0600
      ---
      Humpty Dumpty sat
      on a wall
      ---
      layout: page
      title:  "WE08RS-WEA"
      date:  2018-10-22 07:31:26.000000000 -0600
      ---
      Little Miss
      Muffet sat on
      her tuffet
      ---
      layout: page
      title:  "AR91G-HUH"
      date:  2017-03-13 01:30:26.000000000 -0800
      ---
      Three blind mice
      See how they run
      BITTER_END
      

      您可能希望执行一系列操作,而不是使用单个正则表达式,以提高可读性并促进测试。我将使用两个正则表达式:

      r1 = /^---\r?\n/
      r2 = /^title: +"([^"]+)/
      

      r1 表示“匹配由三个连字符组成的行”。 ^ 是行首锚点,\r?\n 是行终止符(如果文件是使用 Windows 创建的,则可以选择包含 回车 字符 \r)。

      r2 读作“匹配 'title:' 在行首后跟一个或多个空格 (+)、一个双引号,后跟一个或多个双引号以外的字符(如尽可能多)。[^"] 是一个字符类,它匹配除" 之外的任何字符。

      我们可以这样写:

      str.split(r1).
          drop(1).
          each_slice(2).
          with_object({}) { |(header,body),h| h[header[r2,1]] = body }
        #=> {"WE07S-AWE"=>"Humpty Dumpty sat\non a wall\n",
        #    "WE08RS-WEA"=>"Little Miss\nMuffet sat on\nher tuffet\n",
        #    "AR91G-HUH"=>"Three blind mice\nSee how they run\n"}
      

      步骤如下。

      a = str.split(r1)
        #=> ["",
        #    "layout: page\ntitle:  \"WE07S-AWE\"\ndate:  2018-10-21 01:31:26.000000000 -0600\n",
        #    "Humpty Dumpty sat\non a wall\n",
        #    "layout: page\ntitle:  \"WE08RS-WEA\"\ndate:  2018-10-22 07:31:26.000000000 -0600\n",
        #    "Little Miss\nMuffet sat on\nher tuffet\n",
        #    "layout: page\ntitle:  \"AR91G-HUH\"\ndate:  2017-03-13 01:30:26.000000000 -0800\n",
        #    "Three blind mice\nSee how they run\n"] 
      b = a.drop(1)
        #=> ["layout: page\ntitle:  \"WE07S-AWE\"\ndate:  2018-10-21 01:31:26.000000000 -0600\n",
        #   ...
        #    "Three blind mice\nSee how they run\n"] 
      c = b.each_slice(2)
        #=> #<Enumerator: ["layout: page\ntitle:  \"WE07S-AWE\"\ndate:  2018-10-21 01:31:26.000000000 -0600\n",..., "Three blind mice\nSee how they run\n"]:each_slice(2)> 
      

      我们可以看到将由枚举器c 生成并通过将其转换为数组传递给with_object 的元素。

      c.to_a
        #=> [["layout: page\ntitle:  \"WE07S-AWE\"\ndate:  2018-10-21 01:31:26.000000000 -0600\n",
        #     "Humpty Dumpty sat\non a wall\n"],
        #    ["layout: page\ntitle:  \"WE08RS-WEA\"\ndate:  2018-10-22 07:31:26.000000000 -0600\n",
        #     "Little Miss\nMuffet sat on\nher tuffet\n"],
        #    ["layout: page\ntitle:  \"AR91G-HUH\"\ndate:  2017-03-13 01:30:26.000000000 -0800\n",
        #     "Three blind mice\nSee how they run\n"]] 
      

      继续,

      d = c.with_object({})
        #=> #<Enumerator: #<Enumerator: ["layout:...]:each_slice(2)>:each_with_object({"\"WE07S-AWE"=>"Humpty Dumpty sat\non a wall\n"})> 
      

      d 可能被认为是一个复合枚举器,尽管 Ruby 没有这样的概念。继续,

      (header,body),h = d.next
        #=> [["layout: page\ntitle:  \"WE07S-AWE\"\ndate:  2018-10-21 01:31:26.000000000 -0600\n",
        #      "Humpty Dumpty sat\non a wall\n"],
        #    {}]
      

      Ruby 使用array decompositiond.next 分解为三个对象,分别成为三个块变量headerbodyh 的值。让我们检查一下这些值。

      header
        #=> "layout: page\ntitle:  \"WE07S-AWE\"\ndate:  2018-10-21 01:31:26.000000000 -0600\n" 
      body
        #=> "Humpty Dumpty sat\non a wall\n" 
      h #=> {} 
      

      这是h 的初始值。它将在计算过程中构建。现在检查块计算。

      s = header[r2,1]
        #=> "WE07S-AWE"
      h[s] = body
        #=> "Humpty Dumpty sat\non a wall\n"
      

      现在

      h #=> {"WE07S-AWE"=>"Humpty Dumpty sat\non a wall\n"}
      

      其余计算类似。

      请参阅String#splitArray#dropEnumerable#each_sliceEnumerator#with_object

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多