วิธีการแทนที่ส่วนของสตริงตามตำแหน่งได้อย่างไร


155

ฉันมีสายนี้: ABCDEFGHIJ

ฉันต้องการแทนที่จากตำแหน่ง 4 ถึงตำแหน่ง 5 ด้วยสตริง ZX

มันจะมีลักษณะเช่นนี้: ABCZXFGHIJ

แต่ไม่ควรใช้กับstring.replace("DE","ZX")- ฉันต้องใช้กับตำแหน่ง

ฉันจะทำมันได้อย่างไร


@TotZam - โปรดตรวจสอบวันที่ อันนี้เก่ากว่าอันที่คุณลิงค์ไว้
ToolmakerSteve

@ToolmakerSteve ฉันมักจะมองไปที่คุณภาพของคำถามและคำตอบที่ไม่ได้วันที่เป็นกล่าวจะทำที่นี่ ในกรณีนี้ดูเหมือนว่าฉันทำผิดและคลิกผิดเพื่อทำเครื่องหมายว่าซ้ำกันเนื่องจากคุณภาพของคำถามนี้ดีขึ้นอย่างเห็นได้ชัดและดังนั้นฉันจึงติดธงคำถามอื่น
Tot Zam

@TotZam - อ่าฉันไม่รู้เกี่ยวกับคำแนะนำนั้น - ขอบคุณที่ชี้ให้เห็น (แม้ว่าจะมีความสับสนที่จะเห็นสิ่งที่เก่ากว่ารายงานว่าเป็นสิ่งที่ใหม่กว่าดังนั้นในกรณีดังกล่าวมันจะคุ้มค่าที่จะอธิบายอย่างชัดเจนว่าคุณกำลังทำเครื่องหมายคำถามซ้ำซ้อนเนื่องจากคำตอบที่เชื่อมโยงมีคำตอบที่ดีกว่า)
ToolmakerSteve

คำตอบ:


198

StringBuilderวิธีที่ง่ายที่สุดในการเพิ่มและลบช่วงในสตริงคือการใช้

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

อีกทางเลือกหนึ่งคือใช้String.Substringแต่ฉันคิดว่าStringBuilderรหัสอ่านง่ายขึ้น


6
จะเป็นการดีกว่าที่ใช้วิธีการขยาย :)
balexandre

ทำไมคุณถึงใช้ StringBuilder ที่นี่? ตรวจสอบคำตอบ V4Vendetta ซึ่งเป็นอ่านได้มากขึ้น ...
Fortega

1
@Fortega Remove () และ Insert () แต่ละอันสร้างและส่งคืนสตริงใหม่ วิธีการ StringBuilder หลีกเลี่ยงค่าใช้จ่ายเพิ่มเติมนี้โดยการทำงานในส่วนที่จัดสรรไว้ล่วงหน้าของหน่วยความจำและการย้ายตัวอักษรรอบตัวมัน
redcalx

@redcalx จริง แต่มีการแนะนำค่าใช้จ่ายเพิ่มเติมโดยการแนะนำวัตถุ StringBuilder และลดความสามารถในการอ่าน
Fortega

2
จุดประสงค์ของการใช้ StringBuilder ที่นี่คืออะไรถ้าคุณโทรลบและแทรกแบบเดียวกับ V4Vendetta แต่เขาทำมันโดยตรงบนสตริง? ดูเหมือนว่ารหัสซ้ำซ้อน
metabuddy

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

17
ฉันชอบสิ่งนี้มากกว่าการเริ่มต้น StringBuilder ใหม่ (... ) ขอบคุณ!
Michael Norgren

3
ฉันไม่รู้ว่าทำไมบางคนถึงใช้ stringbuilder สำหรับการดำเนินการนี้ นี่เป็นคำตอบที่ดี
NappingRabbit

43

ReplaceAt (ดัชนี int, ความยาว int, การแทนที่สตริง)

นี่คือวิธีการขยายที่ไม่ได้ใช้ StringBuilder หรือซับสตริง วิธีนี้ยังช่วยให้สายอักขระการแทนที่ขยายผ่านความยาวของสายอักขระแหล่งที่มา

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

