ฉันจะเพิกเฉยต่อ UTF-8 Byte Order Marker ในการเปรียบเทียบสตริงได้อย่างไร

ฉันมีปัญหาในการเปรียบเทียบสตริงใน Unit Test ใน C# 4.0 โดยใช้ Visual Studio 2010 กรณีทดสอบเดียวกันนี้ทำงานอย่างถูกต้องใน Visual Studio 2008 (พร้อม C# 3.5)

นี่คือข้อมูลโค้ดที่เกี่ยวข้อง:

byte[] rawData = GetData();
string data = Encoding.UTF8.GetString(rawData);

Assert.AreEqual("Constant", data, false, CultureInfo.InvariantCulture);

ขณะทำการดีบั๊กการทดสอบนี้ สตริง data จะปรากฏด้วยตาเปล่าเพื่อให้มีสตริงเดียวกันกับตัวอักษรทุกประการ เมื่อฉันเรียก data.ToCharArray() ฉันสังเกตเห็นว่าไบต์แรกของสตริง data คือค่า 65279 ซึ่งเป็น UTF-8 Byte Order Marker สิ่งที่ฉันไม่เข้าใจคือทำไม Encoding.UTF8.GetString() เก็บไบต์นี้ไว้

ฉันจะรับ Encoding.UTF8.GetString() เพื่อ ไม่ ใส่ Byte Order Marker ในสตริงผลลัพธ์ได้อย่างไร

อัปเดต: ปัญหาคือ GetData() ซึ่งอ่านไฟล์จากดิสก์ อ่านข้อมูลจากไฟล์โดยใช้ FileStream.readbytes() ฉันแก้ไขสิ่งนี้โดยใช้ StreamReader และแปลงสตริงเป็นไบต์โดยใช้ Encoding.UTF8.GetBytes() ซึ่งเป็นสิ่งที่ควรทำตั้งแต่แรก! ขอบคุณสำหรับความช่วยเหลือทั้งหมด


person Skrud    schedule 26.05.2010    source แหล่งที่มา
comment
คุณสามารถโพสต์โปรแกรมเล็กๆ แต่ครบถ้วนที่แสดงให้เห็นถึงปัญหาได้หรือไม่   -  person Lasse V. Karlsen    schedule 26.05.2010


คำตอบ (3)


ฉันคิดว่าเป็นเพราะข้อมูลไบนารีดิบรวม BOM ไว้ด้วย คุณสามารถลบ BOM ด้วยตัวเองได้ตลอดเวลาหลังจากถอดรหัส หากคุณไม่ต้องการ แต่คุณควรพิจารณาว่าอาร์เรย์ไบต์ควรพิจารณา BOM เพื่อเริ่มต้นด้วยหรือไม่

แก้ไข: หรือคุณสามารถใช้ StreamReader เพื่อทำการถอดรหัส ต่อไปนี้คือตัวอย่าง การแสดงอาร์เรย์ไบต์เดียวกันที่ถูกแปลงเป็นอักขระสองตัวโดยใช้ Encoding.GetString หรืออักขระหนึ่งตัวผ่าน StreamReader:

using System;
using System.IO;
using System.Text;

class Test
{
    static void Main()
    {
        byte[] withBom = { 0xef, 0xbb, 0xbf, 0x41 };
        string viaEncoding = Encoding.UTF8.GetString(withBom);
        Console.WriteLine(viaEncoding.Length);

        string viaStreamReader;
        using (StreamReader reader = new StreamReader
               (new MemoryStream(withBom), Encoding.UTF8))
        {
            viaStreamReader = reader.ReadToEnd();           
        }
        Console.WriteLine(viaStreamReader.Length);
    }
}
person Jon Skeet    schedule 26.05.2010
comment
คุณพูดถูกที่ข้อมูลดิบรวม BOM ด้วย มันไม่ควร ดังนั้นฉันกำลังแก้ไขส่วนนั้น คำถามติดตามผลเชิงปรัชญา: เหตุใดวิธี String.Equals จึงคำนึงถึง BOM เหตุใดจึงไม่ละเลยเมื่อทำการเปรียบเทียบสตริงหรือถือเป็นข้อมูลเมตาและไม่ใช่เนื้อของสตริง - person Skrud; 26.05.2010
comment
@Skrud: คุณมีลำดับตัวละครที่แตกต่างกัน เมธอด String.Equals แบบดิบจะเปรียบเทียบลำดับตามลำดับโดยไม่มีการพิจารณาเพิ่มเติม อาจเป็นไปได้ว่าการเปรียบเทียบสตริงอื่นๆ บางส่วนที่มีอยู่ (การรับรู้ถึงวัฒนธรรม ฯลฯ) อาจเพิกเฉยต่อ BOM - ฉันไม่แน่ใจ เนื่องจากมันเป็นตัวละครที่แปลกในบางแง่ ฉันจึงไม่เชื่อว่าเป็นการเหมาะสมที่จะเพิกเฉยต่อมันตามอำเภอใจ พูดแบบนี้: ความล้มเหลวของความเท่าเทียมกันแสดงให้เห็นว่าคุณมีข้อมูลที่ไม่ดี ดังนั้นพฤติกรรมดังกล่าวจึงทำให้คุณปรับปรุงโค้ดของคุณ นั่นเป็นสิ่งที่ดีใช่ไหม? - person Jon Skeet; 26.05.2010
comment
อย่างแน่นอน. ซึ่งเป็นจุดทดสอบในตอนแรก :-) - person Skrud; 26.05.2010

มีวิธีที่มีประสิทธิภาพมากกว่าการสร้าง StreamReader และ MemoryStream เล็กน้อย:

1) หากคุณรู้ว่ามี BOM อยู่เสมอ

string viaEncoding = Encoding.UTF8.GetString(withBom, 3, withBom.Length - 3);

2) หากคุณไม่ทราบ ให้ตรวจสอบ:

string viaEncoding;
if (withBom.Length >= 3 && withBom[0] == 0xEF && withBom[1] == 0xBB && withBom[2] == 0xBF)
    viaEncoding = Encoding.UTF8.GetString(withBom, 3, withBom.Length - 3);
else
    viaEncoding = Encoding.UTF8.GetString(withBom);
person Tergiver    schedule 27.05.2010

ฉันเชื่อว่าอักขระพิเศษจะถูกลบออกหากคุณ Trim() สตริงที่ถอดรหัส

person JoeGeeky    schedule 26.05.2010