อ่านและเขียนไปยังไฟล์ไบนารี่?

ฉันกำลังบันทึกคลาสออบเจ็กต์ที่สร้างขึ้นเอง (ข้อมูลสตรีม) ลงในไฟล์

ฉันต้องสามารถโหลดเนื้อหาของไฟล์ลงใน TStringList เพื่อให้ฉันสามารถต่อท้ายบรรทัดใหม่ต่อท้ายไฟล์ จากนั้นจึงบันทึกการเปลี่ยนแปลง

สิ่งนี้ไม่ทำงานเนื่องจาก LoadFromFile ไม่สามารถแยกวิเคราะห์ไฟล์ได้อย่างถูกต้อง ฉันคิดว่าเพราะตัวละครตลกที่ Stream Saves to File เป็นและ TStringList คาดหวังข้อมูลที่เป็นข้อความธรรมดา

ฉันจะทำอย่างไรต่อไปนี้:

  • อ่านไฟล์ Raw Binary ลงใน TStringList
  • เพิ่มบรรทัดใหม่ของฉัน เช่น StringList1.Add(MyString);
  • บันทึกไฟล์ Raw Binary อีกครั้ง

คำถามนี้เกี่ยวข้องกับคำถามอื่นที่ฉันถาม: บันทึกค่า CRC ในไฟล์ โดยไม่เปลี่ยนแปลงการตรวจสอบ CRC จริงใช่หรือไม่

นี่คือสิ่งที่ฉันพยายามทำ:

  • คำนวณการตรวจสอบ CRC ของไฟล์สตรีมที่บันทึกไว้ของฉัน
  • เพิ่มค่า CRC ที่ส่วนท้ายของไฟล์
  • บันทึกไฟล์อีกครั้ง

เมื่อฉันพยายามเปิดไฟล์สตรีมของฉัน:

  • กำหนดค่า CRC (ที่ส่วนท้ายของไฟล์) ให้กับตัวแปร
  • ลบค่า CRC ออกจากไฟล์
  • บันทึกไฟล์สตรีมเป็นไฟล์ชั่วคราวใหม่
  • คำนวณและเปรียบเทียบ CRC ของไฟล์ชั่วคราว โดยที่ CRC เก็บไว้ในตัวแปร
  • หาก CRC ของไฟล์ตรงกับค่า CRC ที่เก็บไว้ภายใน ฉันสามารถประมวลผลไฟล์ได้ตามปกติ

แต่ฉันไม่รู้วิธีอ่านหรือเขียนข้อมูลไบนารีดิบของไฟล์

ฉันจะขอบคุณถ้ามีคนสามารถช่วยและคำแนะนำให้ฉันได้ ขอบคุณ :)


