Lấy chuỗi giữa hai chuỗi trong một chuỗi


102

Tôi có một chuỗi như:

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

Tôi muốn chỉ giữ chuỗi nằm giữa "key : "" - ". Làm thế nào tôi có thể làm điều đó? Tôi phải sử dụng Regex hay tôi có thể làm theo cách khác?


2
sử dụng substringindexof
Sayse

Lấy chuỗi sau một chuỗi cụ thể trong một chuỗi và trước một chuỗi cụ thể khác cũng được chứa trong chuỗi mà chuỗi trước đó nằm trong ..
Ken Kin

Câu trả lời:


160

Có lẽ, một cách tốt chỉ là cắt bỏ một chuỗi con :

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;

hoặc chỉ với các hoạt động chuỗi

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

29

Bạn có thể làm điều đó mà không cần regex

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

5
Điều này sẽ tạo ra nhiều chuỗi không cần thiết trong bộ nhớ. Đừng sử dụng cái này nếu bạn quan tâm đến bộ nhớ.
Mikael Dúi Bolinder 25/02/19

14

Tùy thuộc vào mức độ mạnh mẽ / linh hoạt mà bạn muốn triển khai của mình, điều này thực sự có thể hơi phức tạp. Đây là cách triển khai tôi sử dụng:

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 "

Tôi đã sử dụng mã của bạn, nhưng tôi tìm thấy một lỗi nhỏ khi ở @ this.IndexOf (cho đến khi, startIndex + fromLength, so sánh) từ các chuỗi như „AB” trong đó A là từ và B là cho đến khi nào, vì vậy tôi đã xóa + khỏiLength. Tôi đã không kiểm tra nó sâu sắc mặc dù
Adrian Iftode

1
@AdrianIftode: cuộc gọi tốt. Đây chắc chắn là một lỗi. Sẽ rất hợp lý khi bắt đầu tìm kiếm mỏ neo thứ hai tại startIndex, vì nó đã qua phần cuối của mỏ neo đầu tiên. Tôi đã sửa mã ở đây.
ChaseMedallion

InvariantCulturekhông hoạt động với Windows Universal Apps. Có cách nào để loại bỏ nó mà vẫn giữ nguyên chức năng của lớp của bạn không? @ChaseMedallion
Leon

@Leon: bạn sẽ có thể tách ra tất cả những thứ liên quan đến văn hóa và .NET sẽ chỉ sử dụng văn hóa hiện tại cho hoạt động indexOf. Tuy nhiên, tôi không quen thuộc với Windows Universal Apps, vì vậy tôi không thể nói chắc chắn.
ChaseMedallion

13

Đây là cách tôi có thể làm điều đó

   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

Tôi nghĩ điều này hoạt động:

   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);                      
    }

Giải pháp tuyệt vời. Cảm ơn!
arcee123,

10

Regex ở đây quá mức cần thiết.

Bạn có thể sử dụng string.Splitvới quá tải chiếm string[]dấu phân cách nhưng điều đó cũng sẽ quá mức cần thiết.

Nhìn vào SubstringIndexOf- cái trước để lấy các phần của một chuỗi đã cho và chỉ mục và độ dài và cái thứ hai để tìm các chuỗi / ký tự bên trong được lập chỉ mục.


2
Nó không phải là quá mức cần thiết ... trên thực tế, tôi sẽ nói rằng Substring và IndexOf là quá mức cần thiết. Tôi muốn nói rằng string.Split là đúng. Regex là quá mức cần thiết.
Không phải đâu.

2
Vấn đề là quá mức cần thiết hoặc giết dưới mức là một cuộc tranh luận, bởi vì câu trả lời đáp ứng yêu cầu của người đăng về việc làm theo cách khác với Regex.
Karl Anderson

2
@newStackExchangeInstance: nó cũng không thành công nếu có dấu "-" trước "key:". Chuỗi con là tại chỗ.
jmoreno

@newStackExchangeInstance - Tôi tin rằng anh ấy đang nói đến string.Split.
Oded

7

Một giải pháp LINQ đang hoạt động:

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

