Dizenin bir kısmını konuma göre nasıl değiştirebilirim?


155

Bu dize var: ABCDEFGHIJ

Dize ile konum 4'ten konum 5'e değiştirmem gerekiyor ZX

Bunun gibi görünecek: ABCZXFGHIJ

Ama birlikte kullanmıyorum string.replace("DE","ZX")- Pozisyonla kullanmam lazım

Nasıl yapabilirim?


@TotZam - lütfen tarihleri ​​kontrol edin. Bu , bağladığınızdan daha eski.
ToolmakerSteve

@ToolmakerSteve Burada söylendiği gibi, tarihlere değil, genellikle soru ve cevapların kalitesine bakarım . Bu durumda, bir hata yapmışım ve yinelenen olarak işaretlemek için yanlış olanı tıkladım, çünkü bu sorunun kalitesi açıkça daha iyi ve bu yüzden diğer soruyu işaretledim.
Tot Zam

@TotZam - ah, bu öneriyi bilmiyordum - işaret ettiğiniz için teşekkürler. (Eski bir şeyi daha yeni bir şeyin kopyası olarak bildirmek kafa karıştırıcı olsa da, bu durumda böyle bir durumda, ESKİ bir soruyu yinelediğinizi işaretlediğinizi açıkça belirtmek gerekir, çünkü bağlantılı olanın daha iyi cevapları vardır.)
ToolmakerSteve

Yanıtlar:


198

Bir dizeye aralık eklemenin ve kaldırmanın en kolay yolu StringBuilder.

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

Bir alternatif kullanmaktır String.Substring, ama StringBuilderkodun daha okunabilir olduğunu düşünüyorum .


6
daha iyi bir uzantısı yöntemiyle sarma daha iyi olurdu :)
balexandre

Neden burada bir StringBuilder kullanıyorsunuz? Çok daha okunabilir olan V4Vendetta'nın cevabını kontrol edin ...
Fortega

1
@Fortega Remove () ve Insert () her biri yeni bir dize oluşturur ve döndürür. StringBuilder yaklaşımı, belleğin önceden ayrılmış bir bölümünde çalışarak ve içindeki karakterleri hareket ettirerek bu ekstra maliyeti önler.
redcalx

@redcalx Bu doğru, ancak bir StringBuilder nesnesi tanıtılarak ve okunabilirliği azaltarak ek bir maliyet
getiriliyor

2
Kaldır ve Ekle'yi V4Vendetta ile aynı şekilde çağırıyorsanız, StringBuilder'ı burada kullanmanın anlamı nedir, ancak doğrudan dizede yapıyor mu? Gereksiz kod gibi görünüyor.
metabuddy

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

17
Bunu yeni bir StringBuilder (...) başlatma tercih ederim. Teşekkürler!
Michael Norgren

3
Neden birisi bu işlem için stringbuilder kullanacak bilmiyorum. Bu cevap iyi
NappingRabbit

43

ReplaceAt (int dizini, int uzunluğu, dize değiştirme)

İşte StringBuilder veya Substring kullanmayan bir uzantı yöntemi. Bu yöntem ayrıca, değiştirme dizesinin kaynak dizenin uzunluğunu geçmesine izin verir.

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

Bu işlevi kullanırken, değiştirme dizesinin tamamının olabildiğince fazla karakterin yerini almasını istiyorsanız, uzunluğu değiştirme dizesinin uzunluğuna ayarlayın:

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

Aksi takdirde, kaldırılacak karakter miktarını belirleyebilirsiniz:

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

Uzunluğu 0 olarak belirtirseniz, bu işlev tıpkı ekleme işlevi gibi davranır:

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

StringBuilder sınıfının başlatılması gerekmediğinden ve daha temel işlemler kullandığından bu daha verimli olduğunu düşünüyorum. Yanlışım varsa lütfen düzelt. :)


5
Dize, 200 karakterden uzunsa dize oluşturucu daha etkilidir.
Tim Schmelter

