Cara efisien untuk menghapus SEMUA spasi putih dari String?

358

Saya menelepon API REST dan menerima kembali respons XML. Ini mengembalikan daftar nama ruang kerja, dan saya sedang menulis IsExistingWorkspace()metode cepat . Karena semua ruang kerja terdiri dari karakter yang berdekatan tanpa spasi, saya mengasumsikan cara termudah untuk mengetahui apakah ruang kerja tertentu ada dalam daftar adalah dengan menghapus semua spasi putih (termasuk baris baru) dan melakukan ini (XML adalah string yang diterima dari web permintaan):

XML.Contains("<name>" + workspaceName + "</name>");

Saya tahu ini peka huruf besar-kecil, dan saya mengandalkan itu. Saya hanya perlu cara untuk menghapus semua spasi putih dalam string secara efisien. Saya tahu RegEx dan LINQ dapat melakukannya, tetapi saya terbuka untuk ide-ide lain. Saya kebanyakan hanya khawatir tentang kecepatan.

Corey Ogburn
sumber
6
Parsing XML dengan regex hampir sama buruknya dengan parsing HTML dengan regex .
dtb
3
@henk holterman; Lihat jawaban saya di bawah ini, regexp tampaknya bukan yang tercepat dalam semua kasus.
Henk J Meulekamp
Regex tampaknya tidak menjadi yang tercepat sama sekali. Saya telah merangkum hasil dari berbagai cara untuk menghapus spasi putih dari string. Ringkasannya ada dalam jawaban di bawah ini - stackoverflow.com/a/37347881/582061
Stian Standahl

Jawaban:

616

Ini adalah cara tercepat yang saya tahu, meskipun Anda mengatakan Anda tidak ingin menggunakan ekspresi reguler:

Regex.Replace(XML, @"\s+", "")
fitnah
sumber
1
Saya bisa menggunakan ekspresi reguler, saya tidak yakin apakah itu cara tercepat.
Corey Ogburn
1
Saya cukup yakin itu. Paling tidak di belakang layar Anda harus memeriksa setiap karakter, dan ini hanya melakukan pencarian linier.
fitnah
19
Bukankah seharusnya begitu Regex.Replace(XML, @"\s+", "")?
Jan-Peter Vos
61
Jika Anda berencana untuk melakukan ini lebih dari sekali, buat dan simpan instance Regex. Ini akan menghemat biaya pembuatannya setiap waktu, yang lebih mahal dari yang Anda kira. private static readonly Regex sWhitespace = new Regex(@"\s+"); public static string ReplaceWhitespace(string input, string replacement) { return sWhitespace.Replace(input, replacement); }
hypehuman
10
Bagi mereka yang baru mengenal RegEx dan mencari penjelasan tentang arti ungkapan ini, \sberarti "cocok dengan token spasi putih", dan +berarti "cocokkan dengan satu atau lebih dari token yang diproses". Juga RegExr adalah situs web yang bagus untuk berlatih menulis ekspresi RegEx, jika Anda ingin bereksperimen.
jrh
181

Saya punya cara alternatif tanpa regexp, dan tampaknya berkinerja cukup baik. Ini adalah kelanjutan jawaban Brandon Moretz:

 public static string RemoveWhitespace(this string input)
 {
    return new string(input.ToCharArray()
        .Where(c => !Char.IsWhiteSpace(c))
        .ToArray());
 }

Saya mengujinya dalam unit test sederhana:

