AkelPad Forum Index AkelPad
Support forum
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Регулярные выражения
Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9, 10  Next
 
Post new topic   Reply to topic    AkelPad Forum Index -> Discussion (Russian)
View previous topic :: View next topic  
Author Message
Instructor
Site Admin


Joined: 06 Jul 2006
Posts: 6223

PostPosted: Tue Aug 30, 2016 5:35 am    Post subject: Reply with quote

DV
Для примера - как найти?
Code:
.*

или
Code:
[^\n]*
Back to top
View user's profile Send private message Send e-mail
YuS



Joined: 15 Sep 2013
Posts: 422

PostPosted: Tue Aug 30, 2016 7:26 am    Post subject: Reply with quote

DV wrote:

Поясняю свою мысль картинкой:

Ваша мысль, как раз, понятна, но это ведь, всё равно, тот же поиск вперед только в определенных, ограниченных пределах, а здесь, как уже сказал Instructor:
Instructor wrote:

Сложность не в определении области цикла, а в самом механизме сравнения.

К тому же, посчитайте сколько раз отработает парсер при таком поиске? Ведь каждая строка, кроме первой и последней будет парситься дважды (плюс ещё механизм сравнения с точками возврата), в отличие от одного прохода с начала текста и до курсора...
В чём тут плюс? Если искомое вхождение находится рядом с курсором - да, поиск будет более быстрым, но ведь это необязательно во всех случаях, искомое может находиться и в самом начале.
Кроме того, при реализации такого поиска придется ломать общую методику работы регэкспов, т.к. уже не будет работать многострочный поиск, как минимум и к тому же придется выключать из поиска подстроки неопределенного размера (квантификаторы *, + и т.п.). Стоит ли овчинка выделки?
Back to top
View user's profile Send private message
DV



Joined: 16 Nov 2006
Posts: 990
Location: Kyiv, Ukraine

PostPosted: Tue Aug 30, 2016 7:52 am    Post subject: Reply with quote

YuS wrote:
Ваша мысль, как раз, понятна, но это ведь тот же поиск только в определенных, ограниченных пределах

Напоминаю, с чего я всё это начал:
Code:

if (IsVariableMultiLineRegEx(regEx))
{
  // ищем от начала файла - как сейчас
}
else
{
  // ищем назад (снизу вверх)
}

Другими словами, когда регулярка такова, что нельзя определить, со сколькими строками текста она может совпасть, то, несомненно, текущий подход к поиску является максимально правильным.
В том же случае, когда количество строк текста, с которыми совпадёт регулярное выражение, известно заранее - то есть является константой, полученной путём простого анализа регулярного выражения - то наиболее оптимальным будет предложенный мной поиск снизу вверх, потому что он будет просматривать не весь документ сразу, а будет просматривать его как бы "узкими окнами" от конца к началу.

P.S.
Если на практике окажется, что для константно-многострочных регулярок поиск снизу вверх окажется медленнее, чем поиск от начала документа вниз, условие в IsVariableMultiLineRegEx всегда можно будет подкорректировать. Как минимум, однострочные регулярки при поиске снизу вверх точно дадут куда лучший результат, чем при поиске от начала документа вниз. Что же касается двух и более -строчных регулярок, тут нужно проверить на практике. Полагаю, с двухстрочной регуляркой, даже если она совпадёт только с самой первой строкой в документе (а это худший случай для поиска снизу вверх), время поиска снизу вверх не будет сильно отличаться от поиска с начала документа вниз. Полагаю, критерием для сравнения можно выбрать совпадение посередине документа. При схожих временах поиска я бы отдал предпочтение поиску снизу вверх в таком случае - поскольку если уж пользователь ищет снизу вверх, то он же предполагает, что искомая строка находится ближе к текущей строке, чем к началу документа, не так ли?

Функция анализа регулярного выражения будет выглядеть как-то так:
AnalyzeRegExp.c
Code:
// TODO: consider "(\n|abc)*" situations

