Получить строку между двумя строками в строке


105

У меня есть строка вроде:

"super exemple of string key : text I want to keep - end of my string"

Я хочу просто сохранить строку между "key : "и " - ". Как я могу это сделать? Должен ли я использовать регулярное выражение или я могу сделать это по-другому?


2
use substringandindexof
Sayse

Получить строку после определенной строки в строке и перед другой конкретной строкой, которая также содержится в строке, где находится первая строка ..
Кен Кин,

Ответы:


165

Возможно, хороший способ - просто вырезать подстроку :

String St = "super exemple of string key : text I want to keep - end of my string";

int pFrom = St.IndexOf("key : ") + "key : ".Length;
int pTo = St.LastIndexOf(" - ");

String result = St.Substring(pFrom, pTo - pFrom);

37
string input = "super exemple of string key : text I want to keep - end of my string";
var match = Regex.Match(input, @"key : (.+?)-").Groups[1].Value;

или с помощью только строковых операций

var start = input.IndexOf("key : ") + 6;
var match2 = input.Substring(start, input.IndexOf("-") - start);

29

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

 input.Split(new string[] {"key :"},StringSplitOptions.None)[1]
      .Split('-')[0]
      .Trim();

6
Это создаст в памяти несколько ненужных строк. Не используйте это, если вам небезразлична память.
Mikael Dúi Bolinder

14

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

public static class StringExtensions {
    /// <summary>
    /// takes a substring between two anchor strings (or the end of the string if that anchor is null)
    /// </summary>
    /// <param name="this">a string</param>
    /// <param name="from">an optional string to search after</param>
    /// <param name="until">an optional string to search before</param>
    /// <param name="comparison">an optional comparison for the search</param>
    /// <returns>a substring based on the search</returns>
    public static string Substring(this string @this, string from = null, string until = null, StringComparison comparison = StringComparison.InvariantCulture)
    {
        var fromLength = (from ?? string.Empty).Length;
        var startIndex = !string.IsNullOrEmpty(from) 
            ? @this.IndexOf(from, comparison) + fromLength
            : 0;

        if (startIndex < fromLength) { throw new ArgumentException("from: Failed to find an instance of the first anchor"); }

            var endIndex = !string.IsNullOrEmpty(until) 
            ? @this.IndexOf(until, startIndex, comparison) 
            : @this.Length;

        if (endIndex < 0) { throw new ArgumentException("until: Failed to find an instance of the last anchor"); }

        var subString = @this.Substring(startIndex, endIndex - startIndex);
        return subString;
    }
}

// usage:
var between = "a - to keep x more stuff".Substring(from: "-", until: "x");
// returns " to keep "

Я использовал ваш код, но обнаружил небольшую ошибку при @ this.IndexOf (до, startIndex + fromLength, сравнение) из строк типа «AB», где A - from, а B - до, поэтому я удалил + fromLength. Я не проверял это глубоко, хотя
Адриан Ифтоде

1
@AdrianIftode: хороший звонок. Это определенно была ошибка. Имеет смысл начать поиск второго якоря с startIndex, поскольку он уже прошел за концом первого якоря. Я исправил код здесь.
ChaseMedallion

InvariantCultureне работает с универсальными приложениями Windows. Есть ли способ удалить его с сохранением функциональности вашего класса? @ChaseMedallion
Леон

@Leon: у вас должна быть возможность удалить все, что связано с культурой, и .NET просто будет использовать текущую культуру для операции indexOf. Я не знаком с универсальными приложениями Windows, поэтому не могу сказать наверняка.
ChaseMedallion

13

Вот как я могу это сделать

   public string Between(string STR , string FirstString, string LastString)
    {       
        string FinalString;     
        int Pos1 = STR.IndexOf(FirstString) + FirstString.Length;
        int Pos2 = STR.IndexOf(LastString);
        FinalString = STR.Substring(Pos1, Pos2 - Pos1);
        return FinalString;
    }

13