Stres yok. Düz yukarı çalıştı. Teşekkürler
D_Edet

9

Sol parçayı, daha sonra yenisini, ardından sağ parçayı kesmek için String.Substring()( burada ayrıntılar ) kullanın . Doğru olana kadar dizinlerle oynayın :)

Gibi bir şey:

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

2
Yukarıda üç yöntem kullanılarak üç yöntem yarattı (string.Remove / Ekle, Stringbuilder.Remove / takın ve Daniel'in Substring cevabı ve alt dize en hızlı yöntem oldu.
Francine DeGrood Taylor

7

Bir uzatma yöntemi olarak.

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

Bu işe yarıyor, muhtemelen bir daha tgeri dönmek istemiyorsunuz .
Pierre

4

Performansı önemsiyorsanız, burada kaçınmak istediğiniz şey tahsislerdir. Ve .Net Core 2.1+ (veya henüz yayınlanmamış, .Net Standard 2.1) kullanıyorsanız , string.Createyöntemi kullanarak şunları yapabilirsiniz :

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

Bu yaklaşımı anlamak alternatiflerden daha zordur, ancak çağrı başına yalnızca bir nesne ayıracak olan tek yaklaşımdır: yeni oluşturulan dize.


3

Bunu bağlayan bir şey deneyebilirsiniz:

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

3

Diğerleri gibi Substring()fonksiyon bir nedeni var:

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

Ayrıca bunu StringBuilderçözüme karşı zamanladım ve 875'e karşı 900 tik aldım . Bu yüzden biraz daha yavaş.


@ Aaron.S - hayır değil. Benim örnekte "ABCDEFGHIJ"ve "ZX"farklı uzunluklarda.
John Alexiou

3

Yine bir başka

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

2

Bu yazının yardımıyla, ek uzunluk kontrolleriyle aşağıdaki işlevi oluşturuyorum

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

Dize Unicode char (Emojis gibi) içeriyorsa diğer tüm yanıtlar çalışmaz, çünkü Unicode char ağırlığı bir chartan daha fazla bayttır.

Örnek : baytlara dönüştürülen emoji '🎶', 2 karaktere eşdeğer olacaktır. Bu nedenle, unicode char dizenizin başına yerleştirilirse offsetparametre kaydırılır).

İle bu konu , ben bunu önlemek Nick Miller algoritması tutarak pozisyonuna göre değiştirin etmek StringInfo sınıfını genişletmek:

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'da başarısız olmak için başka yöntemler alamadım. Lütfen kullanım durumunuzu göstermek için kemanı değiştirin. Sonuçlarla çok ilgili.
Markus

1
@MarkusHooge Unicode karakterleri dizenizin başına aşağıdaki gibi yerleştirmeye çalışın : dotnetfiddle.net/3V2K3Y . Sadece son satırın iyi çalıştığını görürsünüz ve karakteri 4. sıraya yerleştirirsiniz (diğer durumda, unicode char 2 karakter uzunluğunda olur)
GGO

Haklısın, net değil. Diğerlerinin neden çalışmadığını daha fazla açıklama eklemek için cevabımı güncelledim
GGO

1

Aşağıdaki gereksinimleri içeren bir çözüm arıyordum:

  1. yalnızca tek satırlık bir ifade kullanın
  2. yalnızca sistem yerleşik yöntemlerini kullan (özel olarak uygulanan yardımcı program yok)

Çözüm 1

Bana en uygun çözüm şudur:

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

Bu kullanır StringBuilder.Replace(oldChar, newChar, position, count)

Çözüm 2

Gereksinimlerimi karşılayan diğer çözüm, Substringbirleştirme ile kullanmaktır :

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

Bu da sorun değil. Ben tahmin akıllıca ilki performansı (nedeniyle gereksiz dize birbirine bağlanmasına yol) kadar etkili değil. Ancak erken optimizasyon tüm kötülüklerin köküdür .

