在Web日志请求字符串中删除其他双引号

时间:2009-06-09 15:40:01

标签: ruby

我尝试编写一个解析Web日志的ruby程序,并确保日志的每个部分都有效。我试图处理日志的请求字符串中的情况,除了起始和结束之外,它还有额外的双引号。我以正则表达式的形式创建了Web日志,因为它更容易阅读为每个部分创建变量。到目前为止,我有这个问题:

isVal = true
lines = lg.readlines
logLine_regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}) - (\w*|-) \[(\d{2})\/(\w{3})\/(\d{4}):(\d{2}):(\d{2}):(\d{2})\s(-0400)\] (".*") (\d+) (\d+|-)$/

lines.each{ |line|

    linePos = logLine_regex.match(line)

    if linePos == nil
        isVal = false
    elsif linePos[0] != line.chomp
        isVal = false
    elsif !((0..255).include?(linePos[1].to_i))
        isVal = false
    elsif !((0..255).include?(linePos[2].to_i))
        isVal = false
    elsif !((0..255).include?(linePos[3].to_i))
        isVal = false
    elsif !((0..255).include?(linePos[4].to_i))
        isVal = false
    #linePos[5] = Username or hyphen
    elsif !((1..31).include?(linePos[6].to_i))
        isVal = false
    elsif !(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].include?(linePos[7]))
        isVal = false
    elsif !((0..9999).include?(linePos[8].to_i))
        isVal = false
    elsif !((0..23).include?(linePos[9].to_i))
        isVal = false
    elsif !((0..59).include?(linePos[10].to_i))
        isVal = false
    elsif !((0..59).include?(linePos[11].to_i))
        isVal = false
    #linePos[12] = -4000
    #linePos[13] = request
    elsif !((0..9999).include?(linePos[14].to_i))
        isVal = false
    #linePos[15] = bytes
    else
        isVal = true
    end

}

我知道如果它们是额外的双引号可以通过在其前面添加反斜杠来逃避,但我不知道如何在ruby中编写代码。请帮忙??

2 个答案:

答案 0 :(得分:1)

直接的方法是根据简单的标记定义正则表达式。在这里,我已将字符串的内容定义为零或更多反斜杠引用或非引用字符的实例。

examples = [
  '"foo"',
  '"foo\"bar\""',
  'empty',
  'one more "time"',
  'the "last" man "standing"'
]

examples.each do |example|
  puts "%s => %s" % [ example, example.match(/\"(?:\\"|[^"])*?\"/) ]
end

您可以看到它在给出的各种示例中的表现。

作为关于解码日志文件内容的策略的说明,将验证作为一系列冗长乏味的if语句进行验证可能会严重影响性能。您可能希望广泛地对各种方法进行基准测试,以验证特定字段的内容。例如,在哈希中存储所有有效数字0.255的Fixnum等价物比运行.to_i然后在低值和高值之间进行比较可能更有效。

答案 1 :(得分:0)

首先,让我们将您的正则表达式分解成碎片,这样您就不必进行所有后验证

BYTE_RE = /(?:[012]?\d)?\d/
IP_RE = /#{BYTE_RE}(?:\.#{BYTE_RE}){3}/
DAY_RE = /0?[1-9]|[12]\d|3[01]/
MONTH_RE = /Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/
YEAR_RE = /\d{4}/
DATE_RE = %r!#{DAY_RE}/#{MONTH_RE}/#{YEAR_RE}!
HOUR_RE = /[01]?\d|2[0-3]/
MIN_RE = /[0-5]\d/
SEC_RE = MIN_RE
TIME_RE = /#{HOUR_RE}:#{MIN_RE}:#{SEC_RE}\s-400/
DATETIME_RE = /#{DATE_RE}:#{TIME_RE}/
STRING_RE = /"(?:\\.|[^\\"])*"/

logLine_regex = /^#{IP_RE} - (?:\w*|-) \[#{DATETIME_RE}\] #{STRING_RE} \d{4} (?:\d+|-)$/ 
isVal = lg.readlines.all? { |line| line =~ logLine_regex }

BYTE_RE只接受整数值为0-255的字符串,因此我们不必在之后验证。 这包括000,因此如果您想将其限制为没有前导零的数字,请将其更改为/\d|[1-9]\d|[12]\d\d/

DAY_RE只接受整数值为1-31的字符串。同样,如果要消除前导零,请使用/[1-9]|[12]\d|3[01]/。 在您的示例中无需验证年份 - 因为它恰好是四位数,它必须介于0到9999之间 包括的。我们可以对第14位做同样的事情,以避免验证。

HOUR_RE只接受整数值为0-23的字符串。不接受前导零会给/1?\d|2[0-3]/MIN_RESEC_RE 将接受的字符串限制为0到59之间的整数值。

然后,为了验证字符串,我们使用STRING_RE。我会打破这个。

  • " - 公开引用
  • (?:...) - 非捕获的parens,适合分组。
    • \\. - 任何反斜杠字母组合 - 匹配字符串转义符,例如\n\a\\\"
    • | - 上述模式或以下
    • [^\\"] - 除反斜杠或双引号外的任何字符
  • * - 前面原子的零个或多个
  • " - 关闭引用

所以这匹配一个开放的双引号,任意数量的转义字符或常规字符,然后是一个接近的引用。

由于开始^和关闭$,因此无需确保正则表达式检查的金额是整行。 主持人负责照顾。

因此我们在事后已经取消了所有验证。因为我们只想知道所有行是否匹配 给定的正则表达式,我们可以使用Enumerable#all?,它将返回true iff并且仅当所有行都匹配时 给定的正则表达式。另外,作为附带好处,如果任何返回错误,它将提前退出,这意味着这将运行 在这种情况下快一点。