Думаю, это работает:

   static void Main(string[] args)
    {
        String text = "One=1,Two=2,ThreeFour=34";

        Console.WriteLine(betweenStrings(text, "One=", ",")); // 1
        Console.WriteLine(betweenStrings(text, "Two=", ",")); // 2
        Console.WriteLine(betweenStrings(text, "ThreeFour=", "")); // 34

        Console.ReadKey();

    }

    public static String betweenStrings(String text, String start, String end)
    {
        int p1 = text.IndexOf(start) + start.Length;
        int p2 = text.IndexOf(end, p1);

        if (end == "") return (text.Substring(p1));
        else return text.Substring(p1, p2 - p1);                      
    }

Отличное решение. Спасибо!
arcee123

10

Регулярное выражение здесь излишне.

Вы можете использовать string.Splitс перегрузкой, которая принимает в качестве string[]разделителей, но это также было бы излишним.

Посмотрите на Substringи IndexOf- первое, чтобы получить части данной строки, индекс и длину, а второе - для поиска индексированных внутренних строк / символов.


2
Это не излишество ... на самом деле, я бы сказал, что Substring и IndexOf недооценены. Я бы сказал, что строка. Split - это правильно. Регулярное выражение излишне.
НЕ ТАЛИ.

2
Смысл в том, что это избыточное или недостаточное уничтожение, спорный, потому что ответ удовлетворяет просьбу автора сделать это иначе, чем Regex.
Карл Андерсон

2
@newStackExchangeInstance: он также не работает, если перед «ключом:» стоит «-». Подстрока на месте.
jmoreno

@newStackExchangeInstance - я думаю, он говорит об этом string.Split.
Oded

7

Рабочее решение LINQ:

string str = "super exemple of string key : text I want to keep - end of my string";
string res = new string(str.SkipWhile(c => c != ':')
                           .Skip(1)
                           .TakeWhile(c => c != '-')
                           .ToArray()).Trim();
Console.WriteLine(res); // text I want to keep

Это работает только для односимвольных заполнителей?
beppe9000

5
 string str="super exemple of string key : text I want to keep - end of my string";
        int startIndex = str.IndexOf("key") + "key".Length;
        int endIndex = str.IndexOf("-");
        string newString = str.Substring(startIndex, endIndex - startIndex);

1
Ваш код приведет к тому, что двоеточие будет возвращено в начале newString.
tsells

5

Поскольку :и -уникальны, вы можете использовать:

string input;
string output;
input = "super example of string key : text I want to keep - end of my string";
output = input.Split(new char[] { ':', '-' })[1];

Этот ответ не добавляет ничего значимого к и без того огромному количеству существующих ответов.
Мефи

4

или с регулярным выражением.

using System.Text.RegularExpressions;

...

var value =
    Regex.Match(
        "super exemple of string key : text I want to keep - end of my string",
        "key : (.*) - ")
    .Groups[1].Value;

с работающим примером .

Вы можете решить, если это перебор.

или

как недостаточно проверенный метод расширения

using System.Text.RegularExpressions;

public class Test
{
    public static void Main()
    {
        var value =
                "super exemple of string key : text I want to keep - end of my string"
                    .Between(
                        "key : ",
                        " - ");

        Console.WriteLine(value);
    }
}

public static class Ext
{
    static string Between(this string source, string left, string right)
    {
        return Regex.Match(
                source,
                string.Format("{0}(.*){1}", left, right))
            .Groups[1].Value;
    }
}

4
var matches = Regex.Matches(input, @"(?<=key :)(.+?)(?=-)");

Это возвращает только значения между "key:" и следующим появлением "-"


3

Вы можете использовать метод расширения ниже:

public static string GetStringBetween(this string token, string first, string second)
    {            
        if (!token.Contains(first)) return "";

        var afterFirst = token.Split(new[] { first }, StringSplitOptions.None)[1];

        if (!afterFirst.Contains(second)) return "";

        var result = afterFirst.Split(new[] { second }, StringSplitOptions.None)[0];

        return result;
    }

