Làm thế nào để thay thế một phần của chuỗi theo vị trí?


155

Tôi có chuỗi này: ABCDEFGHIJ

Tôi cần thay thế từ vị trí 4 sang vị trí 5 bằng chuỗi ZX

Nó sẽ trông giống thế này: ABCZXFGHIJ

Nhưng không sử dụng với string.replace("DE","ZX")- Tôi cần sử dụng với vị trí

Tôi làm nó như thế nào?


@TotZam - vui lòng kiểm tra ngày. Cái này cũ hơn cái bạn liên kết.
ToolmakerSteve

@ToolmakerSteve Tôi thường nhìn vào chất lượng của câu hỏi và câu trả lời, không phải ngày tháng, như đã nói ở đây . Trong trường hợp này, tôi dường như đã mắc lỗi và nhấp vào sai để đánh dấu là trùng lặp, vì chất lượng của câu hỏi này rõ ràng là tốt hơn, và vì vậy tôi đã đánh dấu câu hỏi khác.
Tot Zam

@TotZam - à, tôi không biết về đề xuất đó - cảm ơn vì đã chỉ ra. (Mặc dù nó là khó hiểu để xem một cái gì đó lớn tuổi được báo cáo là một bản sao của một cái gì đó mới hơn, vì vậy trong trường hợp này, nó sẽ có giá trị rõ ràng giải thích rằng bạn đang đánh dấu như lặp lại một câu hỏi cũ, vì một liên kết có câu trả lời tốt hơn.)
ToolmakerSteve

Câu trả lời:


198

Cách dễ nhất để thêm và xóa phạm vi trong chuỗi là sử dụng StringBuilder.

var theString = "ABCDEFGHIJ";
var aStringBuilder = new StringBuilder(theString);
aStringBuilder.Remove(3, 2);
aStringBuilder.Insert(3, "ZX");
theString = aStringBuilder.ToString();

Một cách khác là sử dụng String.Substring, nhưng tôi nghĩ StringBuildermã dễ đọc hơn.


6
sẽ tốt hơn khi gói nó trong một phương thức mở rộng :)
balexandre

Tại sao bạn sử dụng StringBuilder ở đây? Kiểm tra câu trả lời của V4Vendetta, dễ đọc hơn nhiều ...
Fortega

1
@Fortega Xóa () và Chèn () mỗi lần tạo và trả về một chuỗi mới. Cách tiếp cận StringBuilder tránh chi phí thêm này bằng cách làm việc trong một phần bộ nhớ được phân bổ trước và di chuyển vòng ký tự trong đó.
redcalx

@redcalx Điều đó đúng, nhưng một chi phí bổ sung được giới thiệu bằng cách giới thiệu một đối tượng StringBuilder và bằng cách giảm khả năng đọc
Fortega

2
Điểm quan trọng của việc sử dụng StringBuilder ở đây là gì, nếu bạn đang gọi Xóa và Chèn giống như V4Vendetta, nhưng anh ấy đang thực hiện trực tiếp trên chuỗi? Có vẻ mã thừa.
metabuddy

186
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");

17
Tôi thích điều này hơn là khởi tạo StringBuilder mới (...). Cảm ơn!
Michael Norgren

3
Tôi không biết tại sao ai đó sẽ sử dụng Stringbuilder cho hoạt động này. Đây là câu trả lời hay
NappingRợi

43

Thay thếAt (chỉ mục int, độ dài int, chuỗi thay thế)

Đây là một phương thức mở rộng không sử dụng StringBuilder hoặc Sub Chuỗi. Phương pháp này cũng cho phép chuỗi thay thế kéo dài quá độ dài của chuỗi nguồn.

//// str - the source string
//// index- the start location to replace at (0-based)
//// length - the number of characters to be removed before inserting
//// replace - the string that is replacing characters
public static string ReplaceAt(this string str, int index, int length, string replace)
{
    return str.Remove(index, Math.Min(length, str.Length - index))
            .Insert(index, replace);
}

Khi sử dụng chức năng này, nếu bạn muốn toàn bộ chuỗi thay thế thay thế càng nhiều ký tự càng tốt, thì hãy đặt độ dài thành độ dài của chuỗi thay thế:

"0123456789".ReplaceAt(7, 5, "Hello") = "0123456Hello"

Nếu không, bạn có thể chỉ định số lượng ký tự sẽ bị xóa:

"0123456789".ReplaceAt(2, 2, "Hello") = "01Hello456789"

Nếu bạn chỉ định độ dài là 0, thì hàm này hoạt động giống như hàm chèn:

"0123456789".ReplaceAt(4, 0, "Hello") = "0123Hello456789"

Tôi đoán điều này hiệu quả hơn vì lớp StringBuilder không cần phải được khởi tạo và vì nó sử dụng các hoạt động cơ bản hơn. Xin hãy sửa tôi nếu tôi sai. :)


