C#文字列の複数の文字を置き換える


178

文字列を置き換えるより良い方法はありますか?

Replaceが文字配列または文字列配列を受け入れないことに驚いています。私は自分の拡張機能を書くことができると思いますが、以下を行うためのより良い組み込み方法があるかどうか知りたいですか?最後のReplaceは文字ではなく文字列であることに注意してください。

myString.Replace(';', '\n').Replace(',', '\n').Replace('\r', '\n').Replace('\t', '\n').Replace(' ', '\n').Replace("\n\n", "\n");

回答:


206

replace正規表現を使用できます。

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ 最初は検索を意味します
  • 間の文字[](任意の順序で)検索する文字です
  • 2番目/は、検索対象テキストと置換テキストを区切ります

英語では、これは次のようになります。

" ;または,or \tまたは\ror (スペース)または正確に2つのシーケンシャル\nを検索し、それを\n"で置き換えます

C#では、次の操作を実行できます:(インポート後System.Text.RegularExpressions

Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");

2
\tおよび\rに含まれてい\sます。したがって、正規表現はと同等[;,\s]です。
NullUserException

3
そして \s実際には同等な[ \f\n\r\t\v]ので、元の質問にはなかったものが含まれています。加えて、元の質問はReplace("\n\n", "\n")あなたの正規表現が処理しないものを求めます。
NullUserException

11
ユーザーが構成できない単純な置換操作の場合、正規表現を使用することは、通常の文字列操作に比べて非常に遅いため、最適ではないことを考慮してください。倍遅い。
あまりにも

ああ正規表現、権力の象形文字!ここで私が目にすることができる唯一の問題は、正規表現の人間の読みやすさです。多くはそれらを理解することを拒否します。最近、それほど複雑でない代替策を探している人のために、以下のソリューションを追加しました。
sɐunıɔןɐqɐp

では、複数の文字を複数の文字で置き換えたい場合、どのように書くのでしょうか?
HabipOğuz19年

114

特に賢く感じていて、Regexを使いたくない場合:

char[] separators = new char[]{' ',';',',','\r','\t','\n'};

string s = "this;is,\ra\t\n\n\ntest";
string[] temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
s = String.Join("\n", temp);

少しの労力でこれを拡張メソッドにラップすることもできます。

編集:または、2分間待つと、とにかくそれを書いてしまいます:)

public static class ExtensionMethods
{
   public static string Replace(this string s, char[] separators, string newVal)
   {
       string[] temp;

       temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
       return String.Join( newVal, temp );
   }
}

そして出来上がり...

char[] separators = new char[]{' ',';',',','\r','\t','\n'};
string s = "this;is,\ra\t\n\n\ntest";

s = s.Replace(separators, "\n");

特に大きな文字列の場合、メモリ効率が非常に低下します。
MarcinJuraszek

@MarcinJuraszek Lol ...組み込みの文字列メソッドは正規表現よりもメモリ効率が悪いと誰かが主張するのを聞いたのはおそらくこれが初めてでしょう。
ポールウォール

10
あなたが正しい。投稿する前に測定しておくべきだった。私はベンチマークを実行し、連続してRegex.Replace複数のstring.Replace呼び出しを行うよりも8倍以上遅いです。Split+ より4倍遅いJoingist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452を
MarcinJuraszek

1
素敵な解決策!ほんの小さなアドオン。残念ながら、これは最初の文字も同様に置き換えたい場合には機能しません。文字列例の「t」文字を置き換えたいとします。SplitEntryは、EmptyEntryであるため、最初の単語「this」の「t」を削除するだけです。RemoveEmptyEntriesの代わりにStringSplitOptions.Noneを使用すると、Splitはエントリを残し、Joinメソッドは代わりに区切り文字を追加します。これがお役に立てば
ピエール

58

LinqのAggregate関数を使用できます。

string s = "the\nquick\tbrown\rdog,jumped;over the lazy fox.";
char[] chars = new char[] { ' ', ';', ',', '\r', '\t', '\n' };
string snew = chars.Aggregate(s, (c1, c2) => c1.Replace(c2, '\n'));

拡張メソッドは次のとおりです。

public static string ReplaceAll(this string seed, char[] chars, char replacementCharacter)
{
    return chars.Aggregate(seed, (str, cItem) => str.Replace(cItem, replacementCharacter));
}

拡張メソッドの使用例:

string snew = s.ReplaceAll(chars, '\n');

