string.IndexOf поиск совпадения всего слова

Я ищу способ поиска строки для точного совпадения или совпадения всего слова. RegEx.Match и RegEx.IsMatch, кажется, не приводят меня туда, где я хочу быть.
Рассмотрите следующий сценарий:

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            string str = "SUBTOTAL 34.37 TAX TOTAL 37.43";
            int indx = str.IndexOf("TOTAL");
            string amount = str.Substring(indx + "TOTAL".Length, 10);
            string strAmount = Regex.Replace(amount, "[^.0-9]", "");

            Console.WriteLine(strAmount);
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }
    }
}

Вывод приведенного выше кода:

// 34.37
// Press any key to continue...

Проблема в том, что мне не нужен SUBTOTAL, но IndexOf находит первое вхождение слова TOTAL, которое находится в SUBTOTAL, что затем дает неверное значение 34,37.

Итак, вопрос в том, есть ли способ заставить IndexOf найти только точное совпадение или есть другой способ заставить это точное совпадение всего слова, чтобы я мог найти индекс этого точного совпадения, а затем выполнить с ним какую-то полезную функцию. RegEx.IsMatch и RegEx.Match, насколько я могу судить, просто boolean поиски. В этом случае недостаточно просто знать, что точное совпадение существует. Мне нужно знать, где он находится в строке.

Любой совет будет принят во внимание.


person D J    schedule 26.06.2014    source источник
comment
str.IndexOf(" TOTAL "); Но это некрасиво.   -  person Bun    schedule 26.06.2014


Ответы (6)


Вы можете использовать регулярное выражение

string str = "SUBTOTAL 34.37 TAX TOTAL 37.43";
var indx = Regex.Match(str, @"\WTOTAL\W").Index; // will be 18
person L.B    schedule 26.06.2014
comment
Спасибо! Это намного чище! Кто знал, что .Index висит на RegEx.Match? :) :) :) - person D J; 26.06.2014
comment
Немного назад в этом ответе был пост с использованием шаблона RegEx, который возвращал число, следующее за точным совпадением для TOTAL. Кто-нибудь еще видел это? Кто-нибудь хочет взвесить такой образец? - person D J; 26.06.2014
comment
@DJ Вы ищете что-то вроде var val = Regex.Match(str, @"\WTOTAL\W\s*([0-9\.]+)").Groups[1].Value; - person L.B; 26.06.2014
comment
ВАУ! Я должен узнать больше о RegEx. Это кажется очень мощным, хотя и не очень интуитивно понятным. Спасибо ЛБ! - person D J; 26.06.2014

Мой метод быстрее, чем принятый ответ, потому что он не использует Regex.

string str = "SUBTOTAL 34.37 TAX TOTAL 37.43";
var indx = str.IndexOfWholeWord("TOTAL");

public static int IndexOfWholeWord(this string str, string word)
{
    for (int j = 0; j < str.Length && 
        (j = str.IndexOf(word, j, StringComparison.Ordinal)) >= 0; j++)
        if ((j == 0 || !char.IsLetterOrDigit(str, j - 1)) && 
            (j + word.Length == str.Length || !char.IsLetterOrDigit(str, j + word.Length)))
            return j;
    return -1;
}
person palota    schedule 04.12.2017
comment
Это также более гибко, так как возвращает -1, если в строке НЕТ ИТОГО. Regex выше возвращает 0. - person brenth; 14.08.2019

Вы можете использовать границы слов, \b и Match.Index свойство:

var text = "SUBTOTAL 34.37 TAX TOTAL 37.43";
var idx = Regex.Match(text, @"\bTOTAL\b").Index;
// => 19

См. демонстрацию C#.

\bTOTAL\b соответствует TOTAL, если оно не заключено ни в какие другие буквы, цифры или символы подчеркивания.

Если вам нужно посчитать слово как целое слово, если оно заключено в символы подчеркивания, используйте

var idx = Regex.Match(text, @"(?<![^\W_])TOTAL(?![^\W_])").Index;

где (?<![^\W_]) — это отрицательный поиск назад, который не соответствует, если есть символ, отличный от не слова, и подчеркивание сразу слева от текущего местоположения (таким образом, может быть начальная позиция строки или символ, который не является ни цифра, ни буква), а (?![^\W_]) — аналогичный отрицательный просмотр вперед, который соответствует только в том случае, если есть позиция конца строки или символ, отличный от буквы или цифры, непосредственно справа от текущего местоположения.

Если границы являются пробелами или началом/концом строки, используйте

var idx = Regex.Match(text, @"(?<!\S)TOTAL(?!\S)").Index;

где (?<!\S) требует начала строки или пробела сразу слева, а (?!\S) требует конца строки или пробела справа.

ПРИМЕЧАНИЕ: \b, (?<!...) и (?!...) являются непотребляющие шаблоны, то есть индекс регулярного выражения не продвигается вперед при сопоставлении с этими шаблонами, таким образом, вы получаете точные позиции искомого слова.

person Wiktor Stribiżew    schedule 03.12.2020

Чтобы сделать принятый ответ немного безопаснее (поскольку IndexOf возвращает -1 для несоответствия):

string pattern = String.Format(@"\b{0}\b", findTxt);
Match mtc = Regex.Match(queryTxt, pattern);
if (mtc.Success)
{
    return mtc.Index;
}
else
    return -1;
person bir yaz    schedule 17.02.2021

Хотя это может быть хак, который работает только для вашего примера, попробуйте

string amount = str.Substring(indx + " TOTAL".Length, 10);

давая дополнительный пробел перед итогом. Так как это не произойдет с SUBTOTAL, он должен пропустить слово, которое вам не нужно, и просто искать изолированное TOTAL.

person krodmannix    schedule 26.06.2014
comment
РЖУ НЕ МОГУ!!! Почему я этого не видел! Это немного хакерски, но только для моего примера это должно работать. Мне бы очень хотелось посмотреть, есть ли способ заставить все слова соответствовать более чистому подходу, но я отмечу это как ответ, если я не увижу более точный ответ через день или около того. БОЛЬШОЕ СПАСИБО!!! :) - person D J; 26.06.2014

Я бы порекомендовал решение Regex от L.B. тоже, но если вы не можете использовать Regex, вы можете использовать String.LastIndexOf("TOTAL"). Предполагая, что ИТОГО всегда идет после ПРОМЕЖУТОЧНОГО?

http://msdn.microsoft.com/en-us/library/system.string.lastindexof%28v=vs.110%29.aspx

person Khôi    schedule 26.06.2014