[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace1(string input, string expected)
{
    string s = null;
    for (int i = 0; i < 1000000; i++)
    {
        s = input.RemoveWhitespace();
    }
    Assert.AreEqual(expected, s);
}

[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace2(string input, string expected)
{
    string s = null;
    for (int i = 0; i < 1000000; i++)
    {
        s = Regex.Replace(input, @"\s+", "");
    }
    Assert.AreEqual(expected, s);
}

Untuk 1.000.000 percobaan, opsi pertama (tanpa regexp) berjalan dalam waktu kurang dari satu detik (700 ms pada mesin saya), dan yang kedua membutuhkan 3,5 detik.

Henk J Meulekamp
sumber
40
.ToCharArray()tidak perlu; Anda dapat menggunakan .Where()langsung pada string.
ProgramFOX
10
Yang perlu diperhatikan di sini. Regex lebih lambat ... pada string kecil! Jika Anda mengatakan Anda memiliki versi digital dari Volume Undang-Undang Pajak AS (~ juta kata?), Dengan beberapa iterasi, sejauh ini Regex adalah raja! Ini bukan apa yang lebih cepat, tetapi apa yang harus digunakan dalam keadaan apa. Anda hanya membuktikan setengah persamaan di sini. -1 hingga Anda membuktikan paruh kedua tes sehingga jawabannya memberikan lebih banyak wawasan tentang apa yang harus digunakan.
Piotr Kula
17
@ ppkinkin Dia meminta penghapusan single pass spasi. Tidak banyak iterasi pemrosesan lainnya. Saya tidak akan membuat penghapusan spasi putih lintasan tunggal ini menjadi posting tambahan tentang benchmarking pemrosesan teks.
Henk J Meulekamp
1
Anda mengatakan lebih baik tidak menggunakan regex kali ini tetapi tidak mengatakan alasannya.
Piotr Kula
2
@ProgramFOX, dalam pertanyaan yang berbeda (tidak dapat dengan mudah menemukannya) Saya perhatikan bahwa setidaknya dalam beberapa pertanyaan, menggunakan ToCharArraylebih cepat daripada menggunakan .Where()langsung pada string. Ini ada hubungannya dengan overhead ke IEnumerable<>dalam setiap langkah iterasi, dan ToCharArrayyang sangat efisien (blok-copy) dan kompiler mengoptimalkan iterasi di atas array. Mengapa perbedaan ini ada, belum ada yang bisa menjelaskan kepada saya, tetapi ukur sebelum Anda menghapus ToCharArray().
Abel
87

Coba ganti metode string di C #.

XML.Replace(" ", string.Empty);
Mike_K
sumber
28
Tidak menghapus tab atau baris baru. Jika saya melakukan banyak penghapusan sekarang saya membuat banyak melewati string.
Corey Ogburn
11
Downvote untuk tidak menghapus semua spasi putih, seperti yang dilakukan oleh fitnah dan Henk.
Matt Sach
@ MattSach mengapa tidak menghapus SEMUA spasi putih?
Zapnologica
4
@Zapnologica Ini hanya mengganti karakter ruang. OP juga meminta penggantian baris baru (yang merupakan karakter "spasi", meskipun mereka bukan karakter spasi).
Matt Sach
75

Solusi saya adalah dengan menggunakan Split dan Gabung dan ini sangat cepat, bahkan yang tercepat dari jawaban teratas di sini.

str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));

Pengaturan waktu untuk 10.000 loop pada string sederhana dengan spasi dan baris baru tab

  • split / gabung = 60 milidetik
  • linq chararray = 94 milidetik
  • regex = 437 milidetik

Perbaiki ini dengan membungkusnya dalam metode untuk memberikan arti, dan juga menjadikannya metode ekstensi saat kita berada di ...

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
kode kernow
sumber
3
Saya sangat suka solusi ini, saya telah menggunakan yang serupa sejak pra-LINQ. Saya benar-benar terkesan dengan kinerja LINQ, dan agak terkejut dengan regex. Mungkin kode itu tidak seoptimal mungkin untuk regex (Anda harus men-cache objek regex misalnya). Tetapi inti masalahnya adalah bahwa "kualitas" data akan sangat berarti. Mungkin dengan string panjang regex akan mengungguli opsi lain. Ini akan menjadi tolok ukur yang menyenangkan untuk tampil ... :-)
Loudenvier
1
Bagaimana default (string []) == daftar semua karakter spasi putih? Saya melihatnya bekerja, tetapi saya tidak mengerti bagaimana caranya?
Jake Drew
5
@kernowcode Maksud Anda ambiguitas antara 2 kelebihan dengan string[]dan char[]? Anda hanya perlu menentukan mana yang Anda inginkan misalnya: string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));. Itulah sebenarnya yang dilakukan oleh panggilan defaultAnda dalam kasus ini karena ia kembali nulljuga: ia membantu kompiler untuk memutuskan mana yang akan dipilih. Karenanya komentar saya karena pernyataan dalam komentar Anda "Split membutuhkan array yang valid dan null tidak akan melakukan ..." salah. Bukan masalah besar, hanya dianggap layak disebut sejak Jake Drew bertanya bagaimana ini bekerja. +1 untuk jawaban Anda
Frank J
6
Ide keren ... tetapi saya akan melakukannya sebagai berikut:string.Concat("H \ne llo Wor ld".Split())
michaelkrisper
3
solusi michaelkrisper sangat mudah dibaca. Saya melakukan tes dan 'split / join' (162 milidetik) tampil lebih baik daripada 'split / concat' (180 milidetik) selama 10.000 iterasi dari string yang sama.
kernowcode
45