// returns max lines that the cszRegExp matches
// -1 means unpredictable multi line match (e.g. "[\n]+")
int AnalyzeRegExp(const wchar_t* cszRegExp, int isDotMatchingNewLine)
{
    enum eStateFlags {
        reNormal           = 0,
        reEscapedJustFound = 0x0001, // just encountered '\'
        reEscaped          = 0x0002, // current char is escaped (after '\')
        reCharSet          = 0x0010, // inside [...]
        reNewLineJustFound = 0x0100, // just encountered '\n' or '\r' (or '.')
        reNewLineChar      = 0x0200, // previous char is a new line char
        reNewLineInCharSet = 0x0400, // a new line char inside [...]
        reDone             = 0x8000  // done
    };
   
    int nRegExpLines = 1;
    unsigned int state = reNormal;

    for (; state != reDone; ++cszRegExp)
    {
        const wchar_t ch = *cszRegExp;
        if (ch == 0)
        {
            state = reDone;
        }
        else if (ch == L'\\')
        {
            if (!(state & reEscaped))
                state |= reEscapedJustFound;
        }
        else if (ch == L'n' || ch == L'r')
        {
            if (state & reEscaped)
                state |= reNewLineJustFound;
        }
        else if (!(state & reEscaped))
        {
            if (ch == L'[')
            {
                state |= reCharSet;
            }
            else if (ch == L']')
            {
                if (state & reCharSet)
                {
                    state ^= reCharSet;
                    if (state & reNewLineInCharSet)
                    {
                        state ^= reNewLineInCharSet;
                        state |= reNewLineJustFound;
                    }
                }
            }
            else if (ch == L'+' || ch == L'*')
            {
                if (state & reNewLineChar)
                {
                    nRegExpLines = -1;
                    state = reDone;
                }
            }
            else if (ch == L'.')
            {
                if (isDotMatchingNewLine)
                    state |= reNewLineJustFound;
            }
        }

        if (state & reNewLineJustFound)
        {
            state ^= reNewLineJustFound;
            if (state & reCharSet)
            {
                state |= reNewLineInCharSet;
            }
            else
            {
                state |= reNewLineChar;
                ++nRegExpLines;
            }
        }
        else if (state & reNewLineChar)
            state ^= reNewLineChar;

        if (state & reEscapedJustFound)
        {
            state ^= reEscapedJustFound;
            state |= reEscaped;
        }
        else if (state & reEscaped)
            state ^= reEscaped;
    }

    return nRegExpLines;
}
Back to top
View user's profile Send private message
DV



Joined: 16 Nov 2006
Posts: 990
Location: Kyiv, Ukraine

PostPosted: Thu Sep 01, 2016 8:53 am    Post subject: Reply with quote

Что-то реализация функции IsVariableMultiLineRegEx на практике перестаёт казаться мне удачной идеей. При написании такой функции с нуля я остановился на группировке () и альтернативах |. Обработка и того, и другого попахивает либо рекурсией, либо вложенными циклами, что, хм, всё усложняет. К тому же я не уверен, что не осталось других синтаксических конструкций, которые тоже надо учесть.
Так что если достоверную информацию о максимальном количестве строк, с которыми совпадёт регулярка, нельзя извлечь после PatCompile, то идею с IsVariableMultiLineRegEx придётся отложить.
Но у меня есть другая гениальная идея.
Почему бы при поиске с регулярками снизу вверх не обрабатывать большие файлы по частям? Грубо говоря, делим файл, к примеру, на 10 частей - и сначала ищем в последней части снизу, затем в предпоследней (в случае мультистрочного поиска захватывая и строки из частей, находящихся ниже, если это необходимо) и т.д.? При этом при каждом переходе на часть, находящуюся выше, мы запоминаем позицию начала предыдущей части, чтобы первая строка текста, совпавшая с регуляркой, никогда не была ниже позиции начала предыдущей, уже обработанной части.
Говоря проще, при переходе от одной части к другой мы как будто смещаем начало документа всё выше. А поскольку мы каждый раз запоминаем позицию начала предыдущей части, то мы не повторяем поиск в уже обработанной ранее части документа.
Важное примечание: это будет работать при отключенной настройке ". совпадает с \n". Если же эта настройка включена, то элементарное ".+" совпадёт со всем документом, и поэтому должен использоваться поиск от начала документа, как сейчас. А с другой стороны, если в регулярке не встречается ни .+, ни .*, ни \n или \r, то какой смысл в поиске от начала документа? В этом случае лишь пустая трата времени.

