【问题标题】:Regex Return First Match正则表达式返回第一个匹配
【发布时间】:2017-07-11 03:47:09
【问题描述】:

我有一个天气文件,我想在其中提取记录在 JSON 文件中的“air_temp”的第一个值。这个 HTTP 检索器使用的格式是正则表达式(我知道这不是最好的方法)。

为简单起见,我将 JSON 文件缩短为 2 个数据条目 - 通常有 100 个。

    {
  "observations": {
    "notice": [
      {
        "copyright": "Copyright Commonwealth of Australia 2017, Bureau of Meteorology. For more information see: http://www.bom.gov.au/other/copyright.shtml http://www.bom.gov.au/other/disclaimer.shtml",
        "copyright_url": "http://www.bom.gov.au/other/copyright.shtml",
        "disclaimer_url": "http://www.bom.gov.au/other/disclaimer.shtml",
        "feedback_url": "http://www.bom.gov.au/other/feedback"
      }
    ],
    "header": [
      {
        "refresh_message": "Issued at 12:11 pm EST Tuesday 11 July 2017",
        "ID": "IDN60901",
        "main_ID": "IDN60902",
        "name": "Canberra",
        "state_time_zone": "NSW",
        "time_zone": "EST",
        "product_name": "Capital City Observations",
        "state": "Aust Capital Territory"
      }
    ],
    "data": [
      {
        "sort_order": 0,
        "wmo": 94926,
        "name": "Canberra",
        "history_product": "IDN60903",
        "local_date_time": "11/12:00pm",
        "local_date_time_full": "20170711120000",
        "aifstime_utc": "20170711020000",
        "lat": -35.3,
        "lon": 149.2,
        "apparent_t": 5.7,
        "cloud": "Mostly clear",
        "cloud_base_m": 1050,
        "cloud_oktas": 1,
        "cloud_type_id": 8,
        "cloud_type": "Cumulus",
        "delta_t": 3.6,
        "gust_kmh": 11,
        "gust_kt": 6,
        "air_temp": 9.0,
        "dewpt": 0.2,
        "press": 1032.7,
        "press_qnh": 1031.3,
        "press_msl": 1032.7,
        "press_tend": "-",
        "rain_trace": "0.0",
        "rel_hum": 54,
        "sea_state": "-",
        "swell_dir_worded": "-",
        "swell_height": null,
        "swell_period": null,
        "vis_km": "10",
        "weather": "-",
        "wind_dir": "WNW",
        "wind_spd_kmh": 7,
        "wind_spd_kt": 4
      },
      {
        "sort_order": 1,
        "wmo": 94926,
        "name": "Canberra",
        "history_product": "IDN60903",
        "local_date_time": "11/11:30am",
        "local_date_time_full": "20170711113000",
        "aifstime_utc": "20170711013000",
        "lat": -35.3,
        "lon": 149.2,
        "apparent_t": 4.6,
        "cloud": "Mostly clear",
        "cloud_base_m": 900,
        "cloud_oktas": 1,
        "cloud_type_id": 8,
        "cloud_type": "Cumulus",
        "delta_t": 2.9,
        "gust_kmh": 9,
        "gust_kt": 5,
        "air_temp": 7.3,
        "dewpt": 0.1,
        "press": 1033.1,
        "press_qnh": 1031.7,
        "press_msl": 1033.1,
        "press_tend": "-",
        "rain_trace": "0.0",
        "rel_hum": 60,
        "sea_state": "-",
        "swell_dir_worded": "-",
        "swell_height": null,
        "swell_period": null,
        "vis_km": "10",
        "weather": "-",
        "wind_dir": "NW",
        "wind_spd_kmh": 4,
        "wind_spd_kt": 2
      }
    ]
  }
}

我目前使用的正则表达式是:.*air_temp": (\d+).*,但这会返回 97.3(条目 1 和 2)。有人可以建议一种只返回第一个值的方法吗?

我尝试过使用惰性量词组,但没有成功。

【问题讨论】:

  • 你用什么语言编程?

标签: json regex capture-group


【解决方案1】:

这个正则表达式会帮助你。但我认为您应该捕获并提取与您正在使用的编程语言的特征的第一个匹配项。

.*air_temp": (\d{1,3}\.\d{0,3})[\s\S]*?},

要更好地理解正则表达式:查看at this

更新

如果您只有两个数据条目,则上述解决方案有效。对于两个以上的条目,我们应该使用这个:

header[\s\S]*?"air_temp": (\d{1,3}\.\d{0,3})

在这里,我们首先匹配单词header,然后以非贪婪的方式匹配任何内容。之后,我们匹配我们预期的模式。因此我们得到了第一场比赛。在regex101 玩它。

要捕获负数,我们需要检查是否存在任何- 字符。我们通过? 执行此操作,这意味着“问号表示前面的元素出现零次或一次”。

所以正则表达式变成了,

header[\s\S]*?"air_temp": (-?\d{1,3}\.\d{0,3})Demo

但是使用\K 没有global 标志(在mickmackusa 给出的另一个答案中)更有效。为了检测负数,该正则表达式的修改版本是

air_temp": \K-?\d{1,2}\.\d{1,2}demo.

这里{1,2} 表示前一个字符出现1~2 次/秒。我们将其用作{min_occurance,max_occurance}

【讨论】:

  • 这确实适用于正数!但是您也有用于负值的建议吗? IE。如果“air_temp”是-2?
【解决方案2】:

我不知道您使用的是哪种语言,但似乎是全局标志和不使用全局标志之间的区别。

如果未设置全局标志,则仅返回第一个结果。如果在您的正则表达式上设置了全局标志,它将遍历返回所有可能的结果。您可以使用 Regex101 轻松测试它,https://regex101.com/r/x1bwg2/1

懒惰/贪婪不应该对使用/不使用全局标志产生任何影响

【讨论】:

    【解决方案3】:

    如果您的编码语言允许使用\K,请使用:Demo

    /air_temp": \K[\d.]+/ (117steps) 这将非常有效地搜索您的超大 JSON 文本。

    如果不允许\K,则可以使用捕获组:(Demo)

    /air_temp": ([\d.]+)/ 这仍然会以不错的速度通过您的 JSON 文本

    请注意,模式末尾没有全局标志,因此在一次匹配后,正则表达式引擎停止搜索。



    更新:

    对于“较少文字”匹配(但您的来源是否可靠并不重要),您可以使用:

    扩展字符类以包括-:

    /air_temp": \K[\d.-]+/   #still 117 steps
    

    或更改为否定字符类并匹配不是, 的所有内容(因为该值始终以逗号结尾):

    /air_temp": \K[^,]+/     #still 117 steps
    

    对于非常严格的匹配(如果您正在寻找一种意味着您对输入数据的信心为零的模式)...

    您的数据似乎不会超过小数点后一位,01 之间的临时值在小数点前加上 0,我认为您不必担心数百个临时值(对吗?),所以你可以使用:

    /air_temp": \K-?[1-9]?\d(?:\.\d)?     #200steps
    

    解释:

    1. 可选的负号
    2. 可选十位数
    3. 必填个位
    4. 可选的十进制,后面必须跟一个数字

    Accuracy Test Demo

    Real Data Demo

    【讨论】:

    • \K 是允许的 - 谢谢。虽然我没有考虑负温度值。例如,您建议我使用什么来捕获 -5?
    • @HenryBlue 我已经用一些新的模式和解释更新了我的答案。您应该花点时间根据您的新要求更新您的问题,以便我们的答案有意义。如果您有任何问题,请尽管提问。
    猜你喜欢
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    相关资源
    最近更新 更多