วิธีที่มีประสิทธิภาพในการลบช่องว่างทั้งหมดออกจาก String?


358

ฉันกำลังเรียก REST API และได้รับการตอบกลับ XML มันส่งคืนรายการชื่อพื้นที่ทำงานและฉันกำลังเขียนIsExistingWorkspace()วิธีการอย่างรวดเร็ว เนื่องจากเวิร์กสเปซทั้งหมดประกอบด้วยอักขระที่ต่อเนื่องกันโดยไม่มีช่องว่างฉันจึงคิดว่าวิธีที่ง่ายที่สุดในการค้นหาว่าเวิร์กสเปซที่เฉพาะเจาะจงอยู่ในรายการคือการลบช่องว่างทั้งหมด (รวมถึงบรรทัดใหม่) คำขอ):

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

ฉันรู้ว่ามันต้องตรงตามตัวพิมพ์ใหญ่ - เล็กและฉันก็พึ่งมัน ฉันต้องการวิธีลบช่องว่างทั้งหมดในสตริงอย่างมีประสิทธิภาพ ฉันรู้ว่า RegEx และ LINQ สามารถทำได้ แต่ฉันเปิดรับความคิดอื่น ๆ ฉันกังวลเกี่ยวกับความเร็วเป็นส่วนใหญ่


6
แยก XML ที่มี regex เกือบจะไม่ดีเท่าที่แยก HTML ที่มี regex
dtb

3
@henk holterman; ดูคำตอบของฉันด้านล่าง regexp ดูเหมือนจะไม่เร็วที่สุดในทุกกรณี
Henk J Meulekamp

Regex ดูเหมือนจะไม่เร็วที่สุดเลย ฉันได้สรุปผลลัพธ์จากหลายวิธีในการลบช่องว่างออกจากสตริง สรุปอยู่ในคำตอบด้านล่าง - stackoverflow.com/a/37347881/582061
Stian Standahl

คำตอบ:


616

นี่เป็นวิธีที่เร็วที่สุดที่ฉันรู้แม้ว่าคุณจะบอกว่าคุณไม่ต้องการใช้นิพจน์ทั่วไป:

Regex.Replace(XML, @"\s+", "")

1
ฉันสามารถใช้การแสดงออกปกติฉันไม่แน่ใจว่ามันเป็นวิธีที่เร็วที่สุด
Corey Ogburn

1
ฉันค่อนข้างแน่ใจว่ามันเป็น อย่างน้อยที่สุดเบื้องหลังคุณต้องตรวจสอบตัวละครทุกตัวและนี่เป็นการค้นหาเชิงเส้น
slandau

19
ไม่ควรจะเป็นอย่างนั้นRegex.Replace(XML, @"\s+", "")?
Jan-Peter Vos

61
หากคุณวางแผนที่จะทำมากกว่าหนึ่งครั้งให้สร้างและเก็บอินสแตนซ์ Regex สิ่งนี้จะช่วยประหยัดค่าใช้จ่ายในการสร้างมันทุกครั้งซึ่งมีราคาแพงกว่าที่คุณคิด private static readonly Regex sWhitespace = new Regex(@"\s+"); public static string ReplaceWhitespace(string input, string replacement) { return sWhitespace.Replace(input, replacement); }
hypehuman

10
สำหรับผู้ที่เพิ่งเริ่มใช้ RegEx และมองหาคำอธิบายว่านิพจน์นี้หมายถึงอะไรหมาย\sถึง "จับคู่กับช่องว่างใด ๆ ของโทเค็น" และ+หมายถึง "จับคู่หนึ่งโทเค็นที่ดำเนินการต่อไป" นอกจากนี้ RegExrเป็นเว็บไซต์ที่ดีสำหรับฝึกเขียนนิพจน์ RegEx ด้วยหากคุณต้องการทดลอง
jrh

181

ฉันมีทางเลือกโดยไม่ต้อง regexp และดูเหมือนว่าจะทำงานได้ดี มันเป็นความต่อเนื่องในคำตอบแบรนดอน Moretz:

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

