Найти подстроку, игнорирующую указанные символы - PullRequest
6 голосов
/ 07 апреля 2010

Кто-нибудь из вас знает простой / понятный способ поиска подстроки в строке, игнорируя при этом некоторые указанные символы для ее поиска. Я думаю, что пример мог бы объяснить вещи лучше:

  • строка: «Привет, это строка»
  • подстрока для поиска:"Hello this"
  • символов для игнорирования:"," и "-"
  • найдена подстрока, результат:"Hello, -this"

Использование Regex не является обязательным для меня, но я добавил тег, потому что он кажется связанным.

Обновление:

Чтобы прояснить требование: мне нужна результирующая подстрока с игнорируемыми символами, а не просто указание на то, что данная подстрока существует.

Обновление 2: Некоторые из вас слишком много читают в примере, извините, я приведу другой сценарий, который должен работать:

  • строка:"? A & 3/3 / C) 412 &"
  • подстрока для поиска:"A41"
  • символов для игнорирования:"&", "/", "3", "C", ")"
  • найдено подстрока, результат:"A & 3/3 / C) 41"

И в качестве бонуса (сам по себе не требуется) было бы здорово, если бы также было небезопасно предполагать, что подстрока для поиска не будет содержать игнорируемых символов, например: учитывая последний Пример, который мы должны сделать:

  • подстрока для поиска:"A3C412 &"
  • символы для игнорирования:"&", "/", "3", "C", ")"
  • найдено подстрока, результат:"A & 3/3 / C) 412 &"

Извините, если я не был ясен раньше, или все же я не :)).

Обновление 3:

Спасибо всем, кто помог !, это реализация, с которой я сейчас работаю:

Вот несколько тестов:

Я использую некоторые нестандартные методы расширения, которые я не включаю, но я считаю, что они должны быть самоочевидными (я добавлю их, если хотите) Я взял много ваших идей для реализации и тестов, но я даю ответ @PierrOz, потому что он был одним из первых, и указал мне правильное направление. Не стесняйтесь давать предложения в качестве альтернативных решений или комментарии о текущем состоянии Impl. если хотите.

Ответы [ 8 ]

1 голос
/ 07 апреля 2010

Вот опция расширения строки без регулярных выражений:

public static class StringExtensions
{
    public static bool SubstringSearch(this string s, string value, char[] ignoreChars, out string result)
    {
        if (String.IsNullOrEmpty(value))
            throw new ArgumentException("Search value cannot be null or empty.", "value");

        bool found = false;
        int matches = 0;
        int startIndex = -1;
        int length = 0;

        for (int i = 0; i < s.Length && !found; i++)
        {
            if (startIndex == -1)
            {
                if (s[i] == value[0])
                {
                    startIndex = i;
                    ++matches;
                    ++length;
                }
            }
            else
            {
                if (s[i] == value[matches])
                {
                    ++matches;
                    ++length;
                }
                else if (ignoreChars != null && ignoreChars.Contains(s[i]))
                {
                    ++length;
                }
                else
                {
                    startIndex = -1;
                    matches = 0;
                    length = 0;
                }
            }

            found = (matches == value.Length);
        }

        if (found)
        {
            result = s.Substring(startIndex, length);
        }
        else
        {
            result = null;
        }
        return found;
    }
}
1 голос
/ 07 апреля 2010

РЕДАКТИРОВАТЬ: вот обновленное решение, касающееся точек в вашем недавнем обновлении. Идея та же, за исключением того, что если у вас есть одна подстрока, она должна будет вставить шаблон игнорирования между каждым символом. Если подстрока содержит пробелы, она разделяется на пробелы и вставляет шаблон игнорирования между этими словами. Если вам не нужна последняя функциональность (которая больше соответствовала вашему первоначальному вопросу), тогда вы можете удалить проверку Split и if, которая предоставляет этот шаблон.

Обратите внимание, что этот подход не будет наиболее эффективным.

string input = @"foo ?A&3/3/C)412& bar A341C2";
string substring = "A41";
string[] ignoredChars = { "&", "/", "3", "C", ")" };

// builds up the ignored pattern and ensures a dash char is placed at the end to avoid unintended ranges
string ignoredPattern = String.Concat("[",
                            String.Join("", ignoredChars.Where(c => c != "-")
                                                        .Select(c => Regex.Escape(c)).ToArray()),
                            (ignoredChars.Contains("-") ? "-" : ""),
                            "]*?");

string[] substrings = substring.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

string pattern = "";
if (substrings.Length > 1)
{
    pattern = String.Join(ignoredPattern, substrings);
}
else
{
    pattern = String.Join(ignoredPattern, substring.Select(c => c.ToString()).ToArray());
}

foreach (Match match in Regex.Matches(input, pattern))
{
    Console.WriteLine("Index: {0} -- Match: {1}", match.Index, match.Value);
}


Попробуйте это решение:
string input = "Hello, -this- is a string";
string[] searchStrings = { "Hello", "this" };
string pattern = String.Join(@"\W+", searchStrings);

foreach (Match match in Regex.Matches(input, pattern))
{
    Console.WriteLine(match.Value);
}

\W+ будет соответствовать любому не алфавитно-цифровому символу. Если вам нужно указать их самостоятельно, вы можете заменить его классом символов, которые нужно игнорировать, например [ ,.-]+ (всегда ставьте символ тире в начале или конце, чтобы избежать непреднамеренных спецификаций диапазона). Также, если вам нужно игнорировать регистр, используйте RegexOptions.IgnoreCase:

Regex.Matches(input, pattern, RegexOptions.IgnoreCase)