Как вариант, можно взять приведенную выше функцию AnalyzeRegExp, и использовать поиск снизу вверх только если она возвращает 1. При этом саму функцию можно упростить, поскольку мы уже не ставим перед собой цель цель подсчитать точное количество строк, с которым может совпасть регулярка, а лишь проверяем её на наличие конструкций, которые делают её неоднострочной.
Это уже вторая гениальная идея за сегодня Smile
Back to top
View user's profile Send private message
F. Phoenix



Joined: 24 Dec 2011
Posts: 175

PostPosted: Sun Nov 27, 2016 10:08 pm    Post subject: Reply with quote

Как в coder-файле проверить кол-во символов на четность?

Составил регулярку для подсветки строк между кавычками с учетом экранирования (нужно именно через QuotesRE):
Code:
1   `(")("|.*[^\\]")`                `\0=(0,${STR},0)`

и все бы ничего, да глючит, к примеру, при двух слешах перед закрывающей кавычкой: "блаблабла\\".
Back to top
View user's profile Send private message
YuS



Joined: 15 Sep 2013
Posts: 422

PostPosted: Mon Nov 28, 2016 6:27 am    Post subject: Reply with quote

F. Phoenix wrote:
Как в coder-файле проверить кол-во символов на четность?

Посчитать символы и разделить на 2?

F. Phoenix wrote:

Составил регулярку для подсветки строк между кавычками с учетом экранирования (нужно именно через QuotesRE):
Code:
1   `(")("|.*[^\\]")`                `\0=(0,${STR},0)`

и все бы ничего, да глючит, к примеру, при двух слешах перед закрывающей кавычкой: "блаблабла\\".

QoutesRE работает построчно, т.е. проверка идет по очереди в каждой строке.
Регулярка означает следующее:
Code:
Совпадает если:
присутствует символ двойная кавычка '"', за ним сразу следует либо ещё одна кавычка '"', либо несколько любых символов (квантификатор жадный, т.е. захватывается максимально возможная строка) и любой символ кроме обратного слеш '\', за которым сразу следует двойная кавычка '"'.

Т.е. непонятно определение "глючит" - что не так-то?
У меня всё подсвечивает так, как написано в регэкспе.
Back to top
View user's profile Send private message
F. Phoenix



Joined: 24 Dec 2011
Posts: 175

PostPosted: Mon Nov 28, 2016 9:07 am    Post subject: Reply with quote

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

К примеру, вывод строки на консоль в C#:
Code:
Console.WriteLine("Ошибка при открытии файла \"C:\\MyFile.txt\"");

Выведет фразу:
Quote:
Ошибка при открытии файла "C:\MyFile.txt"

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