ฉันทดสอบในการทดสอบหน่วยง่ายๆ:

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

สำหรับ 1,000,000 ครั้งตัวเลือกแรก (โดยไม่มี regexp) ทำงานในเวลาน้อยกว่าหนึ่งวินาที (700 ms บนเครื่องของฉัน) และตัวที่สองใช้เวลา 3.5 วินาที


40
.ToCharArray()ไม่จำเป็น; คุณสามารถใช้.Where()โดยตรงบนสตริง
ProgramFOX

10
เพียงแค่ต้องทราบที่นี่ Regex ช้าลง ... ในสายเล็ก ๆ ! ถ้าคุณบอกว่าคุณมีหนังสือฉบับหนึ่งฉบับดิจิทัลเกี่ยวกับกฎหมายภาษีของสหรัฐอเมริกา (~ ล้านคำ?) ด้วยการทำซ้ำเพียงไม่กี่ครั้ง Regex ก็คือราชา! ไม่ใช่สิ่งที่เร็วกว่า แต่ควรใช้ในกรณีใด คุณพิสูจน์ได้แค่ครึ่งสมการตรงนี้ -1 จนกว่าคุณจะพิสูจน์ในช่วงครึ่งหลังของการทดสอบเพื่อให้คำตอบให้ข้อมูลเชิงลึกมากขึ้นว่าควรใช้อะไรเมื่อใด
Piotr Kula

17
@ppumkin เขาขอให้ลบช่องว่างรอบเดียว ไม่ใช่การวนซ้ำหลายครั้งของการประมวลผลอื่น ๆ ฉันจะไม่ทำการลบช่องว่างรอบเดียวในโพสต์แบบขยายเกี่ยวกับการประมวลผลข้อความเปรียบเทียบ
Henk J Meulekamp

1
คุณบอกว่าไม่ต้องการใช้ regex ในครั้งนี้ แต่ไม่ได้บอกว่าทำไม
Piotr Kula

2
@ProgramFOX ในคำถามอื่น (หาไม่พบอย่างง่ายดาย) ฉันสังเกตว่าอย่างน้อยในบางคำถามการใช้ToCharArrayเร็วกว่าการใช้.Where()โดยตรงบนสตริง สิ่งนี้เกี่ยวข้องกับค่าใช้จ่ายIEnumerable<>ในแต่ละขั้นตอนการวนซ้ำและToCharArrayการมีประสิทธิภาพมาก (การคัดลอกบล็อก) และคอมไพเลอร์ช่วยปรับการวนซ้ำให้เหมาะสมกับอาร์เรย์ ทำไมความแตกต่างนี้มีอยู่แล้วไม่มีใครได้รับสามารถที่จะอธิบายฉัน ToCharArray()แต่วัดก่อนที่คุณจะลบ
Abel

87

ลองใช้เมธอด replace ของสตริงใน C #

XML.Replace(" ", string.Empty);

28
ไม่ลบแท็บหรือบรรทัดใหม่ หากฉันลบหลายครั้งตอนนี้ฉันกำลังทำหลาย ๆ ผ่านสายอักขระ
Corey Ogburn

11
ลงคะแนนเพราะไม่ลบช่องว่างทั้งหมดตามคำตอบของ slandau และ Henk
Matt Sach

@MattSach ทำไมมันไม่ลบช่องว่างทั้งหมด?
Zapnologica

4
@Zapnologica มันเป็นเพียงการแทนที่อักขระช่องว่าง OP ขอให้แทนที่บรรทัดใหม่ด้วย (ซึ่งเป็นอักขระ "ช่องว่าง" แม้ว่าจะไม่ใช่อักขระเว้นวรรค)
Matt Sach

75

โซลูชันของฉันคือใช้ Split and Join และเป็นไปอย่างรวดเร็วอย่างน่าประหลาดใจจริงๆแล้วเป็นคำตอบที่เร็วที่สุดที่นี่

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

