Birden çok alanı C # 'da tek bir boşlukla nasıl değiştiririm?


440

Bir dizede birden çok boşluğu C # 'da yalnızca bir boşlukla nasıl değiştirebilirim?

Misal:

1 2 3  4    5

olabilir:

1 2 3 4 5

1
bir devlet makinesi bunu kolayca yapabilir, ancak sadece boşlukları kaldırmak için ihtiyacınız varsa muhtemelen aşırıya kaçar
Adrian

Yinelenen bir soru stackoverflow.com/a/37592018/582061 içinde bunu yapmanın farklı yollarına bir kıyaslama ekledim . Regex bunu yapmanın en hızlı yolu değildi.
Stian Standahl

Yanıtlar:


468
string sentence = "This is a sentence with multiple    spaces";
RegexOptions options = RegexOptions.None;
Regex regex = new Regex("[ ]{2,}", options);     
sentence = regex.Replace(sentence, " ");

2
Bunu kopyalayıp yapıştırıyorum ve işe yarıyor. REgex'i gerçekten sevmiyorum ama bu sefer hayatımı kurtarıyor.
Pokus

9
@ Yorum yapmak yeterli, IMO. // Bu blok birden fazla alanı biriyle değiştirir ... :)
paulwhit

6
Gerçekten, RegEx bunun için aşırıya kaçmış.
Joel Coehoorn

11
@Joel: Kabul edemiyorum. Aslında, bu yolun yeterince büyük dizeler için sizden daha verimli olduğundan ve tek bir satırda yapılabileceğinden eminim. Overkill nerede?
Konrad Rudolph