Такое правило без регулярок работает прекрасно:
Code:
Quotes:
;=======================================================
;Флаги  Стиль   Цвет    Цвет   Начало     Конец   Символ
;       шрифта  текста  фона   диапазона  диапаз. экран.
;=======================================================
0       0       ${STR}  0      `"`        `"`     `\`


Но мне нужен аналог для секции QuotesRE.
Да никакой ошибки в обработке выражения-то нет. Проблема в составлении.
Back to top
View user's profile Send private message
F. Phoenix



Joined: 24 Dec 2011
Posts: 175

PostPosted: Mon Nov 28, 2016 9:45 am    Post subject: Reply with quote

Похоже, нашел выход:
Code:
"([^\\]|\\.)*?"
Back to top
View user's profile Send private message
Drugmix



Joined: 08 Apr 2013
Posts: 582
Location: Win7SP1x64, APx64

PostPosted: Mon Nov 28, 2016 11:12 am    Post subject: Reply with quote

del.

Last edited by Drugmix on Tue Nov 29, 2016 9:57 am; edited 1 time in total
Back to top
View user's profile Send private message
YuS



Joined: 15 Sep 2013
Posts: 422

PostPosted: Mon Nov 28, 2016 11:20 am    Post subject: Reply with quote

F. Phoenix wrote:

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

А, это уже более понятно, а то ведь подумалось, что баг в AP...

F. Phoenix wrote:

Но мне нужен аналог для секции QuotesRE.
Да никакой ошибки в обработке выражения-то нет. Проблема в составлении.

Да, тогда надо подумать над шаблоном.

Этот хорош:
Code:
"([^\\]|\\.)*?"

но, вот тестовый массив, на котором этот шаблон не вполне правильно работает:
Code:
"тест" проверка тест\\" тест
"тест" проверка тест\\" тест тест
"тест" проверка "тест\\" тест тест"
abc\"тест" проверка "тест\" тест "тест"
abc\\"тест" проверка "тест\\" тест "тест"
abc\\\"тест" проверка "тест\\\" тест "тест"
abc\\\\"тест" проверка "тест\\\\" тест "тест"
abc\\\\\"тест" проверка "тест\\\\\" тест "тест"
abc\\\\\\"тест" проверка "тест\\\\\\" тест "тест"


Предлагаю вот такой:
Code:
1   `(?<=[^\\])(?:(\\)\1)*\K"([^\\]|\\.)*?"`   `\0=(0,${STR},0)`
Back to top
View user's profile Send private message
F. Phoenix



Joined: 24 Dec 2011
Posts: 175

PostPosted: Mon Nov 28, 2016 12:03 pm    Post subject: Reply with quote

Ну, такой шаблон по поведению ближе к тому, что я написал для секции Quotes, но единственная разница между ними, как я понимаю, это считать ли открывающую кавычку экранированной, если бекслеш стоит перед ней.

Склоняюсь к варианту, что в целом не считать, т.к. свойство символа экранирования бекслеш обретает внутри строк, а за их пределами может работать, к примеру, в качестве оператора. Хотя это зависит от конкректного синтаксиса. Но в моем случае (синтаксис Gettext) бекслеш перед открывающей кавычкой вообще недопустим.
Back to top
View user's profile Send private message
Drugmix



Joined: 08 Apr 2013
Posts: 582
Location: Win7SP1x64, APx64

PostPosted: Mon Nov 28, 2016 6:06 pm    Post subject: Reply with quote

del

Last edited by Drugmix on Tue Nov 29, 2016 9:57 am; edited 1 time in total
Back to top
View user's profile Send private message
YuS



Joined: 15 Sep 2013
Posts: 422

PostPosted: Mon Nov 28, 2016 6:24 pm    Post subject: Reply with quote

F. Phoenix wrote:
Ну, такой шаблон по поведению ближе к тому, что я написал для секции Quotes, но единственная разница между ними, как я понимаю, это считать ли открывающую кавычку экранированной, если бекслеш стоит перед ней.

Не только. Это ещё и ответ на вопрос об определении четности символов, который был задан выше.
И если я правильно понял, то, как раз, и нужна была реализация работы на регэкспах близкая к возможностям Quotes. Полного соответствия получить, естественно, невозможно, в силу особенностей работы QuotesRE, но получилось то, что получилось. Smile

F. Phoenix wrote:
Но в моем случае (синтаксис Gettext) бекслеш перед открывающей кавычкой вообще недопустим.

Правила правилами, а вероятность ошибок никто не отменял...
Code:
"тест" проверка тест\\\"тест тест"

В общем, мое дело было предложить...


Last edited by YuS on Mon Nov 28, 2016 6:29 pm; edited 1 time in total
Back to top
View user's profile Send private message
YuS



Joined: 15 Sep 2013
Posts: 422

PostPosted: Mon Nov 28, 2016 6:27 pm    Post subject: Reply with quote

Drugmix wrote:

Внутри диапазонов экранировать символы не надо.

Понимаю, что понедельник - день тяжелый, но дублировать посты не требуется.
Вы просто попробуйте не экранировать бакслеш... а потом уже отписывайтесь, что не надо. Smile
Back to top
View user's profile Send private message
F. Phoenix



Joined: 24 Dec 2011
Posts: 175

PostPosted: Mon Nov 28, 2016 8:45 pm    Post subject: Reply with quote

Code:
Code:
"тест" проверка тест\\\"тест тест"

Вторая строка подсвечивается с шаблоном
Code:
"([^\\]|\\.)*?"

но не подсвечивается с
Code:
(?<=[^\\])(?:(\\)\1)*\K"([^\\]|\\.)*?"

и Quotes: `"` `"` `\`
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AkelPad Forum Index -> Discussion (Russian) All times are GMT
Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9, 10  Next
Page 8 of 10

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


SourceForge.net Logo Powered by phpBB © 2001, 2005 phpBB Group