กำหนดเวลา 10,000 ลูปบนสตริงอย่างง่ายพร้อมช่องว่างรวมบรรทัดใหม่และแท็บ

  • แยก / เข้าร่วม = 60 มิลลิวินาที
  • linq chararray = 94 มิลลิวินาที
  • regex = 437 มิลลิวินาที

ปรับปรุงสิ่งนี้โดยห่อมันด้วยวิธีเพื่อให้ความหมายและทำให้เป็นส่วนขยายในขณะที่เราอยู่ที่ ...

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

3
ฉันชอบวิธีนี้จริง ๆ ฉันใช้คล้ายกันมาตั้งแต่ก่อนวัน LINQ ฉันประทับใจในประสิทธิภาพของ LINQ จริง ๆ และค่อนข้างประหลาดใจกับ regex บางทีรหัสอาจไม่เหมาะสมเท่าที่ควรสำหรับ regex (ตัวอย่างเช่นคุณต้องแคชวัตถุ regex) แต่ปัญหาของปัญหาคือ "คุณภาพ" ของข้อมูลจะมีความสำคัญมาก บางทีด้วยสตริงยาว regex จะทำได้ดีกว่าตัวเลือกอื่น ๆ มันจะเป็นมาตรฐานที่สนุกสนานในการแสดง ... :-)
Loudenvier

1
ค่าเริ่มต้น (สตริง []) == รายการอักขระช่องว่างทั้งหมดได้อย่างไร ฉันเห็นมันใช้งานได้ แต่ฉันไม่เข้าใจว่าอย่างไร
Jake Drew

5
@kernowcode คุณหมายถึงความกำกวมระหว่าง 2 overloads กับstring[]และchar[]? string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));คุณเพียงแค่ต้องระบุที่หนึ่งที่คุณต้องการเช่น: นั่นคือสิ่งที่คุณเรียกร้องให้defaultทำในกรณีนี้เพราะมันส่งกลับnullเช่นกัน: มันช่วยให้คอมไพเลอร์ตัดสินใจได้ว่าจะเลือกโอเวอร์โหลดใด ดังนั้นความคิดเห็นของฉันเพราะคำสั่งในความคิดเห็นของคุณ "การแยกต้องการอาร์เรย์ที่ถูกต้องและค่า null จะไม่ทำ ... " เป็นเท็จ ไม่ใช่เรื่องใหญ่แค่คิดว่าควรจะพูดถึงตั้งแต่ Jake Drew ถามว่ามันทำงานอย่างไร +1 สำหรับคำตอบของคุณ
Frank J

6
ไอเดียเด็ด ๆ ... แต่ฉันจะทำดังนี้:string.Concat("H \ne llo Wor ld".Split())
michaelkrisper

3
วิธีการแก้ปัญหา michaelkrisper สามารถอ่านได้มาก ฉันทำการทดสอบและ 'แยก / เข้าร่วม' (162 มิลลิวินาที) ทำงานได้ดีกว่า 'แยก / เชื่อมต่อ' (180 มิลลิวินาที) สำหรับการทำซ้ำ 10,000 ครั้งของสตริงเดียวกัน
kernowcode

45

การสร้างคำตอบของ Henksฉันได้สร้างวิธีการทดสอบบางอย่างโดยใช้คำตอบของเขาและบางวิธีก็เพิ่มประสิทธิภาพมากขึ้น ฉันพบว่าผลลัพธ์ต่างกันตามขนาดของสตริงอินพุต ดังนั้นฉันได้ทดสอบสองชุดผลลัพธ์ ในวิธีที่เร็วที่สุดแหล่งที่เชื่อมโยงมีวิธีที่เร็วกว่า แต่เนื่องจากมันเป็นลักษณะที่ไม่ปลอดภัยฉันได้ออกจากนี้

