【问题标题】:How can I Correctly Parse a Hex Color Code in Python using Regex?如何使用正则表达式正确解析 Python 中的十六进制颜色代码?
【发布时间】:2020-02-16 17:52:25
【问题描述】:

我是 Regex 的初学者,所以我通过解决我能找到的所有练习来不断练习。在其中之一中,我需要使用 Regex 和 Python 从 HTML 源代码中提取所有十六进制代码。根据练习,发现 Hex 代码的规则是:

  1. 以#开头
  2. 有 3 位或 6 位数字
  3. 每个数字都在 0-F 范围内(字符串不区分大小写)

示例输入是这样的:

#BED
{
    color: #FfFdF8; background-color:#aef;
    font-size: 123px;
    background: -webkit-linear-gradient(top, #f9f9f9, #fff);
}
#Cab
{
    background-color: #ABC;
    border: 2px dashed #fff;
}

想要的输出是:

#FfFdF8
#aef
#f9f9f9
#fff
#ABC
#fff

#BED#Cab 将被省略,因为它们不是十六进制颜色。

我试过这段代码,解决了这个问题:

import re

text = """
#BED
{
    color: #FfFdF8; background-color:#aef;
    font-size: 123px;
    background: -webkit-linear-gradient(top, #f9f9f9, #fff);
}
#Cab
{
    background-color: #ABC;
    border: 2px dashed #fff;
} """

r = re.compile(r'#[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}')
a = r.findall(text)
print(a)

得到的输出:

['#BED', '#FfF', '#aef', '#f9f', '#fff', '#Cab', '#ABC', '#fff']

它工作得很好,除了它没有捕捉到 6 位代码并且它没有消除实际上不是十六进制颜色代码的两个标签。

我错了什么?我查看了其他尝试,但他们没有提供正确的答案。我正在使用 Python 3.7.4 和最新版本的 PyCharm。

【问题讨论】:

  • #BED#CAB 是有效的十六进制颜色。
  • 我知道,但在这个练习中,它们是它们名字的书签。我不精通 HTML。
  • @dgw 是的,但 #BED#CAB 在该示例中不是颜色。
  • 但是正则表达式无法区分。所以正则表达式也会显示这些,这不会是错误。

标签: python regex parsing


【解决方案1】:

一方面,您可以首先匹配 6 位代码,否则匹配 3 位代码将首先匹配其中的一半(因此不匹配完整的 6 位代码)。但由于您还只想匹配 CSS 属性规则,而不是选择器,请提前查找 ;,)

(?i)#(?:[0-9a-f]{6}|[0-9a-f]{3})(?=[;,)])

https://regex101.com/r/BtZaoV/2

如果您还需要能够排除组合选择器,例如#BED, foo {,您可以先查找非{s,后跟}

(?i)#(?:[0-9a-f]{6}|[0-9a-f]{3})(?=[^{]*})

https://regex101.com/r/BtZaoV/3

使用不区分大小写的标志来保持 DRY。 (您也可以使用 {3}){1,2} 来避免重复字符集,但这会使模式更难阅读 IMO)

【讨论】:

  • #BED, p { } 多个css选择器?
  • 我觉得第二个更好,+1。
  • 是的,我认为 css (+1) 中没有嵌套的 {}
  • 有媒体查询时匹配失败,Regex Demo,因为(?=[^{]*)
  • @CodeManiac (?=[^{]*}|[^{]*?@media) 我认为这将解决问题。
【解决方案2】:

你可以试试

#(?:[0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})(?=;|[^(]*\))

所以这里的想法是匹配6字符长度,如果找不到匹配3字符匹配,以确保它不匹配#BED或者我们需要匹配十六进制颜色代码的终止,所以我们交替使用前瞻

Regex Demo

【讨论】:

  • 很棒的图表!
【解决方案3】:

你可以使用

r = re.compile(r'#[0-9A-Fa-f]{3}(?:[0-9A-Fa-f]{3})?(?!$)', re.M)

proof

示例 Python 代码:

import re
regex = r"#[0-9A-Fa-f]{3}(?:[0-9A-Fa-f]{3})?(?!$)"
test_str = ("#BED\n"
    "{\n"
    "    color: #FfFdF8; background-color:#aef;\n"
    "    font-size: 123px;\n"
    "    background: -webkit-linear-gradient(top, #f9f9f9, #fff);\n"
    "}\n"
    "#Cab\n"
    "{\n"
    "    background-color: #ABC;\n"
    "    border: 2px dashed #fff;\n"
    "}")
matches = re.findall(regex, test_str, re.MULTILINE)
print(matches)

【讨论】:

  • 如问题#BED and #Cab are to be omitted, because they are not Hex colors.中所述,此模式将同时匹配两者。
  • 虽然这是朝着正确方向迈出的一步,但我认为可以通过将 \b 替换为 (?!$) 来修复它:#[0-9A-Fa-f]{3}(?:[0-9A-Fa-f]{3})?(?!$) 以避免在字符串末尾匹配(或者如果 @ 则为行使用 987654329@ 选项),请参阅demo
  • @WiktorStribiżew 同意,但是当有多个 css 选择器时,建议的不会覆盖,#BED, xyz
猜你喜欢
  • 1970-01-01
  • 2018-03-17
  • 2012-10-02
  • 1970-01-01
  • 1970-01-01
  • 2019-04-25
  • 2011-10-03
  • 2017-07-02
相关资源
最近更新 更多