Điều này chỉ hoạt động cho các trình giữ chỗ một ký tự?
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
Mã của bạn sẽ dẫn đến dấu hai chấm được trả về ở đầu Chuỗi mới.
tsells

5

Vì cái :và cái -là duy nhất bạn có thể sử dụng:

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];

Câu trả lời này không thêm bất cứ điều gì có ý nghĩa vào số lượng lớn các câu trả lời hiện có.
Mephy

4

hoặc, với một regex.

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;

với một ví dụ đang chạy .

Bạn có thể quyết định xem nó có quá mức cần thiết hay không.

hoặc là

như một phương thức tiện ích mở rộng đã được xác thực

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 :)(.+?)(?=-)");

Điều này chỉ trả về (các) giá trị giữa "key:" và lần xuất hiện sau của "-"


3

Bạn có thể sử dụng phương thức mở rộng bên dưới:

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;
    }

Cách sử dụng là:

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

3

Tôi đã sử dụng đoạn mã từ Vijay Singh Rana về cơ bản thực hiện công việc. Nhưng nó gây ra vấn đề nếu firstStringkhông đã chứa lastString. Những gì tôi muốn là trích xuất một access_token từ một JSON Response (không tải JSON Parser). Của tôi firstStringđã được \"access_token\": \"và của tôi lastStringđã được \". Tôi đã kết thúc với một chút sửa đổi

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
Có dư thừa. pos1 đã được thêm vào pos2, và sau đó được rút ra từ pos2.
Jfly 20/07/19

Cảm ơn, bạn đã đúng. Tôi đã sửa ví dụ trên.
nvm-uli

2

Nếu bạn đang tìm kiếm giải pháp 1 dòng, thì đây là:

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

Toàn bộ giải pháp 1 dòng, với 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

Bạn đã có một số câu trả lời hay và tôi nhận thấy mã mà tôi đang cung cấp còn lâu mới hiệu quả và sạch sẽ nhất. Tuy nhiên, tôi nghĩ nó có thể hữu ích cho mục đích giáo dục. Chúng ta có thể sử dụng các lớp học và thư viện được tạo sẵn cả ngày. Nhưng nếu không hiểu được hoạt động bên trong, chúng ta chỉ bắt chước và lặp đi lặp lại và sẽ không bao giờ học được gì. Mã này hoạt động và cơ bản hoặc "trinh nguyên" hơn một số mã khác:

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;
}

Bạn kết thúc với chuỗi mong muốn của bạn được gán cho biến parsedString. Hãy nhớ rằng nó cũng sẽ ghi lại các không gian tiếp tục và trước đó. Hãy nhớ rằng một chuỗi chỉ đơn giản là một mảng các ký tự có thể được thao tác giống như các mảng khác có chỉ số, v.v.

Bảo trọng.


Đây là thuật toán tốt nhất mặc dù kém nhất trong việc tạo chuỗi. Tất cả các câu trả lời được cung cấp không phải là chỉ regex đều giúp kích hoạt khả năng tạo chuỗi nhưng câu trả lời này là tệ nhất theo nghĩa đó. Nếu bạn vừa bắt đầu một phần cuối của chuỗi để nắm bắt và sử dụng '' string.Substring '' để trích xuất nó, thì nó sẽ rất hoàn hảo.
Paulo Morgado

Tôi đồng ý. Như tôi đã đề cập, nó còn lâu mới hiệu quả. Tôi không khuyên bạn nên sử dụng thuật toán này. Nó chỉ đơn giản là "" dumbing nó xuống" để anh có thể hiểu được chuỗi ở một mức độ thấp hơn Nếu anh ta chỉ đơn giản là muốn hoàn thành công việc, anh đã có câu trả lời rằng sẽ đạt được điều đó..
flyNflip

Tôi đã hiểu điều đó. Tôi chỉ chỉ ra điểm mạnh và điểm tuần của nó. Mặc dù, để trả lời câu hỏi ban đầu, nó đòi hỏi nhiều hơn một chút vì nó cần phải khớp với ranh giới chuỗi chứ không chỉ ranh giới ký tự. Nhưng ý tưởng vẫn giống nhau.
Paulo Morgado

1