ผลลัพธ์สตริงที่อินพุตแบบยาว:

  1. InPlaceCharArray: 2021 ms ( คำตอบของ Sunsetquest ) - ( แหล่งต้นฉบับ )
  2. การแตกสตริงแล้วเข้าร่วม: 4277ms ( คำตอบของ Kernowcode )
  3. ตัวอ่านสตริง: 6082 ms
  4. LINQ โดยใช้เนทีฟ char.IsWhitespace: 7357 ms
  5. LINQ: 7746 ms ( คำตอบของ Henk )
  6. ForLoop: 32320 ms
  7. RegexCompiled: 37157 ms
  8. Regex: 42940 ms

ผลลัพธ์สตริงอินพุตแบบสั้น:

  1. InPlaceCharArray: 108 ms ( คำตอบของ Sunsetquest ) - ( แหล่งต้นฉบับ )
  2. แยกสตริงแล้วเข้าร่วม: 294 ms ( คำตอบของ Kernowcode )
  3. เครื่องอ่านสตริง: 327 ms
  4. ForLoop: 343 ms
  5. LINQ โดยใช้เนทีฟ char.IsWhitespace: 624 ms
  6. LINQ: 645ms ( คำตอบของ Henk )
  7. RegexCompiled: 1671 ms
  8. Regex: 2599 ms

รหัส :

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

การทดสอบ :

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

แก้ไข : ทดสอบสายการบินที่ดีจาก Kernowcode


24

เป็นทางเลือกเพราะมันดูค่อนข้างดี :) - หมายเหตุ: คำตอบของ Henksคือสิ่งที่รวดเร็วที่สุด

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

ทดสอบ 1,000,000 ลูป "This is a simple Test"

วิธีนี้ = 1.74 วินาที
Regex = 2.58 วินาที
new String(Henks) = 0.82


1
ทำไมเรื่องนี้จึงถูกลดระดับลง เป็นที่ยอมรับอย่างสมบูรณ์ตรงตามข้อกำหนดทำงานเร็วกว่าตัวเลือก RegEx และอ่านง่ายมาก?
BlueChippy

4
เพราะมันสามารถเขียนได้สั้นกว่ามาก: สตริงใหม่ (input.Where (c =>! Char.IsWhiteSpace (c)) ToArray ());
Bas Smit

7
อาจเป็นจริง - แต่คำตอบยังคงอยู่สามารถอ่านได้เร็วกว่า regex และสร้างผลลัพธ์ที่ต้องการ คำตอบอื่น ๆ อีกมากมายอยู่หลังจากนี้ ... ดังนั้น downvote ไม่สมเหตุสมผล
BlueChippy

2
มีหน่วยสำหรับ "0.82" หรือไม่ หรือมันเป็นตัวชี้วัดที่เกี่ยวข้อง (82%)? คุณสามารถแก้ไขคำตอบเพื่อให้ชัดเจนขึ้นได้หรือไม่?
Peter Mortensen

20

ฉันพบบทความที่น่าสนใจเกี่ยวกับเรื่องนี้ใน CodeProject โดย Felipe Machado (ด้วยความช่วยเหลือจากRichard Robertson )

เขาทดสอบสิบวิธีที่ต่างกัน อันนี้เป็นรุ่นที่ไม่ปลอดภัยเร็วที่สุด...

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

และรุ่นที่ปลอดภัยที่สุด...

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

นอกจากนี้ยังมีเกณฑ์มาตรฐานอิสระที่ดีเกี่ยวกับ Stack Overflow ของ Stian Standahl ที่แสดงให้เห็นว่าฟังก์ชั่นของ Felipe นั้นเร็วกว่าฟังก์ชั่นถัดไปเร็วที่สุดประมาณ 300%


ฉันได้ลองแปลสิ่งนี้เป็นภาษา C ++ แล้ว แต่ฉันติดอยู่เล็กน้อย ความคิดใด ๆ ที่พอร์ตของฉันอาจจะล้มเหลว stackoverflow.com/questions/42135922/…
Jon Cage

2
ฉันไม่สามารถต้านทาน ดูในส่วนความเห็นของบทความที่คุณอ้างถึง คุณจะพบว่าฉันเป็น "ซอฟต์แวร์ตะกร้า" เขาและทำงานด้วยกันในขณะนี้ ฉันลืมเรื่องนี้ไปหมดแล้วเมื่อปัญหานี้กลับมาอีกครั้ง ขอบคุณสำหรับความทรงจำที่ดี :)
Richard Robertson