21

これが最短の方法です。

myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");

1
この1つのライナーは、イニシャライザで必要な場合にも役立ちます。
Guney Ozsan、

8

ああ、パフォーマンスホラー!答えは少し時代遅れですが、それでも...

public static class StringUtils
{
    #region Private members

    [ThreadStatic]
    private static StringBuilder m_ReplaceSB;

    private static StringBuilder GetReplaceSB(int capacity)
    {
        var result = m_ReplaceSB;

        if (null == result)
        {
            result = new StringBuilder(capacity);
            m_ReplaceSB = result;
        }
        else
        {
            result.Clear();
            result.EnsureCapacity(capacity);
        }

        return result;
    }


    public static string ReplaceAny(this string s, char replaceWith, params char[] chars)
    {
        if (null == chars)
            return s;

        if (null == s)
            return null;

        StringBuilder sb = null;

        for (int i = 0, count = s.Length; i < count; i++)
        {
            var temp = s[i];
            var replace = false;

            for (int j = 0, cc = chars.Length; j < cc; j++)
                if (temp == chars[j])
                {
                    if (null == sb)
                    {
                        sb = GetReplaceSB(count);
                        if (i > 0)
                            sb.Append(s, 0, i);
                    }

                    replace = true;
                    break;
                }

            if (replace)
                sb.Append(replaceWith);
            else
                if (null != sb)
                    sb.Append(temp);
        }

        return null == sb ? s : sb.ToString();
    }
}

7

文字列は不変のchar配列です

あなたはそれを変更可能にする必要があります:

  • を使用して StringBuilder
  • unsafe世界に行ってポインターで遊んでください(ただし危険です)

そして、文字の配列を最小限の回数だけ反復してみます。HashSetループ内の文字シーケンスのトラバースを回避するため、ここに注意してください。さらに高速なルックアップが必要な場合は、(に基づいて)のHashSet最適化されたルックアップに置き換えることができます。chararray[256]

StringBuilderの例

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace, 
    char replacement)
{
    HashSet<char> set = new HashSet<char>(toReplace);
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set.Contains(currentCharacter))
        {
            builder[i] = replacement;
        }
    }
}

編集-最適化バージョン

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace,
    char replacement)
{
    var set = new bool[256];
    foreach (var charToReplace in toReplace)
    {
        set[charToReplace] = true;
    }
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set[currentCharacter])
        {
            builder[i] = replacement;
        }
    }
}

次に、次のように使用します。

var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();

その文字列がある忘れないでくださいwchar_t。ネットでは、すべての可能な文字のサブセットのみを交換する(そして、あなたはそれを...最適化するために、65536 boolsが必要です)
GOG

3

これらの文字列拡張メソッドを記述して、ソリューションのどこかに置くこともできます。

using System.Text;

public static class StringExtensions
{
    public static string ReplaceAll(this string original, string toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(toBeReplaced)) return original;
        if (newValue == null) newValue = string.Empty;
        StringBuilder sb = new StringBuilder();
        foreach (char ch in original)
        {
            if (toBeReplaced.IndexOf(ch) < 0) sb.Append(ch);
            else sb.Append(newValue);
        }
        return sb.ToString();
    }

    public static string ReplaceAll(this string original, string[] toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || toBeReplaced == null || toBeReplaced.Length <= 0) return original;
        if (newValue == null) newValue = string.Empty;
        foreach (string str in toBeReplaced)
            if (!string.IsNullOrEmpty(str))
                original = original.Replace(str, newValue);
        return original;
    }
}


次のように呼び出します。

"ABCDE".ReplaceAll("ACE", "xy");

xyBxyDxy


この:

"ABCDEF".ReplaceAll(new string[] { "AB", "DE", "EF" }, "xy");

xyCxyF



1

パフォーマンスの向上これはおそらく最善の解決策ではないかもしれませんが、機能します。

var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '\\', '<', '>', '*', '?', '/', ' ', '$', '!', '\'', '"', ':', '@' };
foreach (var singleChar in charArray)
{
   str = str.Replace(singleChar, '_');
}

1
string ToBeReplaceCharacters = @"~()@#$%&amp;+,'&quot;&lt;&gt;|;\/*?";
string fileName = "filename;with<bad:separators?";

foreach (var RepChar in ToBeReplaceCharacters)
{
    fileName = fileName.Replace(RepChar.ToString(), "");
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.