Избавление от дополнительных двойных кавычек в строке запроса веб-журнала - PullRequest
0 голосов
/ 09 июня 2009

Я пытаюсь написать программу ruby, которая анализирует веб-журнал и проверяет правильность каждой его части. Я пытаюсь разобраться со случаем в строке запроса журнала, у него есть дополнительные двойные кавычки, кроме начальных и конечных. Я сделал веб-журнал в форме регулярного выражения, потому что его легче читать, создавая переменные для каждой части. Вот что я имею до сих пор:

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 ]

1 голос
/ 09 июня 2009

Простой подход заключается в определении вашего регулярного выражения в терминах простых токенов. Здесь я определил содержимое строки равным нулю или более экземпляров обратной кавычки или не кавычки.

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

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

Вы можете увидеть, как это работает на различных приведенных примерах.

В качестве заметки о вашей стратегии декодирования содержимого файла журнала, выполнение проверки в виде серии длинных и утомительных операторов if может привести к серьезному снижению производительности. Возможно, вы захотите тщательно сравнить различные подходы к проверке содержимого конкретных полей. Например, может быть более эффективно хранить эквиваленты Fixnum всех действительных чисел 0,255 в хэше, чем запускать .to_i, а затем выполнять сравнение между низким и высоким значением.

0 голосов
/ 10 июня 2009

Прежде всего, давайте разберем ваше регулярное выражение на части, чтобы вам не приходилось делать всю эту пост-проверку

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_RE и SEC_RE ограничьте количество принятых строк целочисленными значениями от 0 до 59.

Затем для проверки строки мы используем STRING_RE. Я сломаю это.

  • " - открытая котировка
  • (?:...) - не захватывающие парены, хорошие для группировки.
    • \\. - любая комбинация обратной косой черты и буквы - соответствует экранированию строки, например \n, \a, \\ или \"
    • | - либо предыдущий шаблон, либо следующий
    • [^\\"] - любой символ, кроме обратной косой черты или двойной кавычки
  • * - ноль или более предшествующего атома
  • " - закрывающая цитата

Таким образом, это соответствует открытой двойной кавычке, любому количеству экранированных или обычных символов, а затем закрывающей кавычке.

Нет необходимости проверять, чтобы сумма, проверяемая регулярным выражением, была всей строкой, начиная с начала ^ и заканчивая $ якоря позаботятся об этом.

Итак, мы удалили всю вашу проверку после факта. Так как мы хотим знать, совпадают ли все строки данное регулярное выражение, мы можем использовать Enumerable#all?, который будет возвращать true тогда и только тогда, когда все строки совпадают данное регулярное выражение. Кроме того, в качестве дополнительного преимущества, он выйдет досрочно, если любой из них вернет false, что означает, что это будет работать немного быстрее в этом случае.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...