เมื่อใช้ฟังก์ชั่นนี้หากคุณต้องการให้สตริงการแทนที่ทั้งหมดแทนที่อักขระได้มากที่สุดให้ตั้งค่าความยาวเป็นความยาวของสตริงการแทนที่:

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

มิฉะนั้นคุณสามารถระบุจำนวนตัวอักษรที่จะถูกลบ:

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

หากคุณระบุความยาวเป็น 0 ฟังก์ชันนี้จะทำหน้าที่เหมือนกับฟังก์ชันแทรก:

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

ฉันเดาว่านี่จะมีประสิทธิภาพมากกว่าเนื่องจากคลาส StringBuilder ไม่จำเป็นต้องเริ่มต้นและเนื่องจากใช้การดำเนินงานขั้นพื้นฐานมากขึ้น โปรดแก้ไขฉันหากฉันผิด :)


5
Stringbuilder มีประสิทธิภาพมากขึ้นถ้าสตริงยาวกว่าพูด 200 ตัวอักษร
Tim Schmelter

ไม่มีความเครียด. ทำงานตรงขึ้น ขอบคุณ
D_Edet

9

ใช้String.Substring()(รายละเอียดที่นี่ ) เพื่อตัดส่วนที่เหลือแล้วเปลี่ยนของคุณจากนั้นส่วนที่ถูกต้อง เล่นกับดัชนีจนกว่าคุณจะเข้าใจถูกต้อง :)

สิ่งที่ต้องการ:

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

2
ฉันสร้างสามวิธีโดยใช้สามวิธีข้างต้น (string.Remove / Insert, Stringbuilder.Remove / Insert และคำตอบ Substring ของ Daniel และ SubString เป็นวิธีที่เร็วที่สุด
Francine DeGrood Taylor

7

เป็นวิธีการขยาย

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


4

หากคุณใส่ใจกับประสิทธิภาพการทำงานสิ่งที่คุณต้องการหลีกเลี่ยงคือการจัดสรร และถ้าคุณอยู่ในสุทธิหลัก 2.1 + (หรือเป็นยังอาคิโอะสุทธิมาตรฐาน 2.1) จากนั้นคุณสามารถจะทำได้โดยใช้วิธีการ :string.Create

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

วิธีนี้ยากต่อการเข้าใจมากกว่าทางเลือก แต่เป็นวิธีเดียวที่จะจัดสรรวัตถุเดียวต่อการโทร: สตริงที่สร้างขึ้นใหม่


3

คุณสามารถลองลิงค์อะไรก็ได้:

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

3

เช่นเดียวกับคนอื่น ๆ ได้กล่าวถึงSubstring()ฟังก์ชั่นจะมีเหตุผล:

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

อีกทั้งฉันยังจับเวลากับStringBuilderวิธีแก้ปัญหาและรับ 900 ภาพเทียบกับ 875 ดังนั้นมันจึงช้ากว่าเล็กน้อย


@ Aaron.S - ไม่ทำไม่ได้ ในตัวอย่างของฉัน"ABCDEFGHIJ"และ"ZX"มีความยาวแตกต่างกัน
John Alexiou

3

อีกอย่างหนึ่ง

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

2

ด้วยความช่วยเหลือของโพสต์นี้ฉันสร้างฟังก์ชั่นต่อไปนี้ด้วยการตรวจสอบความยาวเพิ่มเติม

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

คำตอบอื่น ๆ ทั้งหมดไม่ทำงานหากสตริงมี Unicode char (เช่น Emojis)เนื่องจาก Unicode char มีน้ำหนักมากกว่าไบต์มากกว่าถ่าน

ตัวอย่าง : อีโมจิ '🎶' ที่แปลงเป็นไบต์จะมีน้ำหนักเท่ากับ 2 ตัวอักษร ดังนั้นถ้าถ่าน Unicode วางอยู่ที่จุดเริ่มต้นของสตริงoffsetพารามิเตอร์จะเปลี่ยน)

