【问题标题】:Split string at double quotes and box brackets在双引号和方括号处拆分字符串
【发布时间】:2014-12-04 21:00:28
【问题描述】:

我刚开始使用 Python,我试图用双引号和方括号分割一个字符串。

例子:

10.223.157.186 - - [15/Jul/2009:14:58:59 -0700] "GET /assets/js/lowpro.js HTTP/1.1" 200 10469

想要的结果:

ip: 10.223.157.186
identity: -
username: -
time: [15/Jul/2009:15:50:35 -0700]
request: "GET /assets/js/lowpro.js HTTP/1.1"
status: 200
size: 10469

我想在“空间”分割它们,但它也会分割[]"" 之间的部分。像这样:

['10.223.157.186', '-', '-', '[15/Jul/2009:14:58:59', '-0700]', '"GET', '/assets/js/lowpro.js', 'HTTP/1.1"', '200', '10469']

我见过很多可能的解决方案,例如:

  • shlex(我的python没有导入)
  • data = line.strip().split('\"')

但这也会产生奇怪的输出 老实说,我不太了解正则表达式,也不知道是否可以导入为 shlex。

【问题讨论】:

  • 如果您没有shlex,是否介意告诉我们您正在运行的 Python 版本(应该是 Python 在交互式提示符下输出的第一件事)?
  • 这可能是学习一点正则表达式的好机会...
  • 我刚刚意识到您的问题说您要处理引号,但您的示例实际上并没有这样做。

标签: python string-split


【解决方案1】:

我要反对“正则表达式”并说要使用 解析器;解析器的复杂程度高于正则表达式,通常使用正则表达式来定义其语法的一部分。我选择的图书馆是pyparsing。你可以这样使用它:

>>> s = '10.223.157.186 - - [15/Jul/2009:14:58:59 -0700] "GET /assets/js/lowpro.js HTTP/1.1" 200 10469'
>>> from pyparsing import ZeroOrMore, Regex
>>> parser = ZeroOrMore(Regex(r'\[[^]]*\]') | Regex(r'"[^"]*"') | Regex(r'[^ ]+'))
>>> for i in parser.parseString(s): print i
...
10.223.157.186
-
-
[15/Jul/2009:14:58:59 -0700]
"GET /assets/js/lowpro.js HTTP/1.1"
200
10469

请注意,标记的顺序(那些Regex 对象)很重要。通过将方括号和双引号标记放在首位,它们获得优先权。如果你把最后一个放在第一位,那就不行了。这样做的一个很好的特性是它比正则表达式更容易扩展为更复杂(它只支持正则语言操作,除非你做了一堆疯狂的环顾四周的事情)。例如,如果您决定需要,解析器可以帮助您拆分这些括号或引号内的部分,并且通过一些工作,您可以更改解析器以允许嵌套括号或引号。 (后者是真正的正则表达式不能做的事情。你也许可以得到一个带有环视扩展的正则表达式来做到这一点,但在我看来这不值得。解析器要多得多功能强大,根据我的经验,更易于理解和使用。)

请注意,解析器不只是返回一个列表或可迭代的。它返回自己的特殊对象:

>>> parser.parseString(s)
(['10.223.157.186', '-', '-', '[15/Jul/2009:14:58:59 -0700]', '"GET /assets/js/lowpro.js HTTP/1.1"', '200', '10469'], {})
>>> type(parser.parseString(s))
<class 'pyparsing.ParseResults'>

我想我也应该解释一下我的正则表达式。

  • \[[^]]*\]:这只是匹配一对方括号,可选地在它们之间添加一些东西。 \[ 表示它需要以方括号开头。 [^]] 是一个字符类(只是正则表达式中的一组字符);外括号使它成为一个字符类。 ^ 告诉它“除了字符列表中的内容之外的任何内容”,而内部的 ] 只是字符列表。所以那部分只是“]以外的任何东西”。 * 的意思是“前面的零个或多个字符”,所以它的意思是“除] 之外的零个或多个字符”。最后是\],这意味着它需要以] 结尾。开头的[ 和结尾的] 必须使用\ 进行转义,因为它们通常用于指示字符类。

  • "[^"]*":这个和上一个差不多。 " 说它必须以双引号开头。 [^"] 是和以前一样的字符类;它的意思是“除了双引号之外的任何东西”。 * 仍然意味着“零或更多”。最后的" 只是意味着它必须以引号结尾。所以这只是“用双引号括起来的零个或多个字符”。

  • [^ ]+: [^ ] 是另一个相同类型的字符类。它只是表示“除空格之外的任何字符”,因为字符列表是一个空格。 + 的意思是“一个或多个以前的事情”(类似于*,除了它至少需要一个)。所以这是“一个或多个非空格字符”。

Regex 对象之间的 | 运算符只是将不同的标记“或”组合在一起,以便解析器在匹配这三个可能的标记之一时立即吐出一个标记。

【讨论】:

  • 感谢您的快速回答!
  • @user3523150 没问题。如果我们的答案之一解决了您的问题,请考虑接受它。
【解决方案2】:

您可以使用正则表达式:

line = """10.223.157.186 - - [15/Jul/2009:14:58:59 -0700] "GET /assets/js/lowpro.js HTTP/1.1" 200 10469\n"""

import re

log_line = re.compile('(?P<ip>[^ ]*) (?P<identity>[^ ]*) (?P<username>[^ ]*) \\[(?P<time>[^\\]]*)\\] "(?P<request>[^"]*)" (?P<status>[^ ]*) (?P<size>[^ ]*)$')

for key, value in log_line.match(line).groupdict().iteritems():
    print "%s: %s" % (key, value)

您也可以按照@jpmc26 的建议使用解析器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-19
    • 2017-08-24
    • 2018-04-14
    • 2014-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多