Як замінити частину рядка позицією?


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
було б краще
обговорити

Чому ви тут використовуєте 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 (...). Дякую!
Майкл Норгрен

3
Я не знаю, чому хтось би використовував stringbuilder для цієї операції. Це гарна відповідь
NappingRabbit

43

ReplaceAt (індекс, довжина int, заміна рядка)

Ось метод розширення, який не використовує StringBuilder або Substring. Цей спосіб також дозволяє рядку заміни подовжуватися на довжину вихідного рядка.

//// 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 символів.
Тім Шмелтер

Без стресу. Працювали прямо вгору. Дякую
D_Edet

9

Використовуйте String.Substring()(детальніше тут ), щоб вирізати ліву частину, потім вашу заміну, потім праву частину. Грайте з індексами, поки не зрозумієте правильно :)

Щось на зразок:

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

2
Я створив три способи, використовуючи вищевказані три методи (string.Remove / Insert, Stringbuilder.Remove / Insert і Substtring відповідь, а 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

Якщо ви дбаєте про продуктивність, то тут ви хочете уникати рішень. І якщо ви на .Net Ядра 2.1 + (або, ще НЕ видавався, .Net стандарт 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"мають різну довжину.
Джон Алексіу


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 більше байтів, ніж char.

Приклад : емоджи "🎶", перетворений в байти, буде важити в еквіваленті 2 знаків. Отже, якщо знак unicode розміщений на початку вашої рядка, offsetпараметр буде зміщений).

З цією темою я розширюю клас StringInfo на Замінити позицією, зберігаючи алгоритм Ніка Міллера, щоб уникнути цього:

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 char займає 2 char довжини)
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 працює, але вимагає виправлення: рядок newString = oldStr.Substring (0, i) + c + oldString.Substring (i + 1, oldString.Length- (i + 1));
Юра Г

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

Це працює, тому що змінна рядка типу може трактуватися як масив змінних char. Ви можете, наприклад, посилатися на другий символ рядкової змінної з назвою "myString" як myString [1]


Це працює лише тому, що в прикладі автора stringсимволи, які слід замінити, трапляються лише один раз. Якщо ви протистояте цьому, скажімо, "DBCDEFGHIE"тоді ви отримуєте "ZBCZXFGHIX", що не є бажаним результатом. Крім того, я не погоджуюся, що це простіше, ніж інші не StringBuilderрішення, які були розміщені два з половиною роки тому.
BACON
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.