1
และถ้าคุณต้องการลบ WS พิเศษเท่านั้น stackoverflow.com/questions/17770202/… mod นี้เกี่ยวข้องกับอะไร?
Tom

เร็วที่สุดช้ากว่า ;-) สตริงเป็นภาชนะใส่น้ำหอมดีกว่าที่นี่ (ในแอพ 4:15 ถึง 3:55 => 8.5% น้อยลง แต่เมื่อเหลือสตริง 3:30 => 21.4% น้อยกว่าและผู้สร้างโปรไฟล์ใช้เวลาประมาณ 50% ใน วิธีนี้) ดังนั้นในสตริงสดจริงควรจะเร็วกว่าประมาณ 40% เมื่อเทียบกับการแปลงอาเรย์ (ช้า) ที่ใช้ที่นี่
Tom

15

หากคุณต้องการประสิทธิภาพที่ยอดเยี่ยมคุณควรหลีกเลี่ยง LINQ และนิพจน์ทั่วไปในกรณีนี้ ฉันทำการเปรียบเทียบประสิทธิภาพแล้วและดูเหมือนว่าถ้าคุณต้องการตัดพื้นที่สีขาวตั้งแต่ต้นจนจบของสตริง string.Trim () เป็นฟังก์ชันสุดท้ายของคุณ

หากคุณต้องการตัดช่องว่างสีขาวทั้งหมดออกจากสตริงวิธีการต่อไปนี้จะทำงานได้เร็วที่สุดจากทั้งหมดที่โพสต์ไว้ที่นี่:

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

ฉันอยากรู้รายละเอียดของการเปรียบเทียบของคุณไม่ใช่ว่าฉันสงสัย แต่ฉันอยากรู้เกี่ยวกับค่าใช้จ่ายที่เกี่ยวข้องกับ Linq มันเลวร้ายแค่ไหน?
ทำเครื่องหมาย Meuer

ฉันยังไม่ได้ทำการทดสอบทั้งหมดอีกครั้ง แต่ฉันจำได้มาก: ทุกสิ่งที่เกี่ยวข้องกับ Linq นั้นช้ากว่ามากหากไม่มีมัน การใช้งานฟังก์ชั่น string / char และตัวสร้างที่ชาญฉลาดทั้งหมดนั้นไม่ได้สร้างความแตกต่างเปอร์เซ็นต์หากใช้ Linq
JHM

11

Regex เกินความสามารถ เพียงใช้ส่วนขยายของสตริง (ขอบคุณ Henk) นี่เป็นเรื่องเล็กน้อยและควรเป็นส่วนหนึ่งของกรอบ อย่างไรก็ตามนี่คือการดำเนินการของฉัน:

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}

นี่เป็นพื้นคำตอบที่ไม่จำเป็น (regex มากเกินไป แต่เป็นวิธีที่เร็วกว่าที่กำหนด - และเป็นที่ยอมรับแล้ว?)
W1ll1amvl

คุณจะใช้วิธีการขยาย Linq บนสตริงได้อย่างไร? ไม่สามารถคิดออกซึ่งใช้ฉันกำลังคนอื่น ๆ หายไปกว่าSystem.Linq
GGirard

ตกลงดูเหมือนว่านี่ไม่สามารถใช้งานได้ใน PCL IEnumerable <char> นั้นมีเงื่อนไขในการใช้งานสตริงของ Microsoft ... และฉันใช้ Profile259 ซึ่งไม่รองรับการทำงานนี้ :)
GGirard

4

นี่คือทางเลือกเชิงเส้นอย่างง่ายสำหรับโซลูชัน RegEx ฉันไม่แน่ใจว่าเร็วกว่าใคร คุณจะต้องเปรียบเทียบมัน

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

3

ฉันต้องการแทนที่ white space ในสตริงด้วยช่องว่าง แต่ไม่ใช่ช่องว่างที่ซ้ำกัน เช่นฉันต้องการแปลงบางอย่างดังต่อไปนี้:

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

