วิธีที่ดีที่สุดในการลบอักขระสุดท้ายออกจากสตริงที่สร้างด้วย stringbuilder


92

ฉันมีดังต่อไปนี้

data.AppendFormat("{0},",dataToAppend);

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

ฉันต้องเปลี่ยนข้อมูลเป็นสตริงที่สตริงย่อยหรือไม่


10
string.Join(",", yourCollection)เหรอ? แก้ไข: เพิ่มเป็นคำตอบ
Vlad

1

@Chris: วิธีนี้คุณไม่จำเป็นต้องมี StringBuilder เลย
Vlad

บางทีคุณอาจหลีกเลี่ยงการเพิ่มลูกน้ำแทนที่จะลบออกในภายหลัง ดู: stackoverflow.com/questions/581448/… (คำตอบของ Jon Skeet)
Paolo Falabella

@ วลาดใช่ขอโทษฉันอ่านผิด; ฉันคิดว่าคุณเสนอให้เป็นข้อเสนอแนะในการแก้ไขสตริงที่สร้างขั้นสุดท้ายไม่ใช่เพื่อทดแทนลูปของเขาทั้งหมด (ฉันคิดว่าฉันลบความคิดเห็นของฉันทันเวลาเดาไม่ออก!)
Chris Sinclair

คำตอบ:


225

วิธีที่ง่ายและมีประสิทธิภาพที่สุดคือดำเนินการคำสั่งนี้:

data.Length--;

โดยการทำเช่นนี้คุณจะย้ายตัวชี้ (เช่นดัชนีสุดท้าย) กลับหนึ่งอักขระ แต่คุณจะไม่เปลี่ยนความไม่แน่นอนของวัตถุ ในความเป็นจริงการล้าง a StringBuilderทำได้ดีที่สุดLengthเช่นกัน (แต่จริงๆแล้วให้ใช้Clear()วิธีการเพื่อความชัดเจนแทนเพราะนั่นคือลักษณะการใช้งาน):

data.Length = 0;

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


2
re data.Length = 0;: นั่นคือสิ่งที่แน่นอนStringBuilder.Clearดังนั้นจึงควรใช้StringBuilder.Clearเพื่อความชัดเจนของเจตนา
Eren Ersönmez

@ ErenErsönmezเพื่อนที่ยุติธรรมฉันควรจะระบุให้ชัดเจนกว่านี้นั่นคือสิ่งที่Clear()ทำ แต่เรื่องตลก นี่คือบรรทัดแรกของClear()วิธีการ แต่คุณรู้หรือไม่ว่าจริงๆแล้วอินเทอร์เฟซจะออกไฟล์return this;. ตอนนี้แหละที่ฆ่าฉัน การตั้งค่าการLength = 0เปลี่ยนแปลงข้อมูลอ้างอิงที่คุณมีอยู่แล้วทำไมต้องกลับมาเอง?
Mike Perrenoud

12
ฉันคิดว่ามันเพื่อความสามารถในการใช้งานได้อย่างคล่องแคล่ว Appendส่งคืนตัวเองเช่นกัน
Eren Ersönmez

43

เพียงแค่ใช้

string.Join(",", yourCollection)

ด้วยวิธีนี้คุณไม่จำเป็นต้องใช้StringBuilderและวนซ้ำ




เพิ่มเติมเกี่ยวกับกรณี async ในปี 2019 ไม่ใช่การตั้งค่าที่หายากเมื่อข้อมูลมาแบบอะซิงโครนัส

ในกรณีที่ข้อมูลของคุณอยู่ในคอลเลกชัน async ไม่มีการซักเกินพิกัดstring.Join IAsyncEnumerable<T>แต่มันง่ายที่จะสร้างด้วยตนเองโดยแฮ็กโค้ดจากstring.Join :

