diff --git a/dBASE.NET.Tests/Writing.cs b/dBASE.NET.Tests/Writing.cs index 0733ca7..f54156b 100644 --- a/dBASE.NET.Tests/Writing.cs +++ b/dBASE.NET.Tests/Writing.cs @@ -2,7 +2,7 @@ { using System; using System.IO; - + using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -86,7 +86,61 @@ record = dbf.CreateRecord(); Assert.AreEqual("OUT THERE", dbf.Records[2][0], "Record content should be OUT THERE."); } + [TestMethod] + public void WriteRecordAndDeleteIt() + { + dbf = new Dbf(); + var field = new DbfField("TEST", DbfFieldType.Character, 12); + dbf.Fields.Add(field); + var record = dbf.CreateRecord(); + record.Data[0] = "HELLO WORLD ! DELETE ME!"; + dbf.DeleteRecord(0); + dbf.Write("test.dbf", DbfVersion.VisualFoxPro); + dbf = new Dbf(); + dbf.Read("test.dbf",false); + Assert.AreEqual(0, dbf.Records.Count,"dbf should have no records"); + } + [TestMethod] + public void WriteMultipleRecordsAndDeleteOne() + { + var dbf = new Dbf(); + var field = new DbfField("TEST", DbfFieldType.Character, 12); + dbf.Fields.Add(field); + AddMultipleRecords(dbf,12); + dbf.DeleteRecord(0); + dbf.Write("test.dbf", DbfVersion.VisualFoxPro); + dbf = new Dbf(); + dbf.Read("test.dbf", false); + Assert.AreEqual(11, dbf.Records.Count,"dbf should have 11 records"); + } + [TestMethod] + public void WriteMultipleRecordsAndDeleteAll() + { + var dbf = new Dbf(); + var field = new DbfField("TEST", DbfFieldType.Character, 12); + dbf.Fields.Add(field); + AddMultipleRecords(dbf, 12); + dbf.DeleteRecords(Enumerable.Range(0,12).ToArray()); + dbf.Write("test.dbf", DbfVersion.VisualFoxPro); + dbf = new Dbf(); + dbf.Read("test.dbf", false); + Assert.AreEqual(0, dbf.Records.Count, "dbf should have no record"); + } + [TestMethod] + public void WriteRecordAndDeleteButLoadDeletedRecords() + { + dbf = new Dbf(); + var field = new DbfField("TEST", DbfFieldType.Character, 12); + dbf.Fields.Add(field); + var record = dbf.CreateRecord(); + record.Data[0] = "HELLO WORLD ! DELETE ME!"; + dbf.DeleteRecord(0); + dbf.Write("test.dbf", DbfVersion.VisualFoxPro); + dbf = new Dbf(); + dbf.Read("test.dbf"); + Assert.AreEqual(0, dbf.Records.Count, "dbf should have no records"); + } [TestMethod] public void NumericField() { @@ -96,7 +150,6 @@ public void NumericField() DbfRecord record = dbf.CreateRecord(); record.Data[0] = 3.14; dbf.Write("test.dbf", DbfVersion.VisualFoxPro); - dbf = new Dbf(); dbf.Read("test.dbf"); @@ -198,5 +251,16 @@ public void CurrencyField() Assert.AreEqual((float) 4.34, dbf.Records[0][0], "Record content should be 4.34."); } + private void AddMultipleRecords(Dbf dbf,int recordsLength) + { + for (int j = 0; j < recordsLength; ++j) + { + var record = dbf.CreateRecord(); + for (int i = 0; i < dbf.Fields.Count; ++i) + { + record.Data[i] = $"test field {i}"; + } + } + } } } diff --git a/dBASE.NET/Dbf.cs b/dBASE.NET/Dbf.cs index fb66fa7..ff610be 100644 --- a/dBASE.NET/Dbf.cs +++ b/dBASE.NET/Dbf.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; + using System.Linq; using System.Text; /// @@ -11,8 +12,7 @@ /// public class Dbf { - private DbfHeader header; - + private DbfHeader header; /// /// Initializes a new instance of the . /// @@ -64,21 +64,21 @@ public DbfRecord CreateRecord() /// Opens a DBF file, reads the contents that initialize the current instance, and then closes the file. /// /// The file to read. - public void Read(string path) - { - // Open stream for reading. + public void Read(string path,bool loadDeleted = true) + { + // Open stream for reading. using (FileStream baseStream = File.Open(path, FileMode.Open, FileAccess.Read)) { string memoPath = GetMemoPath(path); if (memoPath == null) { - Read(baseStream); + Read(baseStream,loadDeleted:loadDeleted); } else { using (FileStream memoStream = File.Open(memoPath, FileMode.Open, FileAccess.Read)) { - Read(baseStream, memoStream); + Read(baseStream, memoStream,loadDeleted); } } } @@ -89,7 +89,7 @@ public void Read(string path) /// /// Stream with a database. /// Stream with a memo. - public void Read(Stream baseStream, Stream memoStream = null) + public void Read(Stream baseStream, Stream memoStream = null,bool loadDeleted = true) { if (baseStream == null) { @@ -110,7 +110,7 @@ public void Read(Stream baseStream, Stream memoStream = null) // After reading the fields, we move the read pointer to the beginning // of the records, as indicated by the "HeaderLength" value in the header. baseStream.Seek(header.HeaderLength, SeekOrigin.Begin); - ReadRecords(reader, memoData); + ReadRecords(reader, memoData,loadDeleted); } } @@ -197,15 +197,23 @@ private void ReadFields(BinaryReader reader) reader.ReadByte(); } - private void ReadRecords(BinaryReader reader, byte[] memoData) + private void ReadRecords(BinaryReader reader, byte[] memoData,bool loadDeleted) { Records.Clear(); // Records are terminated by 0x1a char (officially), or EOF (also seen). while (reader.PeekChar() != 0x1a && reader.PeekChar() != -1) { + var isDeletedMarker = (byte)reader.PeekChar() == (byte)0x2A; + if (isDeletedMarker) + { + reader.ReadBytes(header.RecordLength); + continue; + } try { + + Records.Add(new DbfRecord(reader, header, Fields, memoData, Encoding)); } catch (EndOfStreamException) { } @@ -233,7 +241,25 @@ private void WriteRecords(BinaryWriter writer) // Write EOF character. writer.Write((byte)0x1a); } - + /// + /// Write the delete Mark as deleted + /// + /// /// the index of the file to be deleted + public void DeleteRecord(int index) + { + this.Records[index].DeleteRecord(); + } + /// + /// Delete multiple records from theirs indexes on record list + /// + /// + public void DeleteRecords(params int[] indexes) + { + for (int i = 0; i < indexes.Length; i++) + { + DeleteRecord(indexes[i]); + } + } private static string GetMemoPath(string basePath) { string memoPath = Path.ChangeExtension(basePath, "fpt"); diff --git a/dBASE.NET/DbfRecord.cs b/dBASE.NET/DbfRecord.cs index 551a0ab..a433a53 100644 --- a/dBASE.NET/DbfRecord.cs +++ b/dBASE.NET/DbfRecord.cs @@ -25,7 +25,7 @@ internal DbfRecord(BinaryReader reader, DbfHeader header, List fields, // Read record marker. byte marker = reader.ReadByte(); - + Deleted = marker == 0x2A; // Read entire record as sequence of bytes. // Note that record length includes marker. byte[] row = reader.ReadBytes(header.RecordLength - 1); @@ -55,7 +55,7 @@ internal DbfRecord(List fields) Data = new List(); foreach (DbfField field in fields) Data.Add(null); } - + public bool Deleted { get; private set; } public List Data { get; } public object this[int index] => Data[index]; @@ -79,7 +79,7 @@ public object this[DbfField field] return Data[index]; } } - + /// /// Returns a string that represents the current object. /// @@ -115,11 +115,14 @@ public string ToString(string separator, string mask) return string.Join(separator, fields.Select(z => string.Format(mask, z.Name, this[z]))); } - + internal void DeleteRecord() + { + Deleted = true; + } internal void Write(BinaryWriter writer, Encoding encoding) { // Write marker (always "not deleted") - writer.Write((byte)0x20); + writer.Write(Deleted ? (byte)0x2A : (byte)0x20); int index = 0; foreach (DbfField field in fields)