Membangun berdasarkan jawaban Henks Saya telah membuat beberapa metode pengujian dengan jawabannya dan beberapa metode yang ditambahkan dan lebih dioptimalkan. Saya menemukan hasil yang berbeda berdasarkan ukuran string input. Karena itu, saya telah menguji dengan dua set hasil. Dalam metode tercepat, sumber tertaut memiliki cara yang lebih cepat. Tapi, karena ini dicirikan sebagai tidak aman saya telah meninggalkan ini.

Input string yang panjang:

  1. InPlaceCharArray: 2021 ms ( jawaban Sunsetquest ) - ( Sumber asli )
  2. String split lalu gabung: 4277ms ( jawaban Kernowcode )
  3. Pembaca string: 6082 ms
  4. LINQ menggunakan char.IsWhitespace asli: 7357 ms
  5. LINQ: 7746 ms ( jawaban Henk )
  6. ForLoop: 32320 ms
  7. RegexCompiled: 37157 ms
  8. Regex: 42940 ms

Hasil input string pendek:

  1. InPlaceCharArray: 108 ms ( jawaban Sunsetquest ) - ( Sumber asli )
  2. String split lalu gabung: 294 ms ( jawaban Kernowcode )
  3. Pembaca string: 327 ms
  4. ForLoop: 343 ms
  5. LINQ menggunakan char.IsWhitespace asli: 624 ms
  6. LINQ: 645ms ( jawaban Henk )
  7. RegexCompiled: 1671 ms
  8. Regex: 2599 ms

Kode :

public class RemoveWhitespace
{
    public static string RemoveStringReader(string input)
    {
        var s = new StringBuilder(input.Length); // (input.Length);
        using (var reader = new StringReader(input))
        {
            int i = 0;
            char c;
            for (; i < input.Length; i++)
            {
                c = (char)reader.Read();
                if (!char.IsWhiteSpace(c))
                {
                    s.Append(c);
                }
            }
        }

        return s.ToString();
    }

    public static string RemoveLinqNativeCharIsWhitespace(string input)
    {
        return new string(input.ToCharArray()
            .Where(c => !char.IsWhiteSpace(c))
            .ToArray());
    }

    public static string RemoveLinq(string input)
    {
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }

    public static string RemoveRegex(string input)
    {
        return Regex.Replace(input, @"\s+", "");
    }

    private static Regex compiled = new Regex(@"\s+", RegexOptions.Compiled);
    public static string RemoveRegexCompiled(string input)
    {
        return compiled.Replace(input, "");
    }

    public static string RemoveForLoop(string input)
    {
        for (int i = input.Length - 1; i >= 0; i--)
        {
            if (char.IsWhiteSpace(input[i]))
            {
                input = input.Remove(i, 1);
            }
        }
        return input;
    }

    public static string StringSplitThenJoin(this string str)
    {
        return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
    }

    public static string RemoveInPlaceCharArray(string input)
    {
        var len = input.Length;
        var src = input.ToCharArray();
        int dstIdx = 0;
        for (int i = 0; i < len; i++)
        {
            var ch = src[i];
            switch (ch)
            {
                case '\u0020':
                case '\u00A0':
                case '\u1680':
                case '\u2000':
                case '\u2001':
                case '\u2002':
                case '\u2003':
                case '\u2004':
                case '\u2005':
                case '\u2006':
                case '\u2007':
                case '\u2008':
                case '\u2009':
                case '\u200A':
                case '\u202F':
                case '\u205F':
                case '\u3000':
                case '\u2028':
                case '\u2029':
                case '\u0009':
                case '\u000A':
                case '\u000B':
                case '\u000C':
                case '\u000D':
                case '\u0085':
                    continue;
                default:
                    src[dstIdx++] = ch;
                    break;
            }
        }
        return new string(src, 0, dstIdx);
    }
}

Tes :

[TestFixture]
public class Test
{
    // Short input
    //private const string input = "123 123 \t 1adc \n 222";
    //private const string expected = "1231231adc222";

    // Long input
    private const string input = "123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222";
    private const string expected = "1231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc222";

    private const int iterations = 1000000;