ด้วยหัวข้อนี้ฉันขยายคลาส StringInfo เพื่อแทนที่โดยตำแหน่งที่รักษาอัลกอริทึมของ Nick Miller เพื่อหลีกเลี่ยงปัญหาดังกล่าว:

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ฉันไม่สามารถรับวิธีการอื่น ๆ ที่จะล้มเหลวใน Unicode โปรดเปลี่ยนซอเพื่อแสดงกรณีการใช้งานของคุณ สนใจมากในผลลัพธ์
มาร์คัส

1
@MarkusHooge พยายามที่จะวางตัวอักษร Unicode ที่จุดเริ่มต้นของสตริงของคุณเช่น: dotnetfiddle.net/3V2K3Y คุณจะเห็นเฉพาะบรรทัดสุดท้ายทำงานได้ดีและวางถ่านไปยังตำแหน่งที่ 4 (ในกรณีอื่นถ่าน Unicode ใช้ความยาว 2 ถ่าน)
GGO

คุณพูดถูกมันไม่ชัดเจน ฉันอัปเดตคำตอบเพื่อเพิ่มคำอธิบายเพิ่มเติมว่าเพราะเหตุใดคำตอบอื่น ๆ จึงใช้ไม่ได้
GGO

1

ฉันกำลังมองหาวิธีแก้ปัญหาด้วยข้อกำหนดต่อไปนี้:

  1. ใช้นิพจน์เดียวบรรทัดเดียวเท่านั้น
  2. ใช้วิธีการของระบบในตัวเท่านั้น (ไม่มียูทิลิตี้ที่ปรับใช้แบบกำหนดเอง)

โซลูชันที่ 1

ทางออกที่ดีที่สุดสำหรับฉันคือ:

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

สิ่งนี้ใช้ StringBuilder.Replace(oldChar, newChar, position, count)

โซลูชันที่ 2

โซลูชันอื่นที่ตรงตามความต้องการของฉันคือใช้Substringกับการต่อข้อมูล:

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

ก็โอเคเช่นกัน ฉันเดาว่ามันไม่ได้มีประสิทธิภาพเท่ากับการแสดงครั้งแรกที่ชาญฉลาด (เนื่องจากการต่อสตริงที่ไม่จำเป็น) แต่เพิ่มประสิทธิภาพก่อนกำหนดเป็นรากของความชั่วร้ายทั้งหมด

ดังนั้นเลือกอันที่คุณชอบมากที่สุด :)


โซลูชัน 2 ใช้งานได้ แต่ต้องมีการแก้ไข: string newString = oldStr.Substring (0, i) + c + oldString.Substring (i + 1, oldString.Length- (i + 1));
Yura G

0

String.substr()มันจะดีกว่าที่จะใช้

แบบนี้:

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

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

ให้ฉันอธิบายวิธีแก้ปัญหาของฉัน
ให้คำแถลงปัญหาของการแก้ไขสตริงในตำแหน่งเฉพาะสองตำแหน่ง (“ ตำแหน่ง 4 เป็นตำแหน่ง 5”) ด้วยอักขระสองตัว 'Z' และ 'X' และการถามคือการใช้ดัชนีตำแหน่งเพื่อเปลี่ยนสตริงและไม่ใช่สตริงแทนที่ ( ) วิธีการ (อาจเป็นเพราะความเป็นไปได้ของการซ้ำซ้อนของอักขระบางตัวในสตริงจริง) ฉันต้องการใช้วิธีการที่เรียบง่ายเพื่อให้บรรลุเป้าหมายมากกว่าการใช้Substring()และสตริงConcat()หรือสตริงRemove()และInsert()วิธีการ แม้ว่าการแก้ปัญหาทั้งหมดนั้นจะให้บริการวัตถุประสงค์ในการบรรลุเป้าหมายเดียวกัน แต่มันก็ขึ้นอยู่กับตัวเลือกส่วนบุคคลและปรัชญาของการตกตะกอนด้วยวิธีการที่เรียบง่าย
กลับมาที่วิธีการแก้ปัญหาของฉันที่กล่าวถึงข้างต้นถ้าเรามองใกล้stringและStringBuilderทั้งคู่จะใช้สตริงที่กำหนดเป็นอาร์เรย์ของอักขระภายใน ถ้าเราดูที่การนำไปปฏิบัติStringBuilderมันเก็บรักษาตัวแปรภายในบางอย่างเช่น " internal char[] m_ChunkChars;" เพื่อจับภาพสตริงที่กำหนด ตอนนี้เนื่องจากนี่เป็นตัวแปรภายในเราจึงไม่สามารถเข้าถึงได้โดยตรง สำหรับโลกภายนอกเพื่อให้สามารถเข้าถึงและแก้ไขอาเรย์ตัวละครนั้นStringBuilderจะเปิดเผยผ่านคุณสมบัติตัวสร้างดัชนีซึ่งมีลักษณะดังนี้

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