5
Stringbuilder hiệu quả hơn nếu chuỗi dài hơn 200 ký tự.
Tim Schmelter

Không áp lực. Làm việc thẳng lên. Cảm ơn
D_Edet

9

Sử dụng String.Substring()(chi tiết tại đây ) để cắt phần bên trái, sau đó thay thế phần của bạn, sau đó phần bên phải. Chơi với các chỉ mục cho đến khi bạn hiểu đúng :)

Cái gì đó như:

string replacement=original.Substring(0,start)+
    rep+original.Substring(start+rep.Length);

2
Tôi đã tạo ba phương thức bằng ba phương thức trên (chuỗi.Remove / Chèn, Stringbuilder.Remove / Chèn và câu trả lời chuỗi con của Daniel và SubString là phương thức nhanh nhất.
Francine DeGrood Taylor

7

Là một phương pháp mở rộng.

public static class StringBuilderExtension
{
    public static string SubsituteString(this string OriginalStr, int index, int length, string SubsituteStr)
    {
        return new StringBuilder(OriginalStr).Remove(index, length).Insert(index, SubsituteStr).ToString();
    }
}

5
        string s = "ABCDEFG";
        string t = "st";
        s = s.Remove(4, t.Length);
        s = s.Insert(4, t);

Điều này hoạt động, có lẽ bạn không muốn đặt tlại.
Pierre

4

Nếu bạn quan tâm đến hiệu suất, thì điều bạn muốn tránh ở đây là phân bổ. Và nếu bạn đang ở trên Net Lõi 2.1 + (hoặc, như được nêu ra chưa được phát hành, Net Chuẩn 2.1), sau đó bạn có thể, bằng cách sử dụng các string.Createphương pháp :

public static string ReplaceAt(this string str, int index, int length, string replace)
{
    return string.Create(str.Length - length + replace.Length, (str, index, length, replace),
        (span, state) =>
        {
            state.str.AsSpan().Slice(0, state.index).CopyTo(span);
            state.replace.AsSpan().CopyTo(span.Slice(state.index));
            state.str.AsSpan().Slice(state.index + state.length).CopyTo(span.Slice(state.index + state.replace.Length));
        });
}

Cách tiếp cận này khó hiểu hơn các lựa chọn thay thế, nhưng đó là cách duy nhất sẽ chỉ phân bổ một đối tượng cho mỗi cuộc gọi: chuỗi mới được tạo.


3

Bạn có thể thử một cái gì đó liên kết này:

string str = "ABCDEFGHIJ";
str = str.Substring(0, 2) + "ZX" + str.Substring(5);

3

Giống như khác đã đề cập Substring()chức năng là có vì một lý do:

static void Main(string[] args)
{
    string input = "ABCDEFGHIJ";

    string output = input.Overwrite(3, "ZX"); // 4th position has index 3
    // ABCZXFGHIJ
}

public static string Overwrite(this string text, int position, string new_text)
{
    return text.Substring(0, position) + new_text + text.Substring(position + new_text.Length);
}

Ngoài ra tôi đã tính thời gian này với StringBuildergiải pháp và nhận được 900 tics so với 875. Vì vậy, nó hơi chậm.


@ Aaron.S - không, không. Trong ví dụ của tôi "ABCDEFGHIJ""ZX"có độ dài khác nhau.
John Alexiou

3

Còn cái khác

    public static string ReplaceAtPosition(this string self, int position, string newValue)        
    {
        return self.Remove(position, newValue.Length).Insert(position, newValue); 
    }

2

Với sự giúp đỡ của bài đăng này, tôi tạo chức năng sau với các kiểm tra độ dài bổ sung

public string ReplaceStringByIndex(string original, string replaceWith, int replaceIndex)
{
    if (original.Length >= (replaceIndex + replaceWith.Length))
    {
        StringBuilder rev = new StringBuilder(original);
        rev.Remove(replaceIndex, replaceWith.Length);
        rev.Insert(replaceIndex, replaceWith);
        return rev.ToString();
    }
    else
    {
        throw new Exception("Wrong lengths for the operation");
    }
}

2

Tất cả các câu trả lời khác không hoạt động nếu chuỗi chứa char char (như Biểu tượng cảm xúc) vì một char char có trọng lượng nhiều byte hơn một char.

Ví dụ : biểu tượng cảm xúc '' được chuyển đổi thành byte, sẽ có trọng số tương đương với 2 ký tự. Vì vậy, nếu char unicode được đặt ở đầu chuỗi của bạn, offsettham số sẽ được thay đổi).

Với chủ đề này , tôi mở rộng lớp StringInfo thành Thay thế bằng cách giữ vị trí thuật toán của Nick Miller để tránh điều đó:

public static class StringInfoUtils
{
    public static string ReplaceByPosition(this string str, string replaceBy, int offset, int count)
    {
        return new StringInfo(str).ReplaceByPosition(replaceBy, offset, count).String;
    }

    public static StringInfo ReplaceByPosition(this StringInfo str, string replaceBy, int offset, int count)
    {
        return str.RemoveByTextElements(offset, count).InsertByTextElements(offset, replaceBy);
    }

    public static StringInfo RemoveByTextElements(this StringInfo str, int offset, int count)
    {
        return new StringInfo(string.Concat(
            str.SubstringByTextElements(0, offset),
            offset + count < str.LengthInTextElements
                ? str.SubstringByTextElements(offset + count, str.LengthInTextElements - count - offset)
                : ""
            ));
    }
    public static StringInfo InsertByTextElements(this StringInfo str, int offset, string insertStr)
    {
        if (string.IsNullOrEmpty(str?.String))
            return new StringInfo(insertStr);
        return new StringInfo(string.Concat(
            str.SubstringByTextElements(0, offset),
            insertStr,
            str.LengthInTextElements - offset > 0 ? str.SubstringByTextElements(offset, str.LengthInTextElements - offset) : ""
        ));
    }
}

dotnetfiddle.net/l0dqS5 Tôi không thể làm cho các phương thức khác bị lỗi trên Unicode. Vui lòng thay đổi fiddle để chứng minh trường hợp sử dụng của bạn. Rất quan tâm, trong kết quả.
Markus

1
@MarkusHooge Hãy thử đặt các ký tự unicode ở đầu chuỗi của bạn như: dotnetfiddle.net/3V2K3Y . Bạn sẽ chỉ thấy dòng cuối cùng hoạt động tốt và đặt char đến vị trí thứ 4 (trong trường hợp khác, unicode char mất 2 char chiều dài)
GGO 15/03/19

Bạn nói đúng, không rõ ràng. Tôi đã cập nhật câu trả lời của mình để thêm giải thích lý do tại sao những câu trả lời khác không hoạt động
GGO 15/03/19

1

Tôi đang tìm kiếm một giải pháp với các yêu cầu sau:

  1. chỉ sử dụng một biểu thức một dòng
  2. chỉ sử dụng các phương thức dựng sẵn hệ thống (không có tiện ích triển khai tùy chỉnh)

Giải pháp 1

Giải pháp phù hợp nhất với tôi là:

// replace `oldString[i]` with `c`
string newString = new StringBuilder(oldString).Replace(oldString[i], c, i, 1).ToString();

Cái này dùng StringBuilder.Replace(oldChar, newChar, position, count)

Giải pháp 2

Giải pháp khác thỏa mãn yêu cầu của tôi là sử dụng Substringkết hợp:

string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length);

Điều này cũng tốt Tôi đoán nó không hiệu quả như lần đầu tiên thực hiện (do nối chuỗi không cần thiết). Nhưng tối ưu hóa sớm là gốc rễ của mọi tội lỗi .

Vì vậy, chọn một trong đó bạn thích nhất :)


Giải pháp 2 hoạt động nhưng yêu cầu chỉnh sửa: chuỗi newString = oldStr.Sub chuỗi (0, i) + c + oldString.Sub chuỗi (i + 1, oldString.Ldrops- (i + 1));
Yura G

0

Tốt hơn là sử dụng String.substr().

Như thế này:

ReplString = GivenStr.substr(0, PostostarRelStr)
           + GivenStr(PostostarRelStr, ReplString.lenght());

0
string myString = "ABCDEFGHIJ";
string modifiedString = new StringBuilder(myString){[3]='Z', [4]='X'}.ToString();

Hãy để tôi giải thích giải pháp của tôi.
Đưa ra tuyên bố vấn đề về việc thay đổi một chuỗi ở hai vị trí cụ thể của nó (vị trí 4 đến vị trí 5) với hai ký tự 'Z' và 'X' và yêu cầu là sử dụng chỉ mục vị trí để thay đổi chuỗi và không thay thế chuỗi ( ) phương thức (có thể là do khả năng lặp lại của một số ký tự trong chuỗi thực tế), tôi thích sử dụng phương pháp tối giản để đạt được mục tiêu sử dụng Substring()và chuỗi Concat()hoặc chuỗi Remove()Insert()cách tiếp cận. Mặc dù tất cả những giải pháp đó sẽ phục vụ mục đích đạt được cùng một mục tiêu, nhưng nó chỉ phụ thuộc vào lựa chọn cá nhân và triết lý giải quyết theo phương pháp tối giản.
Quay trở lại với giải pháp của tôi đã đề cập ở trên, nếu chúng ta xem xét kỹ hơn vềstringStringBuilder, cả hai bên trong đều coi một chuỗi đã cho là một mảng các ký tự. Nếu chúng ta nhìn vào việc thực hiện StringBuilderthì nó sẽ duy trì một biến nội bộ giống như (cơ bản internal char[] m_ChunkChars;) để nắm bắt chuỗi đã cho. Bây giờ vì đây là một biến nội bộ, chúng tôi không thể truy cập trực tiếp như vậy. Đối với thế giới bên ngoài, để có thể truy cập và thay đổi mảng ký tự đó, hãy StringBuilderhiển thị chúng thông qua thuộc tính bộ chỉ mục trông giống như bên dưới

    [IndexerName("Chars")]
    public char this[int index]
    {
      get
      {
        StringBuilder stringBuilder = this;
        do
        {
          // … some code
            return stringBuilder.m_ChunkChars[index1];
          // … some more code
        }
      }
      set
      {
        StringBuilder stringBuilder = this;
        do
        {
            //… some code
            stringBuilder.m_ChunkChars[index1] = value;
            return;
            // …. Some more code
        }
      }
    }