public static class StringEx
{
    public static async Task<string> JoinAsync<T>(string separator, IAsyncEnumerable<T> seq)
    {
        if (seq == null)
            throw new ArgumentNullException(nameof(seq));

        await using (var en = seq.GetAsyncEnumerator())
        {
            if (!await en.MoveNextAsync())
                return string.Empty;

            string firstString = en.Current?.ToString();

            if (!await en.MoveNextAsync())
                return firstString ?? string.Empty;

            // Null separator and values are handled by the StringBuilder
            var sb = new StringBuilder(256);
            sb.Append(firstString);

            do
            {
                var currentValue = en.Current;
                sb.Append(separator);
                if (currentValue != null)
                    sb.Append(currentValue);
            }
            while (await en.MoveNextAsync());
            return sb.ToString();
        }
    }
}

หากข้อมูลมาแบบอะซิงโครนัส แต่IAsyncEnumerable<T>ไม่รองรับอินเทอร์เฟซ(เช่นที่กล่าวถึงในความคิดเห็นSqlDataReader) การรวมข้อมูลเป็นIAsyncEnumerable<T>:

async IAsyncEnumerable<(object first, object second, object product)> ExtractData(
        SqlDataReader reader)
{
    while (await reader.ReadAsync())
        yield return (reader[0], reader[1], reader[2]);
}

และใช้มัน:

Task<string> Stringify(SqlDataReader reader) =>
    StringEx.JoinAsync(
        ", ",
        ExtractData(reader).Select(x => $"{x.first} * {x.second} = {x.product}"));

เพื่อที่จะใช้งานคุณจะต้องใช้แพคเกจSelect nuget ที่นี่คุณสามารถหาตัวอย่างที่ compilableSystem.Interactive.Async


12
คำตอบที่ดีที่สุดไม่ใช่คำตอบที่ช่วยแก้ปัญหา แต่เป็นคำตอบที่ช่วยป้องกัน
LastTribunal

1
@Seabizkit: แน่นอน! คำถามทั้งหมดเกี่ยวกับ C # โดยวิธีการ
Vlad

1
@ วลาดฉันเข้าใจแล้วเพียงแค่ตรวจสอบสองครั้งในขณะที่ฉันทำการทดสอบง่ายๆเช่นการทดสอบดิบและพวกเขาก็ไม่ได้ผลลัพธ์เหมือนกัน string.Join(",", yourCollection)ยังคงมี,ในตอนท้าย ดังนั้นกล่าวstring.Join(",", yourCollection)คือไม่มีประสิทธิภาพและไม่ได้ลบออกด้วยตัวเอง
Seabizkit

2
@Seabizkit: แปลกมาก! คุณช่วยโพสต์ตัวอย่างได้ไหม ในรหัสของฉันมันทำงานได้อย่างสมบูรณ์: ideone.com/aj8PWR
Vlad

2
หลายปีของการใช้ลูปเพื่อสร้างตัวสร้างสตริงจากนั้นลบเครื่องหมายจุลภาคต่อท้ายและฉันก็ใช้สิ่งนี้ได้ ขอบคุณสำหรับทิป!
Caverman

11

ใช้สิ่งต่อไปนี้หลังการวนซ้ำ

.TrimEnd(',')

หรือเพียงแค่เปลี่ยนเป็น

string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)

4
เขาใช้ StringBuilder ไม่ใช่สตริง ยิ่งไปกว่านั้นมันค่อนข้างไม่ได้ผล: การแปลงเป็นสตริงแรกจากนั้นตัดแต่ง
Piotr Stapp

หรือstring.Join(",", input)
Tvde1

11

เกี่ยวกับเรื่องนี้ ..

string str = "The quick brown fox jumps over the lazy dog,";
StringBuilder sb = new StringBuilder(str);
sb.Remove(str.Length - 1, 1);

7

ฉันชอบจัดการความยาวของตัวสร้างสตริง:

data.Length = data.Length - 1;

4
ทำไมไม่เพียงdata.Length--หรือ--data.Length?
Gone Coding

ฉันมักจะใช้ data.Length - แต่ในกรณีหนึ่งฉันต้องย้ายกลับ 2 อักขระเนื่องจากค่าว่างหลังอักขระที่ฉันต้องการลบ การตัดแต่งไม่ทำงานในกรณีนั้นดังนั้น data.Length = data.Length - 2; ทำงาน.
Caverman