ถึง

"a b c d e"

ฉันใช้วิธีการดังต่อไปนี้

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

2

ฉันถือว่าการตอบสนอง XML ของคุณเป็นดังนี้:

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

วิธีที่ดีที่สุดในการประมวลผล XML คือการใช้ตัวแยกวิเคราะห์ XML เช่นLINQ ถึง XML :

var doc = XDocument.Parse(xml);

var containsFoo = doc.Root
                     .Elements("name")
                     .Any(e => ((string)e).Trim() == "foo");

เมื่อฉันยืนยันว่าแท็ก <name> เฉพาะมีค่าที่เหมาะสมฉันก็เสร็จแล้ว การแยกวิเคราะห์เอกสารจะมีค่าใช้จ่ายหรือไม่
Corey Ogburn

4
แน่นอนว่ามันมีค่าใช้จ่าย แต่มีประโยชน์ในการแก้ไขให้ถูกต้อง วิธีการแก้ปัญหาเช่นบน regex ยากมากที่จะได้รับสิทธิ หากคุณพิจารณาว่าโซลูชัน LINQ to XML ช้าเกินไปคุณสามารถแทนที่มันได้ด้วยสิ่งที่เร็วกว่าเสมอ แต่คุณควรหลีกเลี่ยงการล่าสัตว์เพื่อให้เกิดประสิทธิภาพสูงสุดก่อนที่คุณจะรู้ว่าสิ่งที่ถูกต้องนั้นช้าเกินไป
dtb

นี่กำลังจะทำงานในเซิร์ฟเวอร์แบ็กเอนด์ของนายจ้าง น้ำหนักเบาเป็นสิ่งที่ฉันกำลังมองหา ฉันไม่ต้องการสิ่งที่ "เพิ่งได้ผล" แต่เหมาะสมที่สุด
Corey Ogburn

4
LINQ to XML เป็นหนึ่งในวิธีที่มีน้ำหนักเบาที่สุดในการทำงานกับ XML ใน. NET อย่างถูกต้อง
dtb

1

นี่คือตัวแปรอื่น:

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

เช่นเดียวกับโซลูชันอื่น ๆ ส่วนใหญ่ฉันไม่ได้ทำการทดสอบเกณฑ์มาตรฐานอย่างละเอียด แต่ก็ใช้งานได้ดีพอสำหรับวัตถุประสงค์ของฉัน


1

เราสามารถใช้:

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

นี่เกือบจะเหมือนกับคำตอบของ Henk ด้านบน nullแต่ที่แตกต่างคือการที่คุณตรวจสอบ
Corey Ogburn

ใช่ตรวจสอบว่า null เป็น importente
Tarik BENARAB

1
บางทีนี่อาจเป็นเพียงความเห็นต่อคำตอบของเขา ฉันดีใจที่คุณนำมันขึ้นมา ฉันไม่รู้ว่าสามารถเรียกใช้ส่วนขยายบนวัตถุที่ไม่มีค่าได้
Corey Ogburn

0

ฉันพบว่าผลลัพธ์ที่แตกต่างเป็นจริง ฉันพยายามที่จะแทนที่ช่องว่างทั้งหมดด้วยช่องว่างเดียวและ regex ช้ามาก

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

สิ่งที่ทำงานได้ดีที่สุดสำหรับฉัน (ใน C ++ cli) คือ:

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

ฉันลองรูทีนด้านบนก่อนโดยแทนที่อักขระแต่ละตัวแยกกัน แต่ต้องสลับไปใช้การทำ substrings สำหรับส่วนที่ไม่มีพื้นที่ เมื่อใช้กับสตริงอักขระ 1,200,000 ตัว:

  • รูทีนด้านบนทำให้เสร็จภายใน 25 วินาที
  • รูทีนด้านบน + การแทนที่อักขระแยกกันใน 95 วินาที
  • regex ยกเลิกหลังจาก 15 นาที
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.