Использование:

var token = "super exemple of string key : text I want to keep - end of my string";
var keyValue = token.GetStringBetween("key : ", " - ");

3

Я использовал фрагмент кода от Виджая Сингха Раны, который в основном выполняет эту работу. Но это вызывает проблемы, если firstStringфайл уже содержит lastString. Я хотел извлечь access_token из ответа JSON (парсер JSON не загружен). Мой firstStringбыл \"access_token\": \"и мой lastStringбыл \". Я закончил с небольшой модификацией

string Between(string str, string firstString, string lastString)
{    
    int pos1 = str.IndexOf(firstString) + firstString.Length;
    int pos2 = str.Substring(pos1).IndexOf(lastString);
    return str.Substring(pos1, pos2);
}

1
Есть избыточность. pos1 добавляли к pos2, а затем вычитали из pos2.
Jfly

Спасибо, вы правы. Я поправил пример выше.
nvm-uli

2

Если вы ищете однострочное решение, вот оно:

s.Substring(s.IndexOf("eT") + "eT".Length).Split("97".ToCharArray()).First()

Полное однострочное решение с System.Linq:

using System;
using System.Linq;

class OneLiner
{
    static void Main()
    {
        string s = "TextHereTisImortant973End"; //Between "eT" and "97"
        Console.WriteLine(s.Substring(s.IndexOf("eT") + "eT".Length)
                           .Split("97".ToCharArray()).First());
    }
}

1

У вас уже есть хорошие ответы, и я понимаю, что код, который я предоставляю, далеко не самый эффективный и чистый. Однако я подумал, что это может быть полезно в образовательных целях. Мы можем использовать готовые классы и библиотеки в течение всего дня. Но не понимая внутренней работы, мы просто имитируем и повторяем и никогда ничему не научимся. Этот код работает и является более простым или "девственным", чем некоторые другие:

char startDelimiter = ':';
char endDelimiter = '-';

Boolean collect = false;

string parsedString = "";

foreach (char c in originalString)
{
    if (c == startDelimiter)
         collect = true;

    if (c == endDelimiter)
         collect = false;

    if (collect == true && c != startDelimiter)
         parsedString += c;
}

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

Береги себя.


Это лучший алгоритм, хотя и худший при создании строк. Все предоставленные ответы, которые не относятся только к регулярным выражениям, запускают триггеры при создании строк, но этот в этом смысле хуже всех. Если бы вы просто захватили начало и конец строки для захвата и использовали '' string.Substring '' для ее извлечения, это было бы идеально.
Пауло Моргадо

Я согласен. Как я уже сказал, это далеко не эффективно. Я бы не рекомендовал использовать этот алгоритм. Это просто «упрощение», чтобы он мог понимать строки на более низком уровне. Если он просто хочет выполнить свою работу, у него уже есть ответы, которые позволят добиться этого »
flyNflip

Я понял это. Я просто указывал на его сильные и слабые стороны. Хотя, чтобы ответить на исходный вопрос, требуется немного больше, поскольку необходимо сопоставить границы строки, а не только границы символов. Но идея все та же.
Пауло Моргадо

1

Если вы хотите обрабатывать несколько вхождений пар подстрок, без RegEx будет нелегко:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty избегает исключения аргумента null
  • ?= сохраняет 1-ю подстроку и ?<= сохраняет вторую подстроку
  • RegexOptions.Singleline разрешает новую строку между парой подстрок

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

var parts = input?.Split(new string[] { "key : ", " - " }, StringSplitOptions.None);
string result = parts?.Length >= 3 ? result[1] : input;

По крайней мере, он позволяет избежать большинства исключений, возвращая исходную строку, если ни одна / одна подстрока не совпадают.


0

Как я всегда говорю, нет ничего невозможного:

string value =  "super exemple of string key : text I want to keep - end of my string";
Regex regex = new Regex(@"(key \: (.*?) _ )");
Match match = regex.Match(value);
if (match.Success)
{
    Messagebox.Show(match.Value);
}

Помните, что следует добавить ссылку на System.Text.RegularExpressions

Надеюсь, что я помог.


0

Что-то вроде этого возможно

private static string Between(string text, string from, string to)
{
    return text[(text.IndexOf(from)+from.Length)..text.IndexOf(to, text.IndexOf(from))];
}

0

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

Для примера, приведенного в вопросе, желаемая строка ясна:

super example of string key : text I want to keep - end of my string
                              ^^^^^^^^^^^^^^^^^^^

Однако эта строка является всего лишь примером строк и граничных строк, для которых необходимо идентифицировать определенные подстроки. Я буду рассматривать общую строку с общими граничными строками, представленную следующим образом.

abc FF def PP ghi,PP jkl,FF mno PP pqr FF,stu FF vwx,PP yza
             ^^^^^^^^^^^^         ^^^^^  

PP- это предыдущая строка , FFэто следующая строка, а шляпы для вечеринок указывают, какие подстроки должны быть сопоставлены. (В примере, приведенном в вопросе, key : это предшествующая строка и -следующая строка.) Я предположил, что PPи FFпредшествуют и следуют границы слова (так что PPAи FF8не совпадают).

Мои предположения, отраженные партийными шляпами, следующие:

  • Первой подстроке PPможет предшествовать один (или несколько)FF подстрок, которые, если они есть, игнорируются;
  • Если PPза ним следует один или несколько PPs, прежде чем FFвстретится, следующееPP s являются частью подстроки между предыдущей и следующей строками;
  • Если PPследует один или более FFсекунд до того, PPявляется столкновение, первый FFследующий PPсчитается следующая строка.

Обратите внимание, что многие ответы здесь относятся только к строкам формы

abc PP def FF ghi
      ^^^^^

или

abc PP def FF ghi PP jkl FF mno
      ^^^^^         ^^^^^

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

(?<=\bPP\b)(?:(?!\bFF\b).)*(?=\bFF\b)

Запустите двигатель! 1

Я тестировал это с помощью механизма регулярных выражений PCRE (PHP), но, поскольку регулярное выражение вовсе не экзотично, я уверен, что оно будет работать с механизмом регулярных выражений .NET (который очень надежен).

Механизм регулярных выражений выполняет следующие операции:

(?<=          : begin a positive lookbehind
  \bPP\b      : match 'PP'
)             : end positive lookbehind
(?:           : begin a non-capture group
  (?!         : begin a negative lookahead
    \bFF\b    : match 'FF'
  )           : end negative lookahead
  .           : match any character
)             : end non-capture group
*             : execute non-capture group 0+ times
(?=           : begin positive lookahead
   \bFF\b     : match 'FF'
)             : end positive lookahead

Этот метод сопоставления одного символа за раз, следующего за предыдущей строкой, до тех пор, пока за символом не Fбудет следовать F(или, в более общем смысле, символ представляет собой строку, составляющую следующую строку), называется Tempered Greedy Token Solution .

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

1. Переместите курсор для получения подробных объяснений.


0

В C # 8.0 и выше вы можете использовать оператор диапазона, ..как в

var s = "header-THE_TARGET_STRING.7z";
var from = s.IndexOf("-") + "-".Length;
var to = s.IndexOf(".7z");
var versionString = s[from..to];  // THE_TARGET_STRING

Подробности смотрите в документации .


0
getStringBetween(startStr, endStr, fullStr) {
    string startIndex = fullStr.indexOf(startStr);
    string endIndex= fullStr.indexOf(endStr);
    return fullStr.substring(startIndex + startStr.length, endIndex);
}

@KiranAmadipudi. Добро пожаловать в StackOverflow. Пожалуйста, объясните, почему, по вашему мнению, предлагаемое вами решение может помочь OP.
Питер Чала
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.