Trim ส่งคืนอินสแตนซ์ใหม่ของสตริงโดยจะไม่เปลี่ยนแปลงเนื้อหาของวัตถุ stringbuilder
bastos.sergio

@GoneCoding Visual Basic .NET ไม่รองรับ--หรือ++ คุณสามารถใช้ได้data.Length -= 1หรือคำตอบนี้ก็ใช้ได้เช่นกัน
Jason S

3

ฉันขอแนะนำให้คุณเปลี่ยนอัลกอริทึมการวนซ้ำของคุณ:

  • เพิ่มเครื่องหมายจุลภาคไม่ใช่หลังรายการ แต่ก่อน
  • ใช้ตัวแปรบูลีนที่ขึ้นต้นด้วย false และไม่กดเครื่องหมายจุลภาคตัวแรก
  • ตั้งค่าตัวแปรบูลีนเป็นจริงหลังจากทดสอบ

2
นี่อาจเป็นคำแนะนำที่มีประสิทธิภาพน้อยที่สุด (และต้องใช้รหัสเพิ่มเติม)
Gone Coding

1
ดู @Vlad answer
Noctis

3

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


2

ใช่แปลงเป็นสตริงเมื่อลูปเสร็จสิ้น:

String str = data.ToString().TrimEnd(',');

3
มันค่อนข้างไม่ได้ผล: การแปลงเป็นสตริงแรกจากนั้นตัดแต่ง
Piotr Stapp

2
@Garath ถ้าคุณหมายถึง "ไม่มีประสิทธิภาพ" ฉันก็ไม่เห็นด้วย แต่มันจะมีประสิทธิภาพ
DonBoitnott

2

คุณมีสองทางเลือก วิธีแรกเป็นRemoveวิธีการใช้งานที่ง่ายมากมันค่อนข้างมีประสิทธิภาพ วิธีที่สองคือการใช้ToStringกับดัชนีเริ่มต้นและดัชนีสิ้นสุด ( เอกสาร MSDN )



1

วิธีที่ง่ายที่สุดคือใช้เมธอด Join ():

public static void Trail()
{
    var list = new List<string> { "lala", "lulu", "lele" };
    var data = string.Join(",", list);
}

หากคุณต้องการ StringBuilder จริงๆให้ตัดเครื่องหมายจุลภาคท้ายหลังลูป:

data.ToString().TrimEnd(',');

4
data.ToString().TrimEnd(',');ไม่มีประสิทธิภาพ
bastos.sergio

1
นอกจากนี้คุณอาจไม่ต้องการแปลงวัตถุ StringBuilder เป็น String เนื่องจากอาจมีหลายบรรทัดที่ลงท้ายด้วย ","
Fandango68

0

ก๊อตช่า !!

คำตอบส่วนใหญ่ในชุดข้อความนี้จะใช้ไม่ได้หากคุณใช้สิ่งAppendLineต่อไปนี้:

var builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length--; // Won't work
Console.Write(builder.ToString());

builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length += -1; // Won't work
Console.Write(builder.ToString());

builder = new StringBuilder();
builder.AppendLine("One,");
Console.Write(builder.TrimEnd(',')); // Won't work

ซอฉัน

ทำไม??? @ (& ** (& @ !!

ปัญหานั้นง่ายมาก แต่ฉันต้องใช้เวลาพอสมควรในการคิดออก: เนื่องจากมีอักขระที่มองไม่เห็นอีก 2 ตัวในตอนท้ายCRและLF(Carriage Return และ Line Feed) ดังนั้นคุณต้องลบ 3 อักขระสุดท้าย:

var builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length -= 3; // This will work
Console.WriteLine(builder.ToString());

สรุปแล้ว

ใช้Length--หรือLength -= 1ถ้าวิธีสุดท้ายที่คุณเรียกคือAppend. ใช้ถ้าคุณวิธีสุดท้ายที่คุณเรียกว่าLength =- 3AppendLine

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