Ganti beberapa karakter dalam string C #

178

Apakah ada cara yang lebih baik untuk mengganti string?

Saya terkejut bahwa Ganti tidak mengambil dalam array karakter atau array string. Saya kira saya bisa menulis ekstensi saya sendiri tetapi saya ingin tahu apakah ada cara yang lebih baik untuk melakukan hal berikut? Perhatikan Ganti terakhir adalah string bukan karakter.

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

Jawaban:

206

Anda dapat menggunakan ganti ekspresi reguler.

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ di awal berarti pencarian
  • Karakter antara [dan ]adalah karakter yang dicari (dalam urutan apa pun)
  • Yang kedua /membatasi pencarian-untuk teks dan teks pengganti

Dalam bahasa Inggris, ini berbunyi:

"Cari ;atau ,atau \tatau \ratau (spasi) atau tepat dua berurutan \ndan menggantinya dengan \n"

Di C #, Anda dapat melakukan hal berikut: (setelah mengimpor System.Text.RegularExpressions)

Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");
johnluetke
sumber
2
\tdan \rtermasuk dalam \s. Jadi regex Anda setara dengan [;,\s].
NullUserException
3
Dan \ssebenarnya setara dengan [ \f\n\r\t\v]jadi Anda memasukkan beberapa hal di sana yang tidak ada dalam pertanyaan awal. Selain itu, pertanyaan awal menanyakan Replace("\n\n", "\n")yang tidak ditangani oleh regex Anda.
NullUserException
11
Harap perhatikan bahwa untuk operasi penggantian sederhana yang tidak dapat dikonfigurasi oleh pengguna, menggunakan ekspresi reguler tidak optimal karena sangat lambat dibandingkan dengan operasi string biasa, menurut artikel benchmark pertama yang saya temukan saat mencari "c # regex performance replace" ini sekitar 13 waktu lebih lambat.
juga
Ah regex, hieroglif kekuasaan! Satu-satunya masalah yang bisa saya lihat di sini adalah keterbacaan manusia terhadap ekspresi reguler; banyak yang menolak untuk memahaminya. Saya baru-baru ini menambahkan solusi di bawah ini untuk mereka yang mencari alternatif yang tidak terlalu rumit.
sɐunıɔ ןɐ qɐp
Jadi bagaimana kita menulis jika kita ingin mengganti beberapa karakter dengan banyak karakter?
Habip Oğuz
114

Jika Anda merasa sangat pintar dan tidak ingin menggunakan 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);

Anda bisa membungkus ini dengan metode ekstensi dengan sedikit usaha juga.

Sunting: Atau hanya menunggu 2 menit dan akhirnya saya akan menulisnya :)

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

Dan voila ...

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

s = s.Replace(separators, "\n");
Paul Walls
sumber
Sangat tidak efisien memori, terutama untuk string yang lebih besar.
MarcinJuraszek
@MarcinJuraszek Lol ... Itu mungkin pertama kalinya saya pernah mendengar ada yang mengklaim bahwa metode string bawaan kurang efisien memori daripada ekspresi reguler.
Paul Walls
10
Kamu benar. Saya seharusnya mengukur sebelum saya memposting itu. Saya menjalankan benchmark dan Regex.Replacelebih dari 8x lebih lambat dari beberapa string.Replacepanggilan berturut-turut. dan 4x lebih lambat dari Split+ Join. Lihat gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek
1
Solusi bagus! hanya addon kecil. Sayangnya, ini tidak akan berfungsi jika Anda ingin karakter pertama diganti juga. Katakanlah Anda ingin mengganti karakter 't' dalam string contoh. Metode Split hanya akan menjatuhkan 't' dari kata pertama 'ini' karena itu adalah EmptyEntry. Jika Anda menggunakan StringSplitOptions.None bukan RemoveEmptyEntries, Split akan meninggalkan entri dan metode Bergabung akan menambahkan karakter pemisah sebagai gantinya. Semoga ini bisa membantu
Pierre
58

Anda bisa menggunakan fungsi Agregat Linq:

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

Inilah metode ekstensi:

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

Contoh penggunaan metode ekstensi:

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

Ini cara terpendek:

myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");
ParPar
sumber
1
Liner satu ini juga membantu saat Anda membutuhkan inisialisasi.
Guney Ozsan
8

Ohhh, horor kinerja! Jawabannya agak ketinggalan jaman, tapi tetap saja ...

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

String adalah array char yang tidak berubah

Anda hanya perlu membuatnya bisa berubah:

  • baik dengan menggunakan StringBuilder
  • pergi di unsafedunia dan bermain dengan pointer (meskipun berbahaya)

dan coba untuk mengulangi array karakter paling sedikit kali. Perhatikan di HashSetsini, karena ia menghindari untuk melintasi urutan karakter di dalam loop. Jika Anda membutuhkan pencarian yang lebih cepat, Anda bisa menggantinya HashSetdengan pencarian yang dioptimalkan char(berdasarkan pada array[256]).

Contoh dengan 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;
        }
    }
}

Edit - Versi yang dioptimalkan

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

Maka Anda cukup menggunakannya seperti ini:

var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();
Hebat
sumber
Ingatlah bahwa Strings ada wchar_tdi .net, Anda hanya mengganti sebagian dari semua karakter yang mungkin (dan Anda akan membutuhkan 65536 bools untuk mengoptimalkannya ...)
gog
3

Anda juga dapat menulis metode ekstensi string ini , dan menempatkannya di suatu tempat di solusi Anda:

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


Panggil mereka seperti ini:

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

xyBxyDxy


Dan ini:

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

xyCxyF

sɐunıɔ ןɐ qɐp
sumber
2

Gunakan RegEx.Replace, sesuatu seperti ini:

  string input = "This is   text with   far  too   much   " + 
                 "whitespace.";
  string pattern = "[;,]";
  string replacement = "\n";
  Regex rgx = new Regex(pattern);
  string result = rgx.Replace(input, replacement);

Berikut info lebih lanjut tentang dokumentasi MSDN ini untuk RegEx.Replace

Dmitry Samuylov
sumber
1

Kinerja-Bijaksana ini mungkin bukan solusi terbaik tetapi berhasil.

var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '\\', '<', '>', '*', '?', '/', ' ', '$', '!', '\'', '"', ':', '@' };
foreach (var singleChar in charArray)
{
   str = str.Replace(singleChar, '_');
}
Daniel Székely
sumber
1
string ToBeReplaceCharacters = @"~()@#$%&amp;+,'&quot;&lt;&gt;|;\/*?";
string fileName = "filename;with<bad:separators?";

foreach (var RepChar in ToBeReplaceCharacters)
{
    fileName = fileName.Replace(RepChar.ToString(), "");
}
Jignesh Bhayani
sumber