    [Test]
    public void RemoveInPlaceCharArray()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveInPlaceCharArray(input);
        }

        stopwatch.Stop();
        Console.WriteLine("InPlaceCharArray: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveStringReader()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveStringReader(input);
        }

        stopwatch.Stop();
        Console.WriteLine("String reader: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveLinqNativeCharIsWhitespace()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveLinqNativeCharIsWhitespace(input);
        }

        stopwatch.Stop();
        Console.WriteLine("LINQ using native char.IsWhitespace: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveLinq()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveLinq(input);
        }

        stopwatch.Stop();
        Console.WriteLine("LINQ: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveRegex()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveRegex(input);
        }

        stopwatch.Stop();
        Console.WriteLine("Regex: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveRegexCompiled()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveRegexCompiled(input);
        }

        stopwatch.Stop();
        Console.WriteLine("RegexCompiled: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveForLoop()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveForLoop(input);
        }

        stopwatch.Stop();
        Console.WriteLine("ForLoop: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [TestMethod]
    public void StringSplitThenJoin()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.StringSplitThenJoin(input);
        }

        stopwatch.Stop();
        Console.WriteLine("StringSplitThenJoin: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }
}

Sunting : Diuji liner yang bagus dari Kernowcode.

Stian Standahl
sumber
24

Hanya sebuah alternatif karena terlihat cukup bagus :) - CATATAN: Jawaban Henks adalah yang tercepat dari ini.

input.ToCharArray()
 .Where(c => !Char.IsWhiteSpace(c))
 .Select(c => c.ToString())
 .Aggregate((a, b) => a + b);

Menguji 1.000.000 loop aktif "This is a simple Test"

Metode ini = 1,74 detik
Regex = 2,58 detik
new String(Henks) = 0,82

BlueChippy
sumber
1
Mengapa ini diturunkan? Ini benar-benar dapat diterima, memenuhi persyaratan, bekerja lebih cepat daripada opsi RegEx dan sangat mudah dibaca?
BlueChippy
4
karena dapat ditulis jauh lebih pendek: string baru (input.Where (c =>! Char.IsWhiteSpace (c)). ToArray ());
Bas Smit
7
Mungkin benar - tetapi jawabannya masih tetap, dapat dibaca, lebih cepat dari regex dan menghasilkan hasil yang diinginkan. Banyak jawaban lain SETELAH yang ini ... oleh karena itu downvote tidak masuk akal.
BlueChippy
2
Apakah ada satuan untuk "0,82"? Atau apakah itu ukuran relatif (82%)? Bisakah Anda mengedit jawaban Anda agar lebih jelas?
Peter Mortensen
20

Saya menemukan artikel bagus tentang ini di CodeProject oleh Felipe Machado (dengan bantuan oleh Richard Robertson )

Dia menguji sepuluh metode berbeda. Yang ini adalah versi tercepat yang tidak aman ...

public static unsafe string TrimAllWithStringInplace(string str) {
    fixed (char* pfixed = str) {
        char* dst = pfixed;
        for (char* p = pfixed; *p != 0; p++)

            switch (*p) {

                case '\u0020': case '\u00A0': case '\u1680': case '\u2000': case '\u2001':

                case '\u2002': case '\u2003': case '\u2004': case '\u2005': case '\u2006':

                case '\u2007': case '\u2008': case '\u2009': case '\u200A': case '\u202F':

                case '\u205F': case '\u3000': case '\u2028': case '\u2029': case '\u0009':

                case '\u000A': case '\u000B': case '\u000C': case '\u000D': case '\u0085':
                    continue;

                default:
                    *dst++ = *p;
                    break;
            }

        return new string(pfixed, 0, (int)(dst - pfixed));
    }
}

Dan versi aman tercepat ...

public static string TrimAllWithInplaceCharArray(string str) {

    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;

    for (int i = 0; i < len; i++) {
        var ch = src[i];

        switch (ch) {

            case '\u0020': case '\u00A0': case '\u1680': case '\u2000': case '\u2001':

            case '\u2002': case '\u2003': case '\u2004': case '\u2005': case '\u2006':

            case '\u2007': case '\u2008': case '\u2009': case '\u200A': case '\u202F':

            case '\u205F': case '\u3000': case '\u2028': case '\u2029': case '\u0009':

            case '\u000A': case '\u000B': case '\u000C': case '\u000D': case '\u0085':
                continue;

            default:
                src[dstIdx++] = ch;
                break;
        }
    }
    return new string(src, 0, dstIdx);
}

