Отримайте рядок між двома рядками в рядку


103

У мене є такий рядок, як:

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

Я хочу просто зберегти рядок, що знаходиться між "key : "і " - ". Як я можу це зробити? Повинен я використовувати Regex чи можу це зробити іншим способом?


2
використання substringтаindexof
Sayse

Отримайте рядок після певного рядка в рядку та перед іншим певним рядком, який також міститься в рядку, де знаходиться попередня рядок ..
Кен Кін

Відповіді:


161

Можливо, хороший спосіб - просто вирізати підрядок :

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
Це створило б декілька непотрібних рядків у пам'яті. Не використовуйте це, якщо ви дбаєте про пам'ять.
Мікаель Дуї Болінджер

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 є, а B до, тому я видалив + fromLength. Я ще не перевіряв це глибоко
Адріан Іфтоде

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

InvariantCultureне працює з Windows Universal Apps. Чи є якийсь спосіб її видалити, зберігаючи функціональність вашого класу? @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 - це недолік. Я б сказав, що string.Split приблизно так. Регекс є надмірним.
ЦеNotALie.

2
Сенс його надмірності або недовикористання - суперечка, оскільки відповідь відповідає просьбі плаката зробити це іншим способом, ніж Regex.
Карл Андерсон

2
@newStackExchangeInstance: він також не працює, якщо перед клавішею "" є "-". Підрядковий ряд на місці.
jmoreno

@newStackExchangeInstance - я вважаю, що він говорить про це string.Split.
Одід

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
Ваш код призведе до того, що двокрапка буде повернута на початку нової строки.
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

Я використав фрагмент коду від Vijay Singh Rana, який в основному робить свою роботу. Але це спричиняє проблеми, якщо firstStringвони вже містять lastString. Я хотів вилучити access_token з відповіді JSON (не завантажений JSON Parser). Моя 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-

2

Якщо ви шукаєте рішення на 1 рядок, це:

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

Ціле рішення на 1 рядок 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

Я це зрозумів. Я тільки вказував на його сильні та тижневі моменти. Хоча для відповіді на оригінальне запитання потрібно трохи більше, оскільки потрібно відповідати меж рядків, а не лише межам символів. Але ідея точно така ж.
Paulo Morgado

1

Якщо ви хочете обробити кілька випадків пар підрядків, без RegEx це буде не просто:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty уникає аргументу нульового виключення
  • ?=зберігає 1-ту підрядку і ?<=зберігає 2-ю підрядку
  • 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);
}

Remeber, який повинен додати посилання на 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, наступні PPs є частиною підрядок між попередньою та наступною строками;
  • Якщо до зустрічі a PPдотримується одне або більше FFs 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), але оскільки регекс зовсім не екзотичний, я впевнений, що він буде працювати з двигуном .ge regex (який дуже надійний).

Регекс-двигун виконує такі операції:

(?<=          : 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(або загалом, персонаж являє собою рядок, що становить наступну рядок), називається загартованим жадним токеном рішення .

Звичайно, якщо змінити викладені вище припущення, змінити регулярний вираз слід (якщо можливо).

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

Детальну інформацію див. У документації .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.