Nếu bạn muốn xử lý nhiều lần xuất hiện của các cặp chuỗi con, sẽ không dễ dàng nếu không có RegEx:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty tránh đối số null ngoại lệ
  • ?=giữ chuỗi con thứ nhất và ?<=giữ chuỗi con thứ hai
  • RegexOptions.Singleline cho phép dòng mới giữa cặp chuỗi con

Nếu thứ tự và số lần xuất hiện của các chuỗi con không quan trọng, thì một chuỗi nhanh và bẩn này có thể là một tùy chọn:

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

Ít nhất nó tránh hầu hết các trường hợp ngoại lệ, bằng cách trả về chuỗi ban đầu nếu không có / chuỗi con nào phù hợp.


0

Như tôi luôn nói không có gì là không thể:

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);
}

Hãy nhớ rằng sẽ thêm tham chiếu của System.Text.RegularExpressions

Hy vọng rằng tôi đã giúp.


0

Có lẽ như thế này

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

0

Khi các câu hỏi được nêu dưới dạng một ví dụ duy nhất, sự mơ hồ chắc chắn sẽ xuất hiện. Câu hỏi này không phải là ngoại lệ.

Đối với ví dụ được đưa ra trong câu hỏi, chuỗi mong muốn là rõ ràng:

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

Tuy nhiên, chuỗi này chỉ là một ví dụ về chuỗi và chuỗi ranh giới mà các chuỗi con nhất định được xác định. Tôi sẽ xem xét một chuỗi chung với các chuỗi ranh giới chung, được biểu diễn như sau.

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

PPchuỗi trước , FFlà chuỗi sau và mũ bên cho biết chuỗi con nào sẽ được so khớp. (Trong ví dụ được đưa ra trong câu hỏi key : là chuỗi đứng trước và -là chuỗi sau.) Tôi đã giả định rằng PPFFđược đặt trước và theo sau bởi các ranh giới từ (sao cho PPAFF8không khớp).

Các giả định của tôi, được phản ánh bởi những chiếc mũ bên, như sau:

  • Chuỗi con đầu tiên PPcó thể đứng trước một (hoặc nhiều) FFchuỗi con, nếu có, sẽ bị bỏ qua;
  • Nếu PPđược theo sau bởi một hoặc nhiều PPs trước khi FFgặp phải, các PPs sau đây là một phần của chuỗi con giữa các chuỗi trước và sau;
  • Nếu PPđược theo sau bởi một hoặc nhiều FFs trước khi PPgặp phải, chuỗi FFsau đầu tiên PPđược coi là chuỗi sau.

Lưu ý rằng nhiều câu trả lời ở đây chỉ giải quyết các chuỗi của biểu mẫu

abc PP def FF ghi
      ^^^^^

hoặc là

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

Người ta có thể sử dụng một biểu thức chính quy, các cấu trúc mã hoặc kết hợp cả hai để xác định các chuỗi con quan tâm. Tôi không đưa ra đánh giá về cách tiếp cận nào là tốt nhất. Tôi sẽ chỉ trình bày biểu thức chính quy sau đây sẽ khớp với các chuỗi con được quan tâm.

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

Khởi động động cơ của bạn! 1

Tôi đã thử nghiệm điều này với công cụ regex PCRE (PHP), nhưng vì regex không có gì kỳ lạ, tôi chắc chắn rằng nó sẽ hoạt động với công cụ regex .NET (rất mạnh mẽ).

Công cụ regex thực hiện các hoạt động sau:

(?<=          : 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

Kỹ thuật này, khớp một ký tự tại một thời điểm, theo sau chuỗi trước, cho đến khi ký tự đó Fvà được theo sau bởi F(hoặc nói chung, ký tự tạo thành chuỗi tạo thành chuỗi sau), được gọi là Giải pháp Mã thông báo Tham lam .

Đương nhiên, regex sẽ phải được sửa đổi (nếu có thể) nếu các giả định tôi đặt ra ở trên bị thay đổi.

1. Di chuyển con trỏ xung quanh để xem giải thích chi tiết.


0

Trong C # 8.0 trở lên, bạn có thể sử dụng toán tử phạm vi ..như trong

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

Xem tài liệu để biết chi tiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.