Ada juga beberapa tolok ukur independen yang bagus pada Stack Overflow oleh Stian Standahl yang juga menunjukkan bagaimana fungsi Felipe sekitar 300% lebih cepat daripada fungsi tercepat berikutnya.

Sunsetquest
sumber
Saya sudah mencoba menerjemahkan ini ke C ++ tetapi saya agak macet. Adakah gagasan mengapa port saya mungkin gagal? stackoverflow.com/questions/42135922/…
Jon Cage
2
Saya tidak bisa menolak. Lihat di bagian komentar dari artikel yang Anda rujuk. Anda akan menemukan saya sebagai "Perangkat Lunak Basketcase". Dia dan mengerjakan ini bersama untuk sementara waktu. Saya benar-benar lupa tentang ini ketika masalah ini muncul lagi. Terima kasih untuk kenangan indahnya. :)
Richard Robertson
1
Dan bagaimana jika Anda ingin menghapus WS tambahan saja? Bagaimana dengan stackoverflow.com/questions/17770202/… mod ini?
Tom
Tercepat sedikit lebih lambat ;-) String sebagai perfom wadah lebih baik di sini (dalam aplikasi 4:15 sampai 3:55 => 8,5% lebih sedikit, tetapi ketika meninggalkan string 3:30 => 21,4% lebih sedikit dan profiller menunjukkan sekitar 50% dihabiskan di metode ini). Jadi dalam string nyata nyata harus sekitar 40% lebih cepat dibandingkan dengan konversi array (lambat) yang digunakan di sini.
Tom
15

Jika Anda membutuhkan kinerja luar biasa, Anda harus menghindari LINQ dan ekspresi reguler dalam kasus ini. Saya melakukan beberapa pembandingan kinerja, dan tampaknya jika Anda ingin menghapus ruang putih dari awal dan akhir string, string.Trim () adalah fungsi utama Anda.

Jika Anda perlu menghapus semua spasi putih dari string, metode berikut ini berfungsi paling cepat dari semua yang telah diposting di sini:

    public static string RemoveWhitespace(this string input)
    {
        int j = 0, inputlen = input.Length;
        char[] newarr = new char[inputlen];

        for (int i = 0; i < inputlen; ++i)
        {
            char tmp = input[i];

            if (!char.IsWhiteSpace(tmp))
            {
                newarr[j] = tmp;
                ++j;
            }
        }
        return new String(newarr, 0, j);
    }
JHM
sumber
Saya ingin tahu mengetahui detail dari tolok ukur Anda - bukan karena saya skeptis, tetapi saya ingin tahu tentang overhead yang terkait dengan Linq. Seberapa buruk itu?
Mark Meuer
Saya belum menjalankan kembali semua tes, tapi saya bisa mengingat ini: Segala sesuatu yang melibatkan Linq jauh lebih lambat daripada apa pun tanpa itu. Semua penggunaan fungsi string / char dan konstruktor yang cerdas tidak membuat perbedaan persentase jika Linq digunakan.
JHM
11

Regex berlebihan; cukup gunakan ekstensi pada string (terima kasih Henk). Ini sepele dan seharusnya menjadi bagian dari kerangka kerja. Bagaimanapun, ini implementasi saya:

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}
Maksood
sumber
ini pada dasarnya merupakan jawaban yang tidak perlu (regex berlebihan, tetapi apakah solusi lebih cepat daripada yang diberikan - dan sudah diterima?)
W1ll1amvl
Bagaimana Anda bisa menggunakan metode ekstensi Linq pada string? Tidak dapat menemukan yang mana yang saya lewatkan selainSystem.Linq
GGirard
Ok sepertinya ini tidak tersedia di PCL, IEnumerable <char> bersyarat dalam implementasi Microsoft String ... Dan saya menggunakan Profile259 yang tidak mendukung ini :)
GGirard
4

Berikut ini adalah alternatif linier sederhana untuk solusi RegEx. Saya tidak yakin mana yang lebih cepat; Anda harus membandingkannya.

static string RemoveWhitespace(string input)
{
    StringBuilder output = new StringBuilder(input.Length);

    for (int index = 0; index < input.Length; index++)
    {
        if (!Char.IsWhiteSpace(input, index))
        {
            output.Append(input[index]);
        }
    }
    return output.ToString();
}
Brandon Moretz
sumber
3