person Community    schedule 23.12.2011    source แหล่งที่มา
comment
คุณกำลังบันทึกเช็คซัมลงในไฟล์ข้อความใช่ไหม ถูกต้องไหม? ฉันยังไม่เข้าใจว่าเหตุใดคุณจึงต้องมีเช็คซัม   -  person David Heffernan    schedule 23.12.2011
comment
เมื่อบันทึกลงในไฟล์สตรีม ฉันกำลังทำเช่นนี้เพื่อว่าทุกครั้งที่เปิดไฟล์จากแอปพลิเคชันของฉัน ฉันจะสามารถระบุได้ว่าแท้จริงแล้วไฟล์นั้นถูกบันทึกจากโปรแกรมของฉันหรือไม่   -  person    schedule 23.12.2011
comment
คุณต้องการให้ไฟล์สามารถอ่านได้ในโปรแกรมแก้ไขข้อความหรือไม่? FWIW วิธีการปัจจุบันของคุณสิ้นเปลืองอย่างมากด้วยการอ่านและเขียนปลอม   -  person David Heffernan    schedule 23.12.2011
comment
คุณใช้ Delphi เวอร์ชันใดอยู่ มันสำคัญโดยเฉพาะสำหรับสตริง Unicode (หรือไม่)   -  person David Heffernan    schedule 23.12.2011
comment
สิ่งนี้ไม่สมเหตุสมผลนัก บางทีคุณควรระบุสิ่งที่คุณตั้งใจจะทำกับไฟล์ไบนารี่เหล่านี้ให้มากขึ้น โดยส่วนตัวแล้วฉันไม่คิดว่าคุณควรใช้ TStringList เพื่อเก็บข้อมูลไบนารี คุณตั้งใจที่จะตัดสินใจว่าขึ้นบรรทัดใหม่ (การขึ้นบรรทัดใหม่ + การป้อนบรรทัดที่เทียบเท่าคืออะไร หรือคุณจะโหลดข้อมูลหนึ่งบล็อก (เช่น 1,024 ไบต์) ต่อรายการใน BinaryStringList   -  person Warren P    schedule 23.12.2011
comment
ฉันใช้ Delphi XE ไฟล์จะถูกบันทึกเป็นสตรีมและไม่สามารถอ่านได้ซึ่งเป็นเรื่องปกติ ฉันไม่จำเป็นต้องแก้ไขไฟล์นอกโปรแกรมของฉัน ค่อนข้างตรงกันข้าม ฉันแค่ต้องเพิ่มบรรทัดที่มี CRC ของไฟล์เพื่อสิ้นสุด แต่มันใช้งานไม่ได้เพราะฉันไม่สามารถอ่านไฟล์ได้ ฉันคิดว่าฉันกำลังสับสนอีกครั้งและสร้างสถานการณ์ที่ยากลำบากจากสิ่งที่เรียบง่าย   -  person    schedule 23.12.2011
comment
ไฟล์ไบนารี่ไม่มี บรรทัด ดังนั้นคุณจึงไม่สามารถเพิ่มได้   -  person Uwe Raabe    schedule 23.12.2011
comment
ฉันคิดว่าคุณกำลังทำให้ชีวิตตัวเองลำบากโดยไม่จำเป็น ฉันไม่สามารถจินตนาการได้ว่าทำไมคุณถึงต้องการทำให้มันยากสำหรับคุณในการอ่านและเขียนไฟล์ ยากสำหรับผู้ใช้ในการทำให้แอปเป็นแบบอัตโนมัติ และมีความเสี่ยงในกรณีที่คุณต้องเปลี่ยนรูปแบบไฟล์ หากคุณต้องทำสิ่งนี้ ให้วางไว้ในไฟล์อื่นที่มีชื่อฐานที่ตรงกับไฟล์ข้อความ คุณมีความซับซ้อนมากเกินไปและพยายามแก้ไขปัญหาที่ไม่มีอยู่จริง   -  person David Heffernan    schedule 23.12.2011
comment
หากฉันพยายามเปิดไฟล์ที่บันทึกจากโปรแกรมของฉันและไฟล์เสียหาย (เช่น สตรีมในไฟล์เสียหาย) โปรแกรมยังคงพยายามเปิดไฟล์สตรีมแม้ว่าจะไม่สามารถทำได้ ก็อาจแจ้งว่า Out of Memory เช่นข้อผิดพลาด หรือบางครั้งก็ไม่มีอะไรเลย Try..Except block ดูเหมือนจะไม่รู้ว่าไฟล์ไม่สามารถเปิดได้และไม่ก่อให้เกิดข้อยกเว้น ดังนั้นฉันคิดว่าฉันสามารถรวมค่าตรวจสอบ CRC ของไฟล์ไว้ภายในและตรวจสอบความสมบูรณ์ของไฟล์ในลักษณะนั้น แต่ใช่ ดูเหมือนว่าฉันกำลังทำให้มันยากสำหรับตัวเอง   -  person    schedule 23.12.2011
comment
จะดีกว่ามากหากตรวจพบไฟล์ที่เสียหายและรายงานข้อผิดพลาด หากผู้ใช้ทำให้ไฟล์เสียหาย นั่นก็คือปัญหาของพวกเขา เก็บไว้ใต้โปรไฟล์ผู้ใช้ของพวกเขาและไม่มีเหตุผลใดที่พวกเขาจะยุ่งกับมัน   -  person David Heffernan    schedule 23.12.2011
comment
นี่ไม่ใช่ไฟล์ที่ฉันสามารถซ่อนไว้ในไดเรกทอรีชั่วคราวได้ เนื่องจากสามารถโหลดได้จาก TOpenDialog ฉันยอมรับว่าใครก็ตามตัดสินใจที่จะทำให้ไฟล์เสียหายซึ่งเป็นความผิดของตนเอง แต่ด้วยเหตุผลบางอย่าง ฉันไม่สามารถดักจับข้อผิดพลาดได้หากไฟล์ (ข้อมูลสตรีม) เสียหาย - แทบจะไม่แสดงข้อผิดพลาดใด ๆ เลย แต่จะพยายามประมวลผลต่อไป   -  person    schedule 23.12.2011
comment
ฉันคิดว่าฉันแก้ไขมันได้แล้ว และครั้งหนึ่งโดยไม่สร้างปัญหาเกินความจำเป็น! ฉันพยายามจับข้อยกเว้นในขั้นตอนของฉันที่เปิดไฟล์ แต่ฉันสร้างบล็อก try..Exception ในคลาสพื้นฐานจริงที่ฉันโหลดสตรีมแทน หากมีข้อยกเว้น ฉันจะตั้งค่าสถานะบูลีนเพื่อแจ้งให้ฉันทราบว่า โหลดไฟล์แล้วโอเค ฉันตรวจสอบแฟล็กนี้ในขั้นตอน Open และสามารถระบุได้ว่าไฟล์นั้นใช้งานได้หรือไม่ ดูเหมือนว่าจะทำงานในลักษณะนี้ ฉันไม่เข้าใจว่าทำไมฉันถึงสับสนและทำให้ปัญหายากขึ้น :(   -  person    schedule 23.12.2011
comment
เครก; ทิปฟรี; ไฟล์ไบนารี่มีปัญหา หากคุณไม่จำเป็นต้องสร้างรูปแบบไฟล์ไบนารี่แบบกำหนดเอง ก็ไม่ต้องทำ ไฟล์ข้อความมีความทนทานและเชื่อถือได้มากกว่ามาก และง่ายต่อการแก้ไขปัญหาเมื่อมีรูปแบบไม่ถูกต้อง   -  person Warren P    schedule 23.12.2011
comment
วอร์เรน ขอบคุณสำหรับคำแนะนำ ฉันอาจพิจารณาเปลี่ยนวิธีการบันทึกอ็อบเจ็กต์ลงในไฟล์ บางที XML อาจเป็นตัวเลือกที่ดีกว่า   -  person    schedule 24.12.2011
comment
@Craig XML จะเป็นทางเลือกหนึ่งที่ดี JSON สามารถอ่านได้ง่ายกว่ามากหากคุณต้องการให้มนุษย์แยกวิเคราะห์ โดยส่วนตัวแล้วสิ่งที่ฉันชอบคือ YAML แต่ต้องใช้ความพยายามอย่างมากในการเข้าสู่ Delphi JSON หรือ XML เป็นตัวเลือกที่ดี   -  person David Heffernan    schedule 24.12.2011
comment
@David ฉันไม่รู้อะไรเกี่ยวกับรูปแบบไฟล์ JSON หรือ YAML โดยไม่ต้องอ่านมัน ฉันได้อ่าน XML เพิ่มเติมแล้ว และเห็นว่าไม่น่าจะมีปัญหาในการใช้งานมากนัก ฉันไม่ชอบวิธีที่ Streams บันทึกเป็นไฟล์และอย่างที่คุณพูดอย่างน้อยบางอย่างเช่น XML จะช่วยให้สามารถอ่านได้หากต้องการ   -  person    schedule 25.12.2011
comment
คุณวางแผนที่จะใช้ตัวแยกวิเคราะห์ XML ใด   -  person David Heffernan    schedule 25.12.2011
comment
ฉันกำลังคิดที่จะใช้ TXMLDocument และบันทึกอ็อบเจ็กต์ของฉันเป็นโหนด XML   -  person    schedule 26.12.2011


คำตอบ (1)


คลาสนี้สืบทอดมาจาก TStringList และเพิ่มค่าตรวจสอบที่ส่วนท้ายเมื่อเขียนลงไฟล์ ค่านี้จะถูกตรวจสอบทุกครั้งที่อ่านไฟล์

type
  TCRCStringList = class(TStringList)
  type
    TCRC = LongWord;
  private
    function CalcCRC(Stream: TStream): TCRC;
  public
    procedure LoadFromStream(Stream: TStream; Encoding: TEncoding); override;
    procedure SaveToStream(Stream: TStream; Encoding: TEncoding); override;
  end;

function TCRCStringList.CalcCRC(Stream: TStream): TCRC;
begin
  Result := 42;  // place CRC calculation here
end;

procedure TCRCStringList.LoadFromStream(Stream: TStream; Encoding: TEncoding);
var
  crc: TCRC;
  temp: TMemoryStream;
begin
  temp := TMemoryStream.Create;
  try
    temp.CopyFrom(Stream, Stream.Size - Sizeof(crc));
    Stream.Read(crc, Sizeof(crc));
    if crc <> CalcCRC(temp) then
      raise Exception.Create('CRC error');
    temp.Position := 0;
    inherited LoadFromStream(temp, Encoding);
  finally
    temp.Free;
  end;
end;

procedure TCRCStringList.SaveToStream(Stream: TStream; Encoding: TEncoding);
var
  crc: TCRC;
  temp: TMemoryStream;
begin
  temp := TMemoryStream.Create;
  try
    inherited SaveToStream(temp, Encoding);
    temp.Position := 0;
    crc := CalcCRC(temp);
    temp.Position := temp.Size;
    temp.Write(crc, Sizeof(crc));
    Stream.CopyFrom(temp, 0); // count = 0 copies the whole stream from the beginning
  finally
    temp.Free;
  end;
end;
person Uwe Raabe    schedule 23.12.2011
comment
ขอบคุณสำหรับการโพสต์ รหัสของคุณดูน่าสนใจ ตั้งแต่นั้นเป็นต้นมา ฉันได้แก้ไขปัญหาเดิมของฉันโดยไม่ต้องใช้การตรวจสอบ CRC ใดๆ ฉันจะยอมรับคำตอบของคุณ แต่เนื่องจากดูเหมือนว่ามีประโยชน์มาก ฉันจะต้องดูให้ละเอียดยิ่งขึ้นเพื่อค้นคว้า ขอบคุณ - person ; 23.12.2011