【问题标题】:Simpler, safer string manipulation Python更简单、更安全的字符串操作 Python
【发布时间】:2015-06-07 22:14:53
【问题描述】:

我使用 Python 进行了很多业余数据清理和擦洗 - 它比使用 Excel 快得多。但我觉得我必须以艰难的方式做每一件事。最大的痛苦是我不知道如何安全地从列表索引或字符串索引中获取,而不会出错或在我的代码中一层又一层不可读的 try/except 乱扔垃圾。

这是我刚刚想出的一个示例,用于清理城市/州组合的 Trulia 个人资料 URL。有时他们不给出状态,但模式非常标准化。

checkstr = 'http://www.trulia.com/profile/agent-name-agent-orlando-fl-24408364/'

state = ''
citystrs = re.findall('-agent-(.*)-\d', checkstr)[0:1]
print citystrs
for citystr in citystrs:
    if '-' in citystr:
        if len(citystr.split('-')[-1]) == 2:
            state = citystr.split('-')[-1].upper().strip()
            city = string.replace(citystr.upper(), state, '')
            city = string.replace(city, '-', ' ').title().strip()
        else:
            city = string.replace(citystr, '-', ' ').title().strip()
    else:
        city = citystr.title().strip()

print city, state

我不需要多个“答案”,但我使用切片 [0:1] 和 for,因为我不希望错误停止我的代码(这样做大约 200 万次)每当模式不适合 findall[0]。

我能否获得一些关于pythonic(和高效)方法更简单的指示?

编辑 1:我不是在寻找不合格的字符串。我希望足够安全,让它贯穿一切并“尽其所能”(即,更符合>更少)

编辑 2:示例中遗漏了一个非常明显的细节:多个单词的城市有内部破折号 ('-')。例如。 agent-name-los-angeles-82348233/

【问题讨论】:

  • 你能包含一个 nonconforming 字符串吗?
  • 我不明白[0:1] 切片在这里给你买了什么?此外,str.partition() 而不是 str.split() 可以提供帮助,因为它总是最多中断一次并准确地吐出三个子字符串。但是由于您无论如何都在使用正则表达式,似乎您可以将所有可能的模式表达为正则表达式替代品?喜欢-agent-(?:(\w+)-(\w{2})|...)-\d
  • 以后,如果您有工作代码并且只是想改进它,请考虑将您的问题提交给Code Review Stack Exchange,而不是在这里发布。这里不一定是题外话,所以它不应该被关闭,但审查和改进现有的工作代码是该网站的专长。
  • @Eevee - 我的想法是避免尝试在不起作用的东西上索引 [0] 时出现异常。我经历了数百万行,我不相信盲目的索引。
  • 但是你从来没有真正做一个[0] 索引。如果findall 失败,它会生成一个空列表,然后循环将跳过该列表。每个 url 可能匹配不止一次,但无论如何你都可以测试所有内容,当你找到一个好的匹配时break

标签: python regex string python-2.7 indexing


【解决方案1】:

为什么不一直使用切片?

if '-' in citystr:
    sep_index = citystr.find('-')
    city = citystr[0:sep_index].title()
    state = citystr[sep_index+1:].upper()
else:
    city = citystr.title()

使用 timeit(number=10000):

yours : 3.56353430347
mine :  1.04823075931

【讨论】:

    【解决方案2】:

    我会这样做:

    import re
    
    reg = re.compile(r'-agent-(?P<city>[^-]*)(?:-(?P<state>[^-]*))?-\d')    
    
    checkstr = 'http://www.trulia.com/profile/agent-name-agent-orlando-fl-24408364/'
    
    m = reg.search(checkstr)
    
    city = m.group('city').title()
    state = m.group('state').upper() if (m.group('state')) else ''
    
    print city, state
    

    如果您需要多次使用该模式,您可以使用re.compile一劳永逸地编译它

    我没有使用.*,它非常宽松并会产生回溯,而是使用[^-]*(所有这些都不是破折号零次或多次),它在第一个破折号之前停止。 p>

    状态和前一个破折号位于一个可选组中:(?:-(?P&lt;state&gt;[^-]*))?。因此,即使字符串没有状态部分,模式也会成功。

    不再需要此更改re.findall,您可以使用返回单个结果的re.search。请注意,如果您不确定字符串格式,您可以随时添加测试以检查是否匹配。

    为了使代码更具可读性,我使用命名捕获(?P&lt;name&gt;...)。所以通过这种方式你可以很容易地检索到一个组的内容:m.group('name')。但是,如果您想稍微提高速度,可以使用编号组(但这不是很重要)。

    【讨论】:

    • 对不起 - 这是一个很好的答案,但我没有正确指定城市可以有内部破折号。这就是为什么我在 -### 部分之前检查最后一个文本位的长度的原因。我什至假设没有多个单词的城市(洛杉矶)以两个字母单词结尾。不过,我提出问题的目的是作为一个示例,帮助我更好地学习更有效地编码,您的命名分组很有帮助。
    【解决方案3】:
    • 当您只查找第一个匹配项时,使用re.search 而不是findall 会更清楚。
    • 如果可能有多个匹配项(如您使用[0:1] 所建议的那样),请注意.* 是贪婪的。例如,您的正则表达式从字符串-agent-orlando-fl-24408364-agent-orlando-fl-24408364 捕获orlando-fl-24408364-agent-orlando-fl。请改用.*?
    • rpartition 字符串方法在分隔符的最后一次出现处进行拆分,并始终返回三个字符串,这样可以更轻松地处理极端情况。

    建议代码:

    m = re.search('-agent-(.*?)-\d', checkstr)
    if m:
        citystr = m.group(1)
        city, _, state = citystr.rpartition('-')
        if len(state) <> 2:
            city = citystr
            state = ''
        city = city.replace('-', ' ').title()
        state = state.upper()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 2017-11-20
      • 1970-01-01
      相关资源
      最近更新 更多