Bu yüzden en çok hoşunuza giden birini seçin :)


Çözüm 2 çalışır, ancak düzeltme gerektirir: string newString = oldStr.Substring (0, i) + c + oldString.Substring (i + 1, oldString.Length- (i + 1));
Yura G

0

Kullanmak daha iyidir String.substr().

Bunun gibi:

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

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

Çözümümü açıklayayım.
Bir dizgiyi iki özel konumunda ("konum 4 - konum 5") iki karakter "Z" ve "X" ile değiştirmenin sorun ifadesi göz önüne alındığında, soru dizeyi değiştirmek için dizeyi değiştirmek ve dizeyi değiştirmek için kullanmaktır. ) yöntemi (gerçek karakter dizisindeki bazı karakterlerin tekrarlanması nedeniyle olabilir), ben Substring()ve string Concat()veya string Remove()ve Insert()yaklaşım üzerinden hedefe ulaşmak için minimalist bir yaklaşım kullanmayı tercih ederim . Tüm bu çözümler aynı hedefe ulaşma amacına hizmet edecek olsa da, sadece kişisel seçim ve minimalist yaklaşımla yerleşim felsefesine bağlıdır.
Eğer daha yakından bakarsak stringveStringBuilder, her ikisi de belirli bir dizeyi dahili olarak bir karakter dizisi olarak ele alır. UygulamasınaStringBuilderinternal char[] m_ChunkChars;verilen dizeyi yakalamak için “ ” gibi bir iç değişkeni korur . Şimdi bu bir iç değişken olduğundan, doğrudan aynı değişkene erişemeyiz. Dış dünya için, bu karakter dizisine erişebilmek ve bunları değiştirebilmek için, StringBuilderbunları aşağıdaki gibi görünen indexer özelliği ile gösterir.

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

Yukarıda bahsedilen çözümüm, IMO'nun verimli ve minimalist olduğu dahili olarak tutulan karakter dizisini doğrudan değiştirmek için bu indeksleyici yeteneğinden yararlanıyor.

BTW; yukarıdaki çözümü daha ayrıntılı olarak aşağıdaki gibi yeniden yazabiliriz

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

Bu bağlamda ayrıca string, iç karakter dizisini ortaya çıkarmanın bir aracı olarak indexer özelliğine sahip olduğunu belirtmek ister , ancak bu durumda dize doğası gereği değişmez olduğu için yalnızca Getter özelliğine (Setter yok) sahiptir. Bu yüzden StringBuilderkarakter dizisini değiştirmek için kullanmamız gerekiyor .

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

Ve son fakat en az değil, bu çözüm sadece bu belirli problem için en uygun olanıdır. Gereksinim oldukça uzun bir dizeyi değiştirmek olduğunda en uygun olmayabilir, yani değiştirilecek karakter sayısı sayıca büyüktür.


1
Lütfen bu kodun soruyu nasıl yanıtladığını açıklamak için cevabınızı düzenleyin
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

İşte basit bir uzantı yöntemi:

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

Şöyle kullanın:

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

-2

En basit yolun bu olacağına inanıyorum: (stringbuilder olmadan)

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

Bu, tür dizesinin bir değişkeninin char değişkenleri dizisi olarak değerlendirilebildiği için çalışır. Örneğin, "myString" adlı bir dize değişkeninin myString [1] olarak ikinci karakterine başvurabilirsiniz.


Bu yalnızca yazarın örneğinde string, değiştirilecek karakterlerin her biri yalnızca bir kez oluştuğu için çalışır. Eğer buna karşı koşarsanız, deyin, "DBCDEFGHIE"o zaman elde edersiniz "ZBCZXFGHIX", ki bu istenen sonuç değildir. Ayrıca, bunun StringBuilderiki buçuk yıl önce yayınlanan diğer çözüm olmayanlardan daha basit olduğunu kabul etmem .
BACON
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.