文字列の一部を位置で置き換える方法は?


155

私はこの文字列を持っています: ABCDEFGHIJ

位置4から位置5までを文字列に置き換える必要があります ZX

次のようになります。 ABCZXFGHIJ

しかし、一緒に使用しないでくださいstring.replace("DE","ZX")-私は位置で使用する必要があります

どうすればできますか?


@TotZam-日付を確認してください。これはリンクしたものより古いです。
ToolmakerSteve 2017

@ToolmakerSteve私は通常、日付ではなく質問と回答の質を確認します。この場合、間違いを犯し、間違ったものをクリックして重複としてマークしたようです。この質問の品質が明らかに優れているため、他の質問にフラグを付けました。
Tot Zam 2017

@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 2017

1
@Fortega Remove()およびInsert()は、それぞれ新しい文字列を作成して返します。StringBuilderアプローチは、事前に割り当てられたメモリのセクション内で作業し、その中で文字を移動することにより、この余分なコストを回避します。
redcalx

@redcalxそのとおりですが、StringBuilderオブジェクトを導入し、読みやすさを低下させることにより、追加コストが発生します
Fortega

2
V4Vendettaと同じ方法でRemoveおよびInsertを呼び出しているが、彼が文字列に対して直接それを行っている場合、ここでStringBuilderを使用する意味は何ですか?冗長なコードのようです。
メタバディ

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

17
私は新しいStringBuilder(...)を初期化するよりもこれを好みます。ありがとう!
Michael Norgren 2014年

3
誰かがこの操作にstringbuilderを使用する理由がわかりません。これは良い答えです
NappingRabbit

43

ReplaceAt(intインデックス、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
文字列が200文字より長い場合、Stringbuilderはより効率的です。
Tim Schmelter

ストレスがない。まっすぐに働きました。ありがとう
D_Edet

9

String.Substring()(詳細はこちら)を使用して左側のパーツをカットし、次に交換品、右側のパーツをカットします。正しくなるまでインデックスを操作してください:)

何かのようなもの:

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

2
私は上記の3行の方法を使用して3つのメソッドを作成した(string.Remove /インサートは、Stringbuilder.Remove /挿入とダニエルのサブストリングの回答、およびSUBSTRINGは最速の方法であった。
フランシーヌDeGroodテイラー

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

5
        string s = "ABCDEFG";
        string t = "st";
        s = s.Remove(4, t.Length);
        s = s.Insert(4, t);

これは機能し、おそらくt元に戻す必要はありません。
ピエール

4

パフォーマンスを重視する場合、ここで避けたいのは割り当てです。また、.Net Core 2.1+(またはまだリリースされていない.Net Standard 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));
        });
}

この方法は他の方法よりも理解が困難ですが、呼び出しごとに1つのオブジェクト(新しく作成された文字列)のみを割り当てる唯一の方法です。


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ソリューションに対して時間を計り、875 に対して900ティックを取得しました。そのため、少し遅くなります。


@ Aaron.S-いいえ、ありません。私の例"ABCDEFGHIJ""ZX"は、長さが異なります。
John

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文字(絵文字など)が含まれている場合、他のすべての回答は機能しません

:バイトに変換された絵文字「🎶」は、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で失敗させることができませんでした。使用例を示すためにフィドルを変更してください。結果に非常に興味があります。
Markus

1
@MarkusHooge次のように、文字列の先頭にUnicode文字を配置してみてください:dotnetfiddle.net/3V2K3Y。最後の行だけが適切に機能し、charを4位に配置します(他の場合では、Unicode charは2文字の長さになります)
GGO

あなたは正しい、それは明確ではない。回答を更新して、他の回答が機能しない理由の説明を追加しました
GGO

1

私は次の要件を持つソリューションを探していました:

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

これもOKです。(不必要な文字列連結のため)最初のパフォーマンスよりも効率的ではないと思います。しかし、時期尚早な最適化はすべての悪の根源です

だからあなたが一番好きなものを選んでください:)


ソリューション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();

私の解決策を説明しましょう。
2つの文字「Z」と「X」を持つ2つの特定の位置(「位置4から位置5」)の文字列を変更するという問題のステートメントがあり、位置のインデックスを使用して、文字列Replace( )メソッド(実際の文字列の一部の文字が繰り返される可能性があるため)、Substring()文字列Concat()または文字列Remove()Insert()アプローチよりも目的を達成するために、ミニマリストのアプローチを使用したいと思います。これらのソリューションはすべて、同じ目標を達成するための目的を果たしますが、それは、個人的な選択と、ミニマリストのアプローチで解決するという哲学に依存します。
上記の私の解決策に戻って、詳しく見てみるとstringStringBuilder、どちらも指定された文字列を内部的に文字の配列として扱います。実装を見ると、StringBuilderinternal 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が効率的でシンプルな内部的に維持されている文字配列を直接変更します。

ところで; 上記の解決策を以下のようにさらに詳しく書き直すことができます

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

このコンテキストではstring、内部の文字配列を公開する手段としてインデクサープロパティもあるが、この場合、文字列は本質的に不変であるため、ゲッタープロパティしかなく(セッターはない)ことにも言及します。これが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

私は最も簡単な方法はこれだと思います:(stringbuilderなし)

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

これは、string型の変数をchar変数の配列として処理できるため機能します。たとえば、 "myString"という名前の文字列変数の2番目の文字をmyString [1]として参照できます。


これは、作成者の例でstringは、置き換えられる文字がそれぞれ1回だけ出現するため、機能します。たとえば、これに対して実行すると、"DBCDEFGHIE"が得られますが"ZBCZXFGHIX"、これは望ましい結果ではありません。また、これがStringBuilder2年半前に投稿された他の非ソリューションよりも簡単であることに同意しません。
BACON
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.