@TotZam - proszę sprawdzić daty. Ten jest starszy niż ten, z którym łączysz się.
ToolmakerSteve
@ToolmakerSteve Zwykle patrzę na jakość pytań i odpowiedzi, a nie na daty, jak powiedziano tutaj . W tym przypadku wydaje mi się, że popełniłem błąd i kliknąłem niewłaściwy, aby oznaczyć jako zduplikowane, ponieważ jakość tego pytania jest oczywiście lepsza, dlatego też oznaczyłem drugie pytanie.
Tot Zam
@TotZam - ach, nie wiedziałem o tej rekomendacji - dziękuję za wskazanie. (Chociaż mylące jest zobaczenie czegoś starszego zgłoszonego jako duplikat czegoś nowszego, więc w takim przypadku warto byłoby wyraźnie wyjaśnić, że oznaczasz jako duplikat pytanie STARSZE, ponieważ link ma lepsze odpowiedzi.)
ToolmakerSteve
Odpowiedzi:
198
Najłatwiejszym sposobem dodawania i usuwania zakresów w ciągu jest użycie rozszerzenia StringBuilder.
var theString ="ABCDEFGHIJ";var aStringBuilder =newStringBuilder(theString);
aStringBuilder.Remove(3,2);
aStringBuilder.Insert(3,"ZX");
theString = aStringBuilder.ToString();
Alternatywą jest użycie String.Substring , ale myślę, że StringBuilderkod staje się bardziej czytelny.
Dlaczego używasz tutaj StringBuilder? Sprawdź odpowiedź V4Vendetta, która jest znacznie bardziej czytelna ...
Fortega
1
@Fortega Remove () i Insert () tworzą i zwracają nowy ciąg. Podejście StringBuilder pozwala uniknąć tego dodatkowego kosztu, pracując w wstępnie przydzielonej sekcji pamięci i przenosząc w niej znaki.
redcalx
@redcalx To prawda, ale wprowadzono dodatkowy koszt, wprowadzając obiekt StringBuilder i zmniejszając czytelność
Fortega
2
Jaki jest sens używania StringBuilder tutaj, jeśli wywołujesz Remove i Insert w taki sam sposób jak V4Vendetta, ale robi to bezpośrednio na łańcuchu? Wydaje się, że kod jest zbędny.
metabuddy
186
string s ="ABCDEFGH";
s= s.Remove(3,2).Insert(3,"ZX");
Wolę to od inicjowania nowego StringBuildera (...). Dzięki!
Michael Norgren
3
Nie wiem, dlaczego ktoś miałby używać do tej operacji stringbuildera. To dobra odpowiedź
NappingRabbit
43
ReplaceAt (int index, int length, string replace)
Oto metoda rozszerzenia, która nie używa StringBuilder ani Substring. Ta metoda pozwala również na wydłużenie ciągu zastępczego poza długość ciągu źródłowego.
//// 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 characterspublicstaticstringReplaceAt(thisstring str,int index,int length,string replace){return str.Remove(index,Math.Min(length, str.Length- index)).Insert(index, replace);}
Jeśli używasz tej funkcji, jeśli chcesz, aby cały zastępczy ciąg zastąpił jak najwięcej znaków, ustaw długość na długość zastępującego ciągu:
Wydaje mi się, że jest to bardziej wydajne, ponieważ klasa StringBuilder nie musi być inicjowana i ponieważ używa bardziej podstawowych operacji. Proszę, popraw mnie jeśli się mylę. :)
Stringbuilder jest bardziej wydajny, jeśli łańcuch jest dłuższy niż, powiedzmy, 200 znaków.
Tim Schmelter,
Bez stresu. Pracowałem prosto. Dzięki
D_Edet
9
Użyj String.Substring()(szczegóły tutaj ), aby wyciąć lewą część, następnie zamiennik, a następnie prawą część. Graj z indeksami, aż wszystko będzie dobrze :)
Stworzyłem trzy metody przy użyciu powyższych trzech metod (string.Remove / Insert, Stringbuilder.Remove / Insert i odpowiedź Daniel's Substring, a SubString był najszybszą metodą.
To działa, prawdopodobnie nie chcesz odkładać tponownie.
Pierre
4
Jeśli zależy Ci na wydajności, to czego chcesz tutaj uniknąć, są alokacje. A jeśli jesteś na .NET Rdzenia 2.1+ (lub, jak dotąd niepublikowany, .Net standardowe 2,1), a następnie można, korzystając z string.Createmetody :
To podejście jest trudniejsze do zrozumienia niż alternatywy, ale jest to jedyne, które przydzieli tylko jeden obiekt na wywołanie: nowo utworzony ciąg.
Wszystkie inne odpowiedzi nie działają, jeśli ciąg zawiera znak Unicode (jak emotikony), ponieważ znak Unicode ma więcej bajtów niż znak.
Przykład : emoji „🎶” przekonwertowane na bajty waży odpowiednik 2 znaków. Tak więc, jeśli znak Unicode zostanie umieszczony na początku ciągu, offsetparametr zostanie przesunięty).
W tym temacie rozszerzam klasę StringInfo na Replace by position, zachowując algorytm Nicka Millera, aby tego uniknąć:
dotnetfiddle.net/l0dqS5 Nie udało mi się doprowadzić do niepowodzenia innych metod w przypadku Unicode. Zmień skrzypce, aby zademonstrować swój przypadek użycia. Bardzo zainteresowany wynikami.
Markus
1
@MarkusHooge Spróbuj umieścić znaki Unicode na początku swojego ciągu, na przykład: dotnetfiddle.net/3V2K3Y . Zobaczysz, że tylko ostatnia linia działa dobrze i umieść znak na 4 miejscu (w innym przypadku znak Unicode zajmuje 2 znaki)
GGO
Masz rację, to nie jest jasne. Zaktualizowałem swoją odpowiedź, aby dodać więcej wyjaśnień, dlaczego inne odpowiedzi nie działają
GGO
1
Szukałem rozwiązania spełniającego następujące wymagania:
użyj tylko jednego, jednowierszowego wyrażenia
używaj tylko metod wbudowanych w system (brak niestandardowego narzędzia)
Rozwiązanie 1
Rozwiązanie, które najbardziej mi odpowiada, to:
// replace `oldString[i]` with `c`string newString =newStringBuilder(oldString).Replace(oldString[i], c, i,1).ToString();
Innym rozwiązaniem, które spełnia moje wymagania, jest użycie Substringz konkatenacją:
string newString = oldStr.Substring(0, i)+ c + oldString.Substring(i+1, oldString.Length);
To też jest w porządku. Wydaje mi się, że nie jest to tak wydajne, jak pierwsze wykonanie (z powodu niepotrzebnej konkatenacji ciągów). Ale przedwczesna optymalizacja jest źródłem wszelkiego zła .
Wybierz więc ten, który najbardziej Ci się podoba :)
Pozwól mi wyjaśnić moje rozwiązanie.
Biorąc pod uwagę stwierdzenie problemu polegające na zmianie łańcucha w jego dwóch określonych pozycjach („pozycja 4 do pozycji 5”) z dwoma znakami „Z” i „X”, a prośba o użycie indeksu pozycji do zmiany ciągu, a nie łańcucha Zamień ( ) (może to wynikać z możliwości powtórzenia się niektórych znaków w rzeczywistym ciągu), wolałbym zastosować minimalistyczne podejście do osiągnięcia celu niż użycie Substring()i string Concat()lub string Remove()i Insert()podejście. Choć wszystkie te rozwiązania będą służyć temu samemu celowi, zależy to tylko od osobistego wyboru i filozofii rozliczenia się z minimalistycznym podejściem.
Wracając do mojego rozwiązania, wspomnij powyżej, jeśli przyjrzymy się bliżej stringiStringBuilder, oba z nich wewnętrznie traktują dany ciąg jako tablicę znaków. Jeśli spojrzymy na implementacjęStringBuilderutrzymuje wewnętrzną zmienną, taką jak „ internal char[] m_ChunkChars;”, przechwytującą dany ciąg. Ponieważ jest to zmienna wewnętrzna, nie możemy bezpośrednio uzyskać do niej dostępu. Aby świat zewnętrzny mógł uzyskać dostęp i zmienić tę tablicę znaków, StringBuilderuwidacznia je za pomocą właściwości indeksatora, która wygląda jak poniżej
[IndexerName("Chars")]publiccharthis[int index]{get{StringBuilder stringBuilder =this;do{// … some codereturn stringBuilder.m_ChunkChars[index1];// … some more code}}set{StringBuilder stringBuilder =this;do{//… some code
stringBuilder.m_ChunkChars[index1]=value;return;// …. Some more code}}}
Moje rozwiązanie wspomniane powyżej wykorzystuje tę możliwość indeksowania do bezpośredniej zmiany wewnętrznie utrzymywanej tablicy znaków, która IMO jest wydajna i minimalistyczna.
BTW; powyższe rozwiązanie możemy przepisać dokładniej, jak poniżej
W tym kontekście chciałbym również wspomnieć, że w przypadku tego stringma również właściwość indeksatora jako sposób na ujawnienie swojej wewnętrznej tablicy znaków, ale w tym przypadku ma tylko właściwość Getter (i nie ma Settera), ponieważ string jest niezmienny z natury. I dlatego musimy użyć, StringBuilderaby zmienić tablicę znaków.
I wreszcie, to rozwiązanie jest najlepsze tylko dla tego konkretnego problemu, w którym prośba o zastąpienie tylko kilku znaków z góry znanym indeksem pozycji. Może nie być najlepszym rozwiązaniem, gdy wymagana jest zmiana dość długiego ciągu, tj. Liczba znaków do zmiany jest duża.
Myślę, że najprostszym sposobem byłoby to: (bez stringbuildera)
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]);}
To działa, ponieważ zmienna typu string może być traktowana jako tablica zmiennych typu char. Możesz na przykład odnieść się do drugiego znaku zmiennej łańcuchowej o nazwie „myString” jako myString [1]
To działa tylko dlatego, że w przykładzie autora stringznaki, które mają być zastąpione, występują tylko raz. Jeśli napotkasz to, powiedzmy, "DBCDEFGHIE"otrzymasz "ZBCZXFGHIX", co nie jest pożądanym rezultatem. Nie zgodziłbym się również, że jest to prostsze niż inne nie- StringBuilderrozwiązania, które zostały opublikowane dwa i pół roku temu.
Odpowiedzi:
Najłatwiejszym sposobem dodawania i usuwania zakresów w ciągu jest użycie rozszerzenia
StringBuilder
.Alternatywą jest użycie
String.Substring
, ale myślę, żeStringBuilder
kod staje się bardziej czytelny.źródło
źródło
ReplaceAt (int index, int length, string replace)
Oto metoda rozszerzenia, która nie używa StringBuilder ani Substring. Ta metoda pozwala również na wydłużenie ciągu zastępczego poza długość ciągu źródłowego.
Jeśli używasz tej funkcji, jeśli chcesz, aby cały zastępczy ciąg zastąpił jak najwięcej znaków, ustaw długość na długość zastępującego ciągu:
W przeciwnym razie możesz określić liczbę znaków, które zostaną usunięte:
Jeśli określisz długość na 0, ta funkcja działa tak samo jak funkcja wstawiania:
Wydaje mi się, że jest to bardziej wydajne, ponieważ klasa StringBuilder nie musi być inicjowana i ponieważ używa bardziej podstawowych operacji. Proszę, popraw mnie jeśli się mylę. :)
źródło
Użyj
String.Substring()
(szczegóły tutaj ), aby wyciąć lewą część, następnie zamiennik, a następnie prawą część. Graj z indeksami, aż wszystko będzie dobrze :)Coś jak:
źródło
Jako metoda rozszerzenia.
źródło
źródło
t
ponownie.Jeśli zależy Ci na wydajności, to czego chcesz tutaj uniknąć, są alokacje. A jeśli jesteś na .NET Rdzenia 2.1+ (lub, jak dotąd niepublikowany, .Net standardowe 2,1), a następnie można, korzystając z
string.Create
metody :To podejście jest trudniejsze do zrozumienia niż alternatywy, ale jest to jedyne, które przydzieli tylko jeden obiekt na wywołanie: nowo utworzony ciąg.
źródło
Możesz spróbować czegoś, co łączy to:
źródło
Tak jak inni wspomnieli,
Substring()
funkcja jest tam z jakiegoś powodu:Również zmierzyłem to w stosunku do
StringBuilder
rozwiązania i otrzymałem 900 tików w porównaniu z 875. Jest to więc nieco wolniejsze.źródło
"ABCDEFGHIJ"
i"ZX"
mają różne długości.Jeszcze inny
źródło
Z pomocą tego posta tworzę następującą funkcję z dodatkowymi kontrolami długości
źródło
Wszystkie inne odpowiedzi nie działają, jeśli ciąg zawiera znak Unicode (jak emotikony), ponieważ znak Unicode ma więcej bajtów niż znak.
W tym temacie rozszerzam klasę StringInfo na Replace by position, zachowując algorytm Nicka Millera, aby tego uniknąć:
źródło
Szukałem rozwiązania spełniającego następujące wymagania:
Rozwiązanie 1
Rozwiązanie, które najbardziej mi odpowiada, to:
To używa
StringBuilder.Replace(oldChar, newChar, position, count)
Rozwiązanie 2
Innym rozwiązaniem, które spełnia moje wymagania, jest użycie
Substring
z konkatenacją:To też jest w porządku. Wydaje mi się, że nie jest to tak wydajne, jak pierwsze wykonanie (z powodu niepotrzebnej konkatenacji ciągów). Ale przedwczesna optymalizacja jest źródłem wszelkiego zła .
Wybierz więc ten, który najbardziej Ci się podoba :)
źródło
Lepiej jest użyć
String.substr()
.Lubię to:
źródło
Pozwól mi wyjaśnić moje rozwiązanie.
Biorąc pod uwagę stwierdzenie problemu polegające na zmianie łańcucha w jego dwóch określonych pozycjach („pozycja 4 do pozycji 5”) z dwoma znakami „Z” i „X”, a prośba o użycie indeksu pozycji do zmiany ciągu, a nie łańcucha Zamień ( ) (może to wynikać z możliwości powtórzenia się niektórych znaków w rzeczywistym ciągu), wolałbym zastosować minimalistyczne podejście do osiągnięcia celu niż użycie
Substring()
i stringConcat()
lub stringRemove()
iInsert()
podejście. Choć wszystkie te rozwiązania będą służyć temu samemu celowi, zależy to tylko od osobistego wyboru i filozofii rozliczenia się z minimalistycznym podejściem.Wracając do mojego rozwiązania, wspomnij powyżej, jeśli przyjrzymy się bliżej
string
iStringBuilder
, oba z nich wewnętrznie traktują dany ciąg jako tablicę znaków. Jeśli spojrzymy na implementacjęStringBuilder
utrzymuje wewnętrzną zmienną, taką jak „internal char[] m_ChunkChars;
”, przechwytującą dany ciąg. Ponieważ jest to zmienna wewnętrzna, nie możemy bezpośrednio uzyskać do niej dostępu. Aby świat zewnętrzny mógł uzyskać dostęp i zmienić tę tablicę znaków,StringBuilder
uwidacznia je za pomocą właściwości indeksatora, która wygląda jak poniżejMoje rozwiązanie wspomniane powyżej wykorzystuje tę możliwość indeksowania do bezpośredniej zmiany wewnętrznie utrzymywanej tablicy znaków, która IMO jest wydajna i minimalistyczna.
BTW; powyższe rozwiązanie możemy przepisać dokładniej, jak poniżej
W tym kontekście chciałbym również wspomnieć, że w przypadku tego
string
ma również właściwość indeksatora jako sposób na ujawnienie swojej wewnętrznej tablicy znaków, ale w tym przypadku ma tylko właściwość Getter (i nie ma Settera), ponieważ string jest niezmienny z natury. I dlatego musimy użyć,StringBuilder
aby zmienić tablicę znaków.I wreszcie, to rozwiązanie jest najlepsze tylko dla tego konkretnego problemu, w którym prośba o zastąpienie tylko kilku znaków z góry znanym indeksem pozycji. Może nie być najlepszym rozwiązaniem, gdy wymagana jest zmiana dość długiego ciągu, tj. Liczba znaków do zmiany jest duża.
źródło
źródło
Oto prosta metoda rozszerzenia:
Użyj tego w ten sposób:
źródło
Myślę, że najprostszym sposobem byłoby to: (bez stringbuildera)
To działa, ponieważ zmienna typu string może być traktowana jako tablica zmiennych typu char. Możesz na przykład odnieść się do drugiego znaku zmiennej łańcuchowej o nazwie „myString” jako myString [1]
źródło
string
znaki, które mają być zastąpione, występują tylko raz. Jeśli napotkasz to, powiedzmy,"DBCDEFGHIE"
otrzymasz"ZBCZXFGHIX"
, co nie jest pożądanym rezultatem. Nie zgodziłbym się również, że jest to prostsze niż inne nie-StringBuilder
rozwiązania, które zostały opublikowane dwa i pół roku temu.