24
@Oscar Joel'in kodu tüm karakterler arasında basit bir döngü değil! İkinci dereceden en kötü duruma sahip gizli bir iç içe döngü. Aksine, bu normal ifade doğrusaldır, sadece tek bir dize oluşturur (= Joel'in koduna kıyasla önemli ölçüde azaltılmış tahsis maliyetleri) ve ayrıca motor cehennemi optimize edebilir (dürüst olmak gerekirse, .NET regex olduğundan şüphe duyuyorum bunun için yeterince akıllı ama teoride bu düzenli ifade o kadar ucuza uygulanabilir ki artık komik bile değil; sadece üç durumlu bir DFA'ya, her biri bir geçişe ve ek bilgiye ihtiyaç duymaz).
Konrad Rudolph

624

Kullanmayı seviyorum:

myString = Regex.Replace(myString, @"\s+", " ");

Çünkü herhangi bir beyaz alanın (örn. Sekmeler, yeni satırlar, vb.) Çalışmalarını yakalar ve bunları tek bir boşlukla değiştirir.


43
Hafif değişiklik: Regex.Replace (kaynak, @ "(\ s) \ s +", "$ 1"); Bu, bulunan ilk boşluk türünü döndürür. 5 sekmeniz varsa, bir sekme döndürür. Birisi bunu tercih ederse.
FB ten Kate

@radistao Bağlantınız Javascript dizesi yerine, C # için değil.
Shiva

1
@Shiva, / \ s \ s + / standart bir POSIX normal ifade deyimidir ve kendi sözdizimi kullanılarak herhangi bir dilde dönüştürülebilir / kullanılabilir
radistao

4
@ FBtenKate'in çözümü ruhu ile: Regex.Replace (kaynak, @ "(\ s) \ 1+", "$ 1"); birbirini izleyen birden çok aynı karakteri tek bir karakterle değiştirir .
François Beaune

1
baştaki ve sondaki boşluk alanlarını kaldırmak için bununla birlikte Trim () işlevini kullanmalısınız, örneğin var myString = Regex.Replace (myString, @ "\ s +", "") .Trim ();
Harish Nayak

50
string xyz = "1   2   3   4   5";
xyz = string.Join( " ", xyz.Split( new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries ));

6
Bu regex üzerinde daha okunabilir, daha fazla tercih çünkü başka bir sözdizimi öğrenmek gerekmez
Michael Bahig

9
Seviyorum çünkü Regex gerekmiyor
AleX_

3
Bu büyük teller için verimsiz olacaktır.
DarcyThomas

3
Bu, önde gelen ve arkadaki boşlukları da kaldırır.
Matzi

1
Ben de bu cevabı tercih ederim. Eski hocası "Eğer bir sorununuz varsa zaman sen şimdi İKİ sorunları var ... Regex'in de çözmek gerektiğini düşünüyorum" derdi <göz>
William Madonna Jr.

38

Bence Matt'in cevabı en iyisi, ama bunun doğru olduğuna inanmıyorum. Yeni satırları değiştirmek istiyorsanız şunu kullanmalısınız:

myString = Regex.Replace(myString, @"\s+", " ", RegexOptions.Multiline);

4
RegexOptions.Multiline, ^ ve $ öğelerinin anlamını değiştirir, böylece tüm çok satırlı dize yerine her satırın başına ve sonuna ($ = \ n) eşleşirler. \ S [\ f \ n \ r \ t \ v] ile eşdeğer olduğu için, Çok Satırlı seçeneği kapalı olsa bile yeni satırlar değiştirilmelidir.
SushiGuy

1
Matt'in cevabı zaten bunu kapsıyor. 30 kişi sadece bu cevabı körü körüne
oyladı

26

LINQ kullanan başka bir yaklaşım:

 var list = str.Split(' ').Where(s => !string.IsNullOrWhiteSpace(s));
 str = string.Join(" ", list);

23

Bunlardan çok daha basit:

while(str.Contains("  ")) str = str.Replace("  ", " ");

23
Dize 3 veya daha fazla boşluk dizisi içeriyorsa, bu "{2,}" normal ifadesinden çok daha az verimli olacaktır.
Jan Goyvaerts

2
@ JanGoyvaerts: 10 boşluk olsa bile, hızlı ve kirli bir test yaptığımda normal ifade daha yavaştı. Bununla birlikte, while döngüsünün performansını tamamen öldürmek yalnızca boşluklarla dolu dev bir alt dizeyi alır. Adalet için, yavaş Regex.Replace yerine RegexOptions.Compiled kullandım.
Brian

5
RegexOptions.Compiled, normal ifadeyi IL'ye derleyen çok fazla ek yük ekler. Uygulamanız normal ifadeyi yeterli sıklıkta veya artan eşleme hızının azalan derleme hızını dengeleyeceği kadar büyük dizelerde kullanmadığı sürece kullanmayın.
Jan Goyvaerts

Bu, aşırı verimsiz kodun bir örneğidir. LOL.
pcbabu

1
@pcbabu Pek çok durumda göründüğü kadar kötü değil. Replace()Yöntem bu yüzden döngü (ve bir bütün dize yeniden tahsis) dizede eşleştirilmiş alanların her örneği için değil, belirli bir dizeye iki boşluk tüm tekrarlarını idare edecek. Yeni bir tahsis hepsini halledecektir. Döngüyü yalnızca 3 veya daha fazla boşluk olduğunda yeniden çalıştırırız; bu, birçok giriş kaynağı için daha nadir bir durumdur. Verileriniz için bir sorun haline geldiğini gösterebiliyorsanız, yeni bir dize oluşturucuya karakter karakter itmek için durum makinesini yazın.
Joel Coehoorn

21

Regex basit görevlerle bile oldukça yavaş olabilir. Bu, herhangi birinden kullanılabilecek bir uzantı yöntemi oluşturur string.

    public static class StringExtension
    {
        public static String ReduceWhitespace(this String value)
        {
            var newString = new StringBuilder();
            bool previousIsWhitespace = false;
            for (int i = 0; i < value.Length; i++)
            {
                if (Char.IsWhiteSpace(value[i]))
                {
                    if (previousIsWhitespace)
                    {
                        continue;
                    }

                    previousIsWhitespace = true;
                }
                else
                {
                    previousIsWhitespace = false;
                }

                newString.Append(value[i]);
            }

            return newString.ToString();
        }
    }

Bu şekilde kullanılır:

string testValue = "This contains     too          much  whitespace."
testValue = testValue.ReduceWhitespace();
// testValue = "This contains too much whitespace."


11

Sevmeyenler için Regex, burada aşağıdakileri kullanan bir yöntem vardır StringBuilder:

    public static string FilterWhiteSpaces(string input)
    {
        if (input == null)
            return string.Empty;

        StringBuilder stringBuilder = new StringBuilder(input.Length);
        for (int i = 0; i < input.Length; i++)
        {
            char c = input[i];
            if (i == 0 || c != ' ' || (c == ' ' && input[i - 1] != ' '))
                stringBuilder.Append(c);
        }
        return stringBuilder.ToString();
    }

Testlerimde, bu yöntem statik olarak derlenmiş Regex'e kıyasla çok büyük küçük-orta ölçekli dizelerle ortalama 16 kat daha hızlıydı. Derlenmemiş veya statik olmayan bir Regex ile karşılaştırıldığında, bu daha da hızlı olmalıdır.

O olmadığını, unutmayın değildir başında veya sonunda boşluk, böyle sadece birden fazla kopyasını çıkarın.


Karakterin sadece boşluk değil boşluk olup olmadığını kontrol etmek istiyorsanız aşağıdaki cevabımı görün .
Reap

8

Bunu tek satırlık bir çözümde yapabilirsiniz!

string s = "welcome to  london";
s.Replace(" ", "()").Replace(")(", "").Replace("()", " ");

İsterseniz diğer köşeli parantezleri (hatta diğer karakterleri) seçebilirsiniz.


1
Dizenizde "()" veya ") (" bulunmadığından emin olmalısınız. Veya "wel()come to london)("olur "wel come to london". Çok sayıda parantez kullanmayı deneyebilirsiniz. Bu yüzden ((((()))))yerine ()ve )))))(((((yerine kullanın )(. Yine de çalışır. Yine de, eğer dize içerir ((((()))))veya )))))(((((bu başarısız olur.
nmit026

7

Bu, Regexher çağrıldığında sınıfın yeni bir örneğini oluşturduğundan, yalnızca bir kez yapıyorsanız kullanılması gereken daha kısa bir sürümdür .

temp = new Regex(" {2,}").Replace(temp, " "); 

Düzenli ifadeler hakkında fazla bilgi sahibi değilseniz, kısa bir açıklama:

{2,}Bunu önceki karakter için regex arama yapar ve 2 ve sınırsız saatler arasında alt dizeleri bulur.
.Replace(temp, " ")Bir boşlukla dize sıcaklığında tüm eşleşmeleri değiştirir.

Bunu birden çok kez kullanmak istiyorsanız, derleme zamanında regex IL oluşturduğundan daha iyi bir seçenek var:

Regex singleSpacify = new Regex(" {2,}", RegexOptions.Compiled);
temp = singleSpacify.Replace(temp, " ");

7

no Regex, no Linq ... önde gelen ve sondaki boşlukları kaldırır ve gömülü birden çok boşluk parçasını bir boşluğa indirir

string myString = "   0 1 2  3   4               5  ";
myString = string.Join(" ", myString.Split(new char[] { ' ' }, 
StringSplitOptions.RemoveEmptyEntries));

sonuç: "0 1 2 3 4 5"


1
Dikkat edilmesi gereken bir nokta: Bölünmenin kullanımı, anlaşılması çok kolay olsa da, şaşırtıcı derecede olumsuz bir performans etkisine sahip olabilir. Çok sayıda dize oluşturulabildiğinden, bu yöntemle büyük dizeleri işlemeniz durumunda bellek kullanımınızı izlemeniz gerekir.
Pac0

5

Joel'e göre diğer cevapları birleştirmek ve umarım ilerledikçe biraz geliştirmek:

Bunu aşağıdakilerle yapabilirsiniz Regex.Replace():

string s = Regex.Replace (
    "   1  2    4 5", 
    @"[ ]{2,}", 
    " "
    );

Veya String.Split():

static class StringExtensions
{
    public static string Join(this IList<string> value, string separator)
    {
        return string.Join(separator, value.ToArray());
    }
}

//...

string s = "     1  2    4 5".Split (
    " ".ToCharArray(), 
    StringSplitOptions.RemoveEmptyEntries
    ).Join (" ");

3

Sadece sevdiğim yeni bir yazı yazdım Join, bu yüzden tekrar cevaplayacağımı düşündüm:

public static string Join<T>(this IEnumerable<T> source, string separator)
{
    return string.Join(separator, source.Select(e => e.ToString()).ToArray());
}

Bununla ilgili harika şeylerden biri, öğeler üzerinde ToString () öğesini çağırarak dize olmayan koleksiyonlarla çalışmasıdır. Kullanım hala aynı:

//...

string s = "     1  2    4 5".Split (
    " ".ToCharArray(), 
    StringSplitOptions.RemoveEmptyEntries
    ).Join (" ");

2
neden bir uzantı yöntemi oluşturmalı? neden sadece string.Join () kullanmıyorsunuz?
Eric Schoonover

3
      // Mysample string
            string str ="hi you           are          a demo";

            //Split the words based on white sapce
            var demo= str .Split(' ').Where(s => !string.IsNullOrWhiteSpace(s));

            //Join the values back and add a single space in between
                    str = string.Join(" ", demo);

//output: string str ="hi you are a demo";

2

Bunun oldukça eski olduğunu biliyorum, ama neredeyse aynı şeyi başarmaya çalışırken bununla karşılaştım. Bu çözümü RegEx Buddy'de buldum. Bu desen tüm çift boşlukları tek boşluklarla değiştirecek ve aynı zamanda ön ve arka boşlukları kırpacaktır.

pattern: (?m:^ +| +$|( ){2,})
replacement: $1

Boş alanla uğraştığımız için okumak biraz zor, bu yüzden yine "boşluklar" yerine "_" ile değiştiriliyor.

pattern: (?m:^_+|_+$|(_){2,})  <-- don't use this, just for illustration.

"(? M:" yapısı "çok satırlı" seçeneğini etkinleştirir.Genel olarak daha fazla kendi içinde kalması için genellikle desenin içine hangi seçenekleri dahil etmeyi severim.


2

Birçok cevap doğru çıktıyı sağlıyor, ancak en iyi performansları arayanlar için Nolanar'ın cevabını (performans için en iyi cevaptı) yaklaşık% 10 iyileştirdim .

public static string MergeSpaces(this string str)
{

    if (str == null)
    {
        return null;
    }
    else
    {
        StringBuilder stringBuilder = new StringBuilder(str.Length);

        int i = 0;
        foreach (char c in str)
        {
            if (c != ' ' || i == 0 || str[i - 1] != ' ')
                stringBuilder.Append(c);
            i++;
        }
        return stringBuilder.ToString();
    }

}

1

Bununla boşlukları kaldırabilirim

while word.contains("  ")  //double space
   word = word.Replace("  "," "); //replace double space by single space.
word = word.trim(); //to remove single whitespces from start & end.

evet ama sadece iki beyaz alanı bir tane ile değiştirirsiniz. Bu, X alan sayısına yardımcı olmaz
MGot90

1
Bu While döngüsü kaldırılacak tüm bu çift boşluklarla ilgilenecektir.
Learner1947

1

Normal ifade modelini kullanma

    [ ]+    #only space

   var text = Regex.Replace(inputString, @"[ ]+", " ");

1

bu yöntemi dene

private string removeNestedWhitespaces(char[] st)
{
    StringBuilder sb = new StringBuilder();
    int indx = 0, length = st.Length;
    while (indx < length)
    {
        sb.Append(st[indx]);
        indx++;
        while (indx < length && st[indx] == ' ')
            indx++;
        if(sb.Length > 1  && sb[0] != ' ')
            sb.Append(' ');
    }
    return sb.ToString();
}

şöyle kullanın:

string test = removeNestedWhitespaces("1 2 3  4    5".toCharArray());

Bu, arka boşlukları kaldıracaktır
The_Black_Smurf

hata için özür dilerim, ben kod düzeltildi, şimdi beklenen test dize olarak iş: "1 2 3 4 9" sonuç dizesi: "1 2 3 4 9"
Ahmed Aljaff

1

İşte Nolonar orijinal cevabı üzerinde küçük bir değişiklik .

Karakterin sadece boşluk değil boşluk olup olmadığını kontrol etmek için şunu kullanın:

Birden çok boşluk karakterini tek bir boşlukla değiştirir.

public static string FilterWhiteSpaces(string input)
{
    if (input == null)
        return string.Empty;

    var stringBuilder = new StringBuilder(input.Length);
    for (int i = 0; i < input.Length; i++)
    {
        char c = input[i];
        if (i == 0 || !char.IsWhiteSpace(c) || (char.IsWhiteSpace(c) && 
            !char.IsWhiteSpace(strValue[i - 1])))
            stringBuilder.Append(c);
    }
    return stringBuilder.ToString();
}

0

Eski skool:

string oldText = "   1 2  3   4    5     ";
string newText = oldText
                    .Replace("  ", " " + (char)22 )
                    .Replace( (char)22 + " ", "" )
                    .Replace( (char)22 + "", "" );

Assert.That( newText, Is.EqualTo( " 1 2 3 4 5 " ) );

0

Normal ifadeler kullanmadan:

while (myString.IndexOf("  ", StringComparison.CurrentCulture) != -1)
{
    myString = myString.Replace("  ", " ");
}

Kısa dizelerde kullanmak için Tamam, ancak çok fazla alana sahip uzun dizelerde kötü performans gösterecektir.


0

Dizeler için genişletme yöntemi olarak StringBuilder ve Enumerable.Aggregate () karışımı :

using System;
using System.Linq;
using System.Text;

public static class StringExtension
{
    public static string StripSpaces(this string s)
    {
        return s.Aggregate(new StringBuilder(), (acc, c) =>
        {
            if (c != ' ' || acc.Length > 0 && acc[acc.Length-1] != ' ')
                acc.Append(c);

            return acc;
        }).ToString();
    }

    public static void Main()
    {
        Console.WriteLine("\"" + StringExtension.StripSpaces("1   Hello       World  2   ") + "\"");
    }
}

Giriş:

"1   Hello       World  2   "

Çıktı:

"1 Hello World 2 "
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.