Regex заменяет все, кроме определенного шаблона
Я хочу извлечь:
50%
Из строки, которая будет иметь более или менее этот формат:
The 50% is in here somewhere.
Я также хотел бы извлечь:
50%50%25%
Из строки, подобной этой:
50% of 50% is 25%
Regex.Match()
кажется очевидным соперником. Однако это включает проверку наличия совпадений (например, match.Success
), извлечение результатов из определенного индекса в массиве и/или риск обращения к индексу вне границ.
Замена Regex обычно проще применять. Одна строка выполняет задание, включая возврат результирующей строки. Это верно для многих языков.
result = Regex.Replace(input, stuffWeDontLike, "")
В принципе, я ищу фильтр регулярных выражений - вместо ввода заменяемого шаблона, я хочу ввести шаблон для извлечения.
percentages = Regex.Filter("50% of 50% is 25%", "[0-9]+\%")
Можно ли сформировать регулярное выражение и инвертировать результат, как если бы это был выбор? Это позволило бы использовать регулярное выражение. Однако я не мог найти способ легко инвертировать регулярное выражение.
Как мы можем достичь желаемого результата (или похожего, соединение или так кажется приемлемым) с очень коротким и простым синтаксисом, похожим на regex replace?
Ответы
Ответ 1
Вы можете использовать Regex.Matches
и объединить результат каждого совпадения. Просто выберите тот, который вам больше нравится.
//Sadly, we can't extend the Regex class
public class RegExp
{
//usage : RegExp.Filter("50% of 50% is 25%", @"[0-9]+\%")
public static string Filter(string input, string pattern)
{
return Regex.Matches(input, pattern).Cast<Match>()
.Aggregate(string.Empty, (a,m) => a += m.Value);
}
}
public static class StringExtension
{
//usage : "50% of 50% is 25%".Filter(@"[0-9]+\%")
public static string Filter(this string input, string pattern)
{
return Regex.Matches(input, pattern).Cast<Match>()
.Aggregate(string.Empty, (a,m) => a += m.Value);
}
}
Ответ 2
Я не понимаю ваших аргументов, почему вы хотите использовать замену. Зачем идти в первую очередь? В классе Regex
есть методы, которые позволяют точно получить все необходимые совпадения. Ваш окольный путь при достижении вашего решения я считаю бессмысленным.
Просто используйте Matches()
, чтобы собрать совпадения. Затем вы можете присоединиться к ним в строку, которую вы хотели.
var str = "50% of 50% is 25%";
var re = new Regex(@"\d+%");
var ms = re.Matches(str);
var values = ms.Cast<Match>().Select(m => m.Value);
var joined = String.Join("", values); // "50%50%25%"
Ответ 3
Одним из решений является замена regex следующим образом:
Regex.Replace("50% of 50% is 25%", "(\d+\%)|(?:.+?)", "$1");
Вывод:
50%50%25%
Как общий подход:
Regex.Replace(input, (pattern)|(?:.+?), "$1");
Это находит все, что соответствует одному из следующих:
- Шаблон. Захвачено как
$1
. Это то, что мы хотим сохранить.
- Любой персонаж, любое количество раз, но не жадный. Это находит все, что не было захвачено первой группой.
?:
, потому что нам не нужно захватывать эту группу.
Как заявляет MSDN: "$1
заменяет весь матч первым захваченным подвыражением". (То есть все совпадения для этой подстроки конкатенированы.)
Эффективно это описанный фильтр регулярных выражений.