Saya perlu mengganti spasi putih dalam string dengan spasi, tetapi tidak menduplikasi spasi. misalnya, saya perlu mengonversi sesuatu seperti yang berikut:

"a b   c\r\n d\t\t\t e"

untuk

"a b c d e"

Saya menggunakan metode berikut

private static string RemoveWhiteSpace(string value)
{
    if (value == null) { return null; }
    var sb = new StringBuilder();

    var lastCharWs = false;
    foreach (var c in value)
    {
        if (char.IsWhiteSpace(c))
        {
            if (lastCharWs) { continue; }
            sb.Append(' ');
            lastCharWs = true;
        }
        else
        {
            sb.Append(c);
            lastCharWs = false;
        }
    }
    return sb.ToString();
}
pengguna1325543
sumber
2

Saya menganggap respons XML Anda terlihat seperti ini:

var xml = @"<names>
                <name>
                    foo
                </name>
                <name>
                    bar
                </name>
            </names>";

Cara terbaik untuk memproses XML adalah dengan menggunakan parser XML, seperti LINQ ke XML :

var doc = XDocument.Parse(xml);

var containsFoo = doc.Root
                     .Elements("name")
                     .Any(e => ((string)e).Trim() == "foo");
dtb
sumber
Setelah saya memverifikasi bahwa tag <name> tertentu memiliki nilai yang tepat, saya selesai. Bukankah parsing dokumen memiliki beberapa overhead?
Corey Ogburn
4
Tentu, ada beberapa overhead. Tetapi memiliki manfaat menjadi benar. Solusi yang berbasiskan misalnya pada regex jauh lebih sulit untuk diperbaiki. Jika Anda menentukan bahwa solusi LINQ to XML terlalu lambat, Anda selalu dapat menggantinya dengan sesuatu yang lebih cepat. Tetapi Anda harus menghindari berburu untuk implementasi yang paling efisien sebelum Anda tahu bahwa yang benar terlalu lambat.
dtb
Ini akan berjalan di server backend majikan saya. Ringan adalah yang saya cari. Saya tidak ingin sesuatu yang "hanya berfungsi" tetapi optimal.
Corey Ogburn
4
LINQ untuk XML merupakan salah satu cara yang paling ringan untuk benar bekerja dengan XML di NET
DTB
1

Inilah varian lain:

public static string RemoveAllWhitespace(string aString)
{
  return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}

Seperti sebagian besar solusi lainnya, saya belum melakukan tes benchmark lengkap, tetapi ini cukup berhasil untuk tujuan saya.

Fred
sumber
1

Kita bisa menggunakan:

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }
Tarik BENARAB
sumber
Ini hampir persis sama dengan jawaban Henk di atas. Satu-satunya perbedaan adalah bahwa Anda memeriksa null.
Corey Ogburn
Ya, periksa nol adalah importente
Tarik BENARAB
1
Mungkin ini seharusnya hanya komentar atas jawabannya. Saya senang Anda membawanya ke atas. Saya tidak tahu metode ekstensi dapat dipanggil pada objek nol.
Corey Ogburn
0

Saya telah menemukan hasil yang berbeda untuk menjadi kenyataan. Saya mencoba mengganti semua spasi putih dengan satu ruang dan regex sangat lambat.

return( Regex::Replace( text, L"\s+", L" " ) );

Apa yang bekerja paling optimal untuk saya (di C ++ cli) adalah:

String^ ReduceWhitespace( String^ text )
{
  String^ newText;
  bool    inWhitespace = false;
  Int32   posStart = 0;
  Int32   pos      = 0;
  for( pos = 0; pos < text->Length; ++pos )
  {
    wchar_t cc = text[pos];
    if( Char::IsWhiteSpace( cc ) )
    {
      if( !inWhitespace )
      {
        if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );
        inWhitespace = true;
        newText += L' ';
      }
      posStart = pos + 1;
    }
    else
    {
      if( inWhitespace )
      {
        inWhitespace = false;
        posStart = pos;
      }
    }
  }

  if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );

  return( newText );
}

Saya mencoba rutin di atas terlebih dahulu dengan mengganti setiap karakter secara terpisah, tetapi harus beralih ke melakukan substring untuk bagian non-spasi. Saat melamar ke 1.200.000 karakter string:

  • rutin di atas menyelesaikannya dalam 25 detik
  • penggantian karakter rutin di atas + terpisah dalam 95 detik
  • regex dibatalkan setelah 15 menit.
hvanbrug
sumber