Сопоставление всего слова (в стиле Visual Studio)

#regex

#регулярное выражение

Вопрос:

Я пытаюсь добавить поиск по совпадению всего слова в мое небольшое приложение. Я хочу, чтобы оно выполняло то же самое, что и Visual Studio. Так, например, приведенный ниже код должен работать нормально:

   public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            String input = "[  abc() *abc  ]";

            Match(input, "abc", 2);
            Match(input, "abc()", 1);
            Match(input, "*abc", 1);
            Match(input, "*abc ", 1);            
        }

        private void Match(String input, String pattern, int expected)
        {
            String escapedPattern = Regex.Escape(pattern);
            MatchCollection mc = Regex.Matches(input, @"b"   escapedPattern   @"b", RegexOptions.IgnoreCase);
            if (mc.Count != expected)
            {
                throw new Exception("match whole word isn't working");
            }
        }
    }   
  

Поиск по «abc» работает нормально, но другие шаблоны возвращают 0 результатов.
Я думаю, что b не подходит, но я не уверен, что использовать.

Будем признательны за любую помощь. Спасибо

Ответ №1:

b Метасимвол совпадает со словом-границей между буквенно-цифровым и не буквенно-цифровым символом. Строки, которые заканчиваются не буквенно-цифровыми символами, в конечном итоге не совпадают, поскольку b работает должным образом.

Чтобы выполнить правильное сопоставление всего слова, поддерживающее оба типа данных, вам необходимо:

  • используйте b перед или после любого буквенно — цифрового символа
  • используйте B (заглавную B ) перед или после любого не буквенно-цифрового символа
  • не использовать B , если первый или последний символ шаблона намеренно не является буквенно-цифровым символом, например, в вашем последнем примере с завершающим пробелом

На основе этих пунктов вам потребуется дополнительная логика для проверки входящего поискового запроса, чтобы сформировать его в соответствующий шаблон. B работает противоположным образом b . Если вы не используете B , то у вас могут неправильно получиться частичные совпадения. Например, слово foo*abc было бы неправильно сопоставлено с шаблоном @"*abcb" .

Чтобы продемонстрировать:

 string input = "[  abc() *abc foo*abc ]";
string[] patterns =
{
    @"babcb",     // 3
    @"babc()B", // 1
    @"B*abcb",   // 1, B prefix ensures whole word match, "foo*abc" not matched
    @"*abcb",     // 2, no B prefix so it matches "foo*abc"
    @"B*abc "     // 1
};

foreach (var pattern in patterns)
{
    Console.WriteLine("Pattern: "   pattern);
    var matches = Regex.Matches(input, pattern);
    Console.WriteLine("Matches found: "   matches.Count);
    foreach (Match match in matches)
    {
        Console.WriteLine("  "   match.Value);
    }
    Console.WriteLine();
}
  

Ответ №2:

Я думаю, это то, что вы ищете:

 @"(?<!w)"   escapedPattern   @"(?!w)"
  

b определяется в терминах наличия или отсутствия символов «word» как до, так и после текущей позиции. Вас волнует только то, что находится перед первым символом и что находится после последнего.

Ответ №3:

b Это утверждение нулевой ширины, которое соответствует символу word и символу, не являющемуся словом.

Буквы, цифры и подчеркивания являются символами word. * , ПРОБЕЛ и скобки в скобках не являются символами word. поэтому, когда вы используете b*abcb в качестве шаблона, он не соответствует вашему вводу, потому что * не является словом. Аналогично для вашего шаблона, включающего скобки.

Чтобы решить эту проблему, вам нужно будет устранить b в случаях, когда ваш шаблон ввода (неэкранированный) начинается или заканчивается символами, отличными от word.


     public void Run()
    {
        String input = "[  abc() *abc  ]";

        Match(input, @"babcb", 2);
        Match(input, @"babc()", 1);
        Match(input, @"*abcb", 1);
        Match(input, @"*abcb ", 1);
    }

    private void Match(String input, String pattern, int expected)
    {
        MatchCollection mc = Regex.Matches(input, pattern, RegexOptions.IgnoreCase);
        Console.WriteLine((mc.Count == expected)? "PASS ({0}=={1})" : "FAIL ({0}!={1})",
                          mc.Count, expected);
    }