Если ваша подстрока находится в виде полной строки, такой как «Hello this», вы можете легко получить ее в виде массива для searchString следующим образом:

string[] searchString = substring.Split(new[] { ' ' },
                            StringSplitOptions.RemoveEmptyEntries);
1 голос
/ 07 апреля 2010

в вашем примере вы бы сделали:

string input = "Hello, -this-, is a string";
string ignore = "[-,]*";
Regex r = new Regex(string.Format("H{0}e{0}l{0}l{0}o{0} {0}t{0}h{0}i{0}s{0}", ignore));
Match m = r.Match(input);
return m.Success ? m.Value : string.Empty;

Динамически вы должны построить часть [-,] со всеми игнорируемыми символами и вставить эту часть между всеми символами вашего запроса.

Позаботьтесь о '-' в классе []: поместите его в начале или в конце

Так что в более общем смысле, это даст что-то вроде:

public string Test(string query, string input, char[] ignorelist)
{
    string ignorePattern = "[";
    for (int i=0; i<ignoreList.Length; i++)
    {
        if (ignoreList[i] == '-')
        {
            ignorePattern.Insert(1, "-");
        }
        else
        {
            ignorePattern += ignoreList[i];
        }
    }

    ignorePattern += "]*";

    for (int i = 0; i < query.Length; i++)
    {
        pattern += query[0] + ignorepattern;
    }

    Regex r = new Regex(pattern);
    Match m = r.Match(input);
    return m.IsSuccess ? m.Value : string.Empty;
}
0 голосов
/ 07 апреля 2010

Вы всегда можете использовать комбинацию RegEx и поиска строки

public class RegExpression {

  public static void Example(string input, string ignore, string find)
  {
     string output = string.Format("Input: {1}{0}Ignore: {2}{0}Find: {3}{0}{0}", Environment.NewLine, input, ignore, find);
     if (SanitizeText(input, ignore).ToString().Contains(SanitizeText(find, ignore)))
        Console.WriteLine(output + "was matched");
     else
        Console.WriteLine(output + "was NOT matched");
     Console.WriteLine();
  }

  public static string SanitizeText(string input, string ignore)
  {
     Regex reg = new Regex("[^" + ignore + "]");
     StringBuilder newInput = new StringBuilder();
     foreach (Match m in reg.Matches(input))
     {
        newInput.Append(m.Value);
     }
     return newInput.ToString();
  }

}

Использование будет похоже на

RegExpression.Example("Hello, -this- is a string", "-,", "Hello this");  //Should match
RegExpression.Example("Hello, -this- is a string", "-,", "Hello this2"); //Should not match
RegExpression.Example("?A&3/3/C)412&", "&/3C\\)", "A41"); // Should match
RegExpression.Example("?A&3/3/C) 412&", "&/3C\\)", "A41"); // Should not match
RegExpression.Example("?A&3/3/C)412&", "&/3C\\)", "A3C412&"); // Should match

выход

Ввод: Hello, -this- это строка Игнорировать: -, Найти: Привет это

было найдено

Ввод: Hello, -this- это строка Игнорировать: -, Найти: Hello this2

НЕ было найдено

Ввод:? A & 3/3 / C) 412 & Игнорировать: & / 3C) Найти: A41

было найдено

Ввод:? A & 3/3 / C) 412 & Игнорировать: & / 3C) Найти: A41

НЕ было найдено

Ввод:? A & 3/3 / C) 412 & Игнорировать: & / 3C) Найти: A3C412 &

было найдено

0 голосов
/ 07 апреля 2010

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

string1 - ваша строка, которую вы хотите просмотреть

//Create a List(Of string) that contains the ignored characters'
List<string> ignoredCharacters = new List<string>();

//Add all of the characters you wish to ignore in the method you choose

//Use a function here to get a return

public bool subStringExist(List<string> ignoredCharacters, string myString, string toMatch)
{
    //Copy Your string to a temp

    string tempString = myString;
    bool match = false;

    //Replace Everything that you don't want

    foreach (string item in ignoredCharacters)
    {
        tempString = tempString.Replace(item, "");
    }

    //Check if your substring exist
    if (tempString.Contains(toMatch))
    {
        match = true;
    }
    return match;
}
0 голосов
/ 07 апреля 2010

Вот нерегулярный способ сделать это с помощью разбора строки.

    private string GetSubstring()

    {
        string searchString = "Hello, -this- is a string";
        string searchStringWithoutUnwantedChars = searchString.Replace(",", "").Replace("-", "");

        string desiredString = string.Empty;
        if(searchStringWithoutUnwantedChars.Contains("Hello this"))
            desiredString = searchString.Substring(searchString.IndexOf("Hello"), searchString.IndexOf("this") + 4);

        return desiredString;
    }
0 голосов
/ 07 апреля 2010

Вы можете сделать это с одним регулярным выражением, но это будет довольно утомительно, так как после каждого персонажа вам нужно будет проверять ноль или более игнорируемых символов.Вероятно, легче удалить все игнорируемые символы с помощью Regex.Replace(subject, "[-,]", "");, а затем проверить, есть ли подстрока.

Или единственный способ регулярного выражения

Regex.IsMatch(subject, "H[-,]*e[-,]*l[-,]*l[-,]*o[-,]* [-,]*t[-,]*h[-,]*i[-,]*s[-,]*")
0 голосов
/ 07 апреля 2010

Этот код будет делать то, что вы хотите, хотя я предлагаю вам изменить его под свои нужды:

string resultString = null;

try
{
    resultString = Regex.Match(subjectString, "Hello[, -]*this", RegexOptions.IgnoreCase).Value;
}
catch (ArgumentException ex)
{
    // Syntax error in the regular expression
}
...