Giải pháp của tôi được đề cập ở trên tận dụng khả năng lập chỉ mục này để thay đổi trực tiếp mảng ký tự được duy trì bên trong mà IMO là hiệu quả và tối giản.

BTW; chúng ta có thể viết lại giải pháp trên một cách công phu hơn

 string myString = "ABCDEFGHIJ";
 StringBuilder tempString = new StringBuilder(myString);
 tempString[3] = 'Z';
 tempString[4] = 'X';
 string modifiedString = tempString.ToString();

Trong bối cảnh này cũng muốn đề cập rằng trong trường hợp stringnó cũng có thuộc tính bộ chỉ mục làm phương tiện để hiển thị mảng ký tự bên trong của nó, nhưng trong trường hợp này, nó chỉ có thuộc tính Getter (và không có Setter) vì bản chất chuỗi là bất biến. Và đó là lý do tại sao chúng ta cần sử dụng StringBuilderđể thay đổi mảng ký tự.

[IndexerName("Chars")]
public extern char this[int index] { [SecuritySafeCritical, __DynamicallyInvokable, MethodImpl(MethodImplOptions.InternalCall)] get; }

Và cuối cùng nhưng không kém phần quan trọng, giải pháp này chỉ phù hợp nhất cho vấn đề cụ thể này, trong đó yêu cầu là chỉ thay thế một vài ký tự bằng một chỉ số vị trí đã biết trước. Nó có thể không phù hợp nhất khi yêu cầu là thay đổi một chuỗi khá dài, tức là số lượng ký tự cần thay đổi là số lượng lớn.


1
Vui lòng chỉnh sửa câu trả lời của bạn để giải thích cách mã này trả lời câu hỏi
tshimkus 16/03/19

0
String timestamp = "2019-09-18 21.42.05.000705";
String sub1 = timestamp.substring(0, 19).replace('.', ':'); 
String sub2 = timestamp.substring(19, timestamp.length());
System.out.println("Original String "+ timestamp);      
System.out.println("Replaced Value "+ sub1+sub2);

0

Đây là một phương pháp mở rộng đơn giản:

    public static class StringBuilderExtensions
    {
        public static StringBuilder Replace(this StringBuilder sb, int position, string newString)
            => sb.Replace(position, newString.Length, newString);

        public static StringBuilder Replace(this StringBuilder sb, int position, int length, string newString)
            => (newString.Length <= length)
                ? sb.Remove(position, newString.Length).Insert(position, newString)
                : sb.Remove(position, length).Insert(position, newString.Substring(0, length));
    }

Sử dụng nó như thế này:

var theString = new string(' ', 10);
var sb = new StringBuilder(theString);
sb.Replace(5, "foo");
return sb.ToString();

-2

Tôi tin rằng cách đơn giản nhất sẽ là thế này: (không có người xây dựng chuỗi)

string myString = "ABCDEFGHIJ";
char[] replacementChars = {'Z', 'X'};
byte j = 0;

for (byte i = 3; i <= 4; i++, j++)  
{                   
myString = myString.Replace(myString[i], replacementChars[j]);  
}

Điều này hoạt động vì một biến của chuỗi kiểu có thể được coi là một mảng các biến char. Ví dụ, bạn có thể tham khảo ký tự thứ hai của biến chuỗi có tên "myString" là myString [1]


Điều này chỉ hoạt động vì trong ví dụ của tác giả, stringcác ký tự được thay thế chỉ xảy ra một lần. Nếu bạn chống lại điều này, giả sử, "DBCDEFGHIE"sau đó bạn nhận được "ZBCZXFGHIX", đó không phải là kết quả mong muốn. Ngoài ra, tôi sẽ không đồng ý rằng việc này đơn giản hơn các StringBuildergiải pháp không phải khác đã được đăng cách đây hai năm rưỡi.
BACON
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.