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?
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?
Yanıtlar:
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 StringBuilder
kodun daha okunabilir olduğunu düşünüyorum .
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
İş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. :)
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);
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();
}
}
string s = "ABCDEFG";
string t = "st";
s = s.Remove(4, t.Length);
s = s.Insert(4, t);
t
geri dönmek istemiyorsunuz .
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.Create
yö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.
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ş.
"ABCDEFGHIJ"
ve "ZX"
farklı uzunluklarda.
Yine bir başka
public static string ReplaceAtPosition(this string self, int position, string newValue)
{
return self.Remove(position, newValue.Length).Insert(position, newValue);
}
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");
}
}
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
offset
parametre 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) : ""
));
}
}
Aşağıdaki gereksinimleri içeren bir çözüm arıyordum:
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)
Gereksinimlerimi karşılayan diğer çözüm, Substring
birleş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 :)
Kullanmak daha iyidir String.substr()
.
Bunun gibi:
ReplString = GivenStr.substr(0, PostostarRelStr)
+ GivenStr(PostostarRelStr, ReplString.lenght());
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 string
veStringBuilder
, her ikisi de belirli bir dizeyi dahili olarak bir karakter dizisi olarak ele alır. UygulamasınaStringBuilder
internal 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, StringBuilder
bunları 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 StringBuilder
karakter 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.
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);
İş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();
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.
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 StringBuilder
iki buçuk yıl önce yayınlanan diğer çözüm olmayanlardan daha basit olduğunu kabul etmem .