Bir C # dizesindeki birden çok karakteri değiştirme


178

Dizeleri değiştirmenin daha iyi bir yolu var mı?

Değiştir bir karakter dizisi veya dize dizisi almaz şaşırdım. Kendi uzantımı yazabileceğimi tahmin ettim ama aşağıdakileri yapmak için daha iyi bir yapının olup olmadığını merak ettim? Son Değiştir'in karakter değil, bir dize olduğuna dikkat edin.

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

Yanıtlar:


206

Normal ifadeyi değiştir işlevini kullanabilirsiniz.

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ başlangıçta bir arama anlamına gelir
  • Arasındaki karakterler [ve ](herhangi bir sırada) aramak için karakterlerdir
  • İkincisi /, arama metnini ve değiştirilen metni sınırlar

İngilizce olarak, bu okur:

" ;Veya ,veya \tveya \rveya (boşluk) veya tam olarak iki ardışık ara \nve değiştir \n"

C # 'da şunları yapabilirsiniz: (içe aktardıktan sonra System.Text.RegularExpressions)

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

2
\tve \riçine dahil edilir \s. Yani normal ifadeniz [;,\s].
NullUserException

3
Ve \saslında eşdeğerdir, [ \f\n\r\t\v]bu yüzden orijinal soruda olmayan bazı şeyleri dahil edersiniz. Ek olarak, asıl soru regex'inizin Replace("\n\n", "\n")işlemediği soruları sorar .
NullUserException

11
Bir kullanıcı tarafından yapılandırılamayan basit değiştirme işlemleri için, normal dize işlemlerine kıyasla çok yavaş olduğu için düzenli ifadeler kullanmanın en uygun olmadığını, "c # regex performans yerine" aradığımda bulduğum ilk karşılaştırmalı makaleye göre, lütfen 13 kat daha yavaş.
Çok

Ah regex, iktidar hiyeroglifleri! Burada görebildiğim tek konu, düzenli ifadelerin insan tarafından okunabilirliğidir; birçoğu onları anlamayı reddediyor. Son zamanlarda daha az karmaşık bir alternatif arayanlar için aşağıya bir çözüm ekledim.
sɐunıɔ ןɐ qɐp

Peki, birden çok karakteri birden çok karakterle değiştirmek istiyorsak nasıl yazabiliriz?
Habip Oğuz

114

Özellikle zeki hissediyorsanız ve Regex'i kullanmak istemiyorsanız:

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

Bunu biraz çaba harcamadan bir uzatma yöntemiyle de silebilirsiniz.

Düzenleme: Veya sadece 2 dakika bekleyin ve yine de yazacağım :)

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

Ve işte ...

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

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

Özellikle büyük karakter dizileri için çok bellek yetersiz.
MarcinJuraszek

@MarcinJuraszek Lol ... Muhtemelen ilk defa, yerleşik dize yöntemlerinin normal ifadelerden daha az bellek verimli olduğunu iddia ettiğini duydum.
Paul Walls

10
Haklısın. Bunu göndermeden önce ölçmeliydim. Ben benchmark çalıştırmak ve üst üste Regex.Replacebirden fazla string.Replaceçağrı daha 8 kat daha yavaş . ve Split+ ' dan 4 kat daha yavaştır Join. Bkz. Gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek

1
Güzel çözüm! sadece küçük bir addon. Maalesef, ilk karakterlerin de değiştirilmesini istiyorsanız bu işe yaramaz. Örnek dizedeki 't' karakterini değiştirmek istediğinizi varsayalım. Split yöntemi sadece bir `` this '' kelimesinin 't' değerini bırakacaktır, çünkü bu bir EmptyEntry'dir. RemoveEmptyEntries yerine StringSplitOptions.None öğesini kullanırsanız, Split girişten ayrılır ve Join yöntemi bunun yerine ayırıcı karakteri ekler. Umut etmek bu yardım etmek
Pierre

58

Linq'in Toplama işlevini kullanabilirsiniz:

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

Uzantı yöntemi şöyledir:

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

Uzantı yöntemi kullanım örneği:

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

21

Bu en kısa yol:

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

1
Bu tek astar, başlatıcılarda buna ihtiyacınız olduğunda da yardımcı olur.
Güney Ozsan

8

Ohhh, performans korkusu! Cevap biraz modası geçmiş, ama yine de ...

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

Dizeler sadece değişmez karakter dizileridir

Sadece değişebilir yapmalısın:

  • ya kullanarak StringBuilder
  • unsafedünyaya git ve işaretçilerle oyna (tehlikeli olsa da)

ve karakter dizisini en az kez tekrarlamaya çalışın. Not HashSeto döngü içinde karakter dizisini çapraz geçiş kaçınır gibi burada. Daha hızlı bir aramaya ihtiyacınız varsa, (için bir ) HashSetiçin optimize edilmiş bir aramayla değiştirebilirsiniz .chararray[256]

StringBuilder ile örnek

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

Düzenle - Optimize edilmiş sürüm

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

Sonra sadece böyle kullanın:

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

Dizelerin wchar_t
.net'te

3

Ayrıca, bu dize uzantısı yöntemlerini yazabilir ve çözümünüzde bir yere koyabilirsiniz:

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


Onlara şöyle deyin:

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

xyBxyDxy


Ve bu:

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

xyCxyF


2

RegEx.Replace kullanın, şöyle bir şey:

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

RegEx.Replace için bu MSDN belgeleri hakkında daha fazla bilgi


1

Performance-Wise bu muhtemelen en iyi çözüm olmayabilir ama işe yarıyor.

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(), "");
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.