Skip to content

Record delete fix #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
68 changes: 66 additions & 2 deletions dBASE.NET.Tests/Writing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
using System;
using System.IO;

using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
Expand Down Expand Up @@ -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()
{
Expand All @@ -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");

Expand Down Expand Up @@ -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}";
}
}
}
}
}
48 changes: 37 additions & 11 deletions dBASE.NET/Dbf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

/// <summary>
Expand All @@ -11,8 +12,7 @@
/// </summary>
public class Dbf
{
private DbfHeader header;

private DbfHeader header;
/// <summary>
/// Initializes a new instance of the <see cref="Dbf" />.
/// </summary>
Expand Down Expand Up @@ -64,21 +64,21 @@ public DbfRecord CreateRecord()
/// Opens a DBF file, reads the contents that initialize the current instance, and then closes the file.
/// </summary>
/// <param name="path">The file to read.</param>
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);
}
}
}
Expand All @@ -89,7 +89,7 @@ public void Read(string path)
/// </summary>
/// <param name="baseStream">Stream with a database.</param>
/// <param name="memoStream">Stream with a memo.</param>
public void Read(Stream baseStream, Stream memoStream = null)
public void Read(Stream baseStream, Stream memoStream = null,bool loadDeleted = true)
{
if (baseStream == null)
{
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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) { }
Expand Down Expand Up @@ -233,7 +241,25 @@ private void WriteRecords(BinaryWriter writer)
// Write EOF character.
writer.Write((byte)0x1a);
}

/// <summary>
/// Write the delete Mark as deleted
/// </summary>
/// /// <param name="index">the index of the file to be deleted</param>
public void DeleteRecord(int index)
{
this.Records[index].DeleteRecord();
}
/// <summary>
/// Delete multiple records from theirs indexes on record list
/// </summary>
/// <param name="indexes"></param>
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");
Expand Down
13 changes: 8 additions & 5 deletions dBASE.NET/DbfRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal DbfRecord(BinaryReader reader, DbfHeader header, List<DbfField> 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);
Expand Down Expand Up @@ -55,7 +55,7 @@ internal DbfRecord(List<DbfField> fields)
Data = new List<object>();
foreach (DbfField field in fields) Data.Add(null);
}

public bool Deleted { get; private set; }
public List<object> Data { get; }

public object this[int index] => Data[index];
Expand All @@ -79,7 +79,7 @@ public object this[DbfField field]
return Data[index];
}
}

/// <summary>
/// Returns a string that represents the current object.
/// </summary>
Expand Down Expand Up @@ -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)
Expand Down