โซลูชันของฉันที่กล่าวถึงข้างต้นใช้ประโยชน์จากความสามารถของตัวทำดัชนีนี้เพื่อปรับเปลี่ยนอาร์เรย์อักขระที่ดูแลรักษาภายในโดยตรงซึ่ง IMO นั้นมีประสิทธิภาพและเรียบง่าย

BTW; เราสามารถเขียนวิธีการแก้ปัญหาข้างต้นอย่างละเอียดมากขึ้นบางอย่างเช่นด้านล่าง

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

ในบริบทนี้ยังต้องการที่จะพูดถึงว่าในกรณีที่stringมันยังมีคุณสมบัติตัวทำดัชนีเป็นวิธีการเปิดเผยอาร์เรย์ตัวละครภายในของมัน แต่ในกรณีนี้มันมีเพียงคุณสมบัติ Getter (และไม่มี Setter) เป็นสตริงที่ไม่เปลี่ยนรูปในธรรมชาติ และนั่นคือสาเหตุที่เราต้องใช้StringBuilderเพื่อเปลี่ยนอาเรย์ตัวละคร

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

และสุดท้าย แต่ไม่ใช่อย่างน้อยโซลูชันนี้เหมาะที่สุดสำหรับปัญหาเฉพาะนี้โดยการถามคือการแทนที่อักขระเพียงไม่กี่ตัวด้วยดัชนีตำแหน่งที่ทราบล่วงหน้า มันอาจจะไม่เหมาะสมที่สุดเมื่อความต้องการคือการเปลี่ยนสตริงที่มีความยาวพอสมควรเช่นจำนวนตัวอักษรที่ต้องการเปลี่ยนมีจำนวนมาก


1
โปรดแก้ไขคำตอบของคุณเพื่ออธิบายว่ารหัสนี้ตอบคำถามอย่างไร
tshimkus

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

นี่คือวิธีการขยายอย่างง่าย:

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

ใช้มันแบบนี้:

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

-2

ฉันเชื่อว่าวิธีที่ง่ายที่สุดคือ: (ไม่มีตัวสร้างสตริง)

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

ใช้งานได้เพราะตัวแปรประเภทสตริงสามารถใช้เป็นอาร์เรย์ของตัวแปรถ่านได้ ตัวอย่างเช่นคุณสามารถอ้างถึงอักขระตัวที่สองของตัวแปรสตริงที่ชื่อ "myString" เป็น myString [1]


ใช้งานได้เพราะในตัวอย่างของผู้เขียนstringอักขระที่จะถูกแทนที่แต่ละตัวจะเกิดขึ้นเพียงครั้งเดียว หากคุณทำสิ่งนี้กับพูด"DBCDEFGHIE"แล้วคุณจะได้รับ"ZBCZXFGHIX"ซึ่งไม่ใช่ผลลัพธ์ที่ต้องการ นอกจากนี้ฉันจะไม่ยอมรับว่าสิ่งนี้ง่ายกว่าStringBuilderโซลูชันอื่น ๆที่โพสต์เมื่อสองปีครึ่งที่แล้ว
BACON
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.