You've already forked OneSTools.FileDatabase
mirror of
https://github.com/akpaevj/OneSTools.FileDatabase.git
synced 2026-04-24 19:13:53 +02:00
Добавьте файлы проекта.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30804.86
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneSTools.FileDatabase", "OneSTools.FileDatabase\OneSTools.FileDatabase.csproj", "{EDF41082-9B3F-4AA2-9E60-C18901AFC25A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneSTools.FileDatabaseTestApp", "OneSTools.FileDatabaseTestApp\OneSTools.FileDatabaseTestApp.csproj", "{40FCEDB2-4C4B-4419-90BB-1BD80840AC05}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{EDF41082-9B3F-4AA2-9E60-C18901AFC25A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EDF41082-9B3F-4AA2-9E60-C18901AFC25A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EDF41082-9B3F-4AA2-9E60-C18901AFC25A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EDF41082-9B3F-4AA2-9E60-C18901AFC25A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{40FCEDB2-4C4B-4419-90BB-1BD80840AC05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{40FCEDB2-4C4B-4419-90BB-1BD80840AC05}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{40FCEDB2-4C4B-4419-90BB-1BD80840AC05}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{40FCEDB2-4C4B-4419-90BB-1BD80840AC05}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {37516B47-10E9-4C36-B852-487DB3568E24}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,104 @@
|
||||
using OneSTools.FileDatabase.HighLevel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using OneSTools.FileDatabase.LowLevel;
|
||||
using System.Linq;
|
||||
using OneSTools.FileDatabase.LowLevel.Pages;
|
||||
using OneSTools.FileDatabase.LowLevel.Files;
|
||||
using OneSTools.BracketsFile;
|
||||
using System.Text;
|
||||
using System.Data.Common;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data;
|
||||
|
||||
namespace OneSTools.FileDatabase
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties and methods for reading 1C file database data
|
||||
/// </summary>
|
||||
public class FileDatabaseConnection : IDisposable
|
||||
{
|
||||
private readonly string _path;
|
||||
private FileDatabaseStream _stream;
|
||||
private HeaderPage _headerPage;
|
||||
private FreePagesPage _freePagesPage;
|
||||
private DatabaseDescriptionFile _databaseDescriptionFile;
|
||||
|
||||
/// <summary>
|
||||
/// The flag of database opening
|
||||
/// </summary>
|
||||
public bool Opened { get; private set; } = false;
|
||||
/// <summary>
|
||||
/// Version of the database file
|
||||
/// </summary>
|
||||
public string Version => _headerPage?.Version;
|
||||
/// <summary>
|
||||
/// Locale of the database
|
||||
/// </summary>
|
||||
public string Language => _databaseDescriptionFile?.Language;
|
||||
/// <summary>
|
||||
/// A collection of database tables
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<Table> Tables { get; private set; } = null;
|
||||
|
||||
public FileDatabaseConnection(string path)
|
||||
=> _path = path;
|
||||
|
||||
/// <summary>
|
||||
/// Open database file
|
||||
/// </summary>
|
||||
public void Open()
|
||||
{
|
||||
if (!File.Exists(_path))
|
||||
throw new FileNotFoundException("Cannot find a database file", _path);
|
||||
|
||||
var stream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
_stream = new FileDatabaseStream(stream);
|
||||
|
||||
ReadStructure();
|
||||
|
||||
Opened = true;
|
||||
}
|
||||
|
||||
private void ReadStructure()
|
||||
{
|
||||
_headerPage = new HeaderPage();
|
||||
_headerPage.Read(_stream);
|
||||
|
||||
_stream.PageSize = _headerPage.PageSize;
|
||||
|
||||
_stream.CurrentPage = 1;
|
||||
_freePagesPage = new FreePagesPage();
|
||||
_freePagesPage.Read(_stream);
|
||||
|
||||
_databaseDescriptionFile = new DatabaseDescriptionFile(_stream);
|
||||
|
||||
var tables = new List<Table>();
|
||||
|
||||
for (int i = 0; i < _databaseDescriptionFile.TablesCount; i++)
|
||||
{
|
||||
var tableDefinitionData = _databaseDescriptionFile.ReadTableDefinitionData();
|
||||
var tableDefinitionStr = Encoding.UTF8.GetString(tableDefinitionData);
|
||||
var tableDefinitionNode = BracketsParser.ParseBlock(tableDefinitionStr);
|
||||
|
||||
var table = new Table(_stream, tableDefinitionNode);
|
||||
tables.Add(table);
|
||||
}
|
||||
|
||||
Tables = tables.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close database file
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
Opened = false;
|
||||
_stream?.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using OneSTools.BracketsFile;
|
||||
|
||||
namespace OneSTools.FileDatabase.HighLevel
|
||||
{
|
||||
public class Field
|
||||
{
|
||||
/// <summary>
|
||||
/// Real size of the value (bytes)
|
||||
/// </summary>
|
||||
internal int MaxSize { get; private set; }
|
||||
/// <summary>
|
||||
/// Internal name of the field
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary>
|
||||
/// Type of the field's value
|
||||
/// </summary>
|
||||
public FieldType Type { get; private set; }
|
||||
/// <summary>
|
||||
/// If the flag is True a value can be null
|
||||
/// </summary>
|
||||
public bool Nullable { get; private set; }
|
||||
/// <summary>
|
||||
/// Length of the value
|
||||
/// </summary>
|
||||
public int Length { get; private set; }
|
||||
/// <summary>
|
||||
/// "Numeric" value only - position of a point in a value
|
||||
/// </summary>
|
||||
public int Precision { get; private set; }
|
||||
public string CaseSensitive { get; private set; }
|
||||
|
||||
internal void Read(BracketsNode node)
|
||||
{
|
||||
Name = node[0];
|
||||
|
||||
var type = (string)node[1];
|
||||
|
||||
Type = type switch
|
||||
{
|
||||
"B" => FieldType.Binary,
|
||||
"L" => FieldType.Logical,
|
||||
"N" => FieldType.Numeric,
|
||||
"NC" => FieldType.NChar,
|
||||
"NVC" => FieldType.NVarChar,
|
||||
"RV" => FieldType.RowVersion,
|
||||
"NT" => FieldType.NText,
|
||||
"I" => FieldType.Image,
|
||||
"DT" => FieldType.DateTime,
|
||||
_ => throw new Exception($"{type} is unknown field type"),
|
||||
};
|
||||
|
||||
Nullable = node[2];
|
||||
Length = node[3];
|
||||
Precision = node[4];
|
||||
CaseSensitive = node[5];
|
||||
|
||||
CalculateRealSize();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> $"{Name} ({Type})";
|
||||
|
||||
private void CalculateRealSize()
|
||||
{
|
||||
MaxSize = Type switch
|
||||
{
|
||||
FieldType.Binary => Length,
|
||||
FieldType.Logical => 1,
|
||||
FieldType.Numeric => (Length + 1) / 2 + (Length + 1) % 2,
|
||||
FieldType.NChar => Length * 2,
|
||||
FieldType.NVarChar => Length * 2 + 2,
|
||||
FieldType.RowVersion => 16,
|
||||
FieldType.NText => 8,
|
||||
FieldType.Image => 8,
|
||||
FieldType.DateTime => 7,
|
||||
_ => throw new NotImplementedException($"There is no alghorithm to calculate size of \"{Type}\" type"),
|
||||
};
|
||||
|
||||
if (Nullable)
|
||||
MaxSize++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace OneSTools.FileDatabase.HighLevel
|
||||
{
|
||||
public enum FieldType
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixed-length binary data
|
||||
/// </summary>
|
||||
Binary,
|
||||
/// <summary>
|
||||
/// Boolean value
|
||||
/// </summary>
|
||||
Logical,
|
||||
/// <summary>
|
||||
/// Fixed point decimal
|
||||
/// </summary>
|
||||
Numeric,
|
||||
/// <summary>
|
||||
/// Fixed-length Unicode string
|
||||
/// </summary>
|
||||
NChar, // Unicode string
|
||||
/// <summary>
|
||||
/// Variable-length Unicode string
|
||||
/// </summary>
|
||||
NVarChar,
|
||||
/// <summary>
|
||||
/// Version of the row
|
||||
/// </summary>
|
||||
RowVersion,
|
||||
/// <summary>
|
||||
/// Unlimited-length Unicode string (UTF-16)
|
||||
/// </summary>
|
||||
NText,
|
||||
/// <summary>
|
||||
/// Unlimited-length binary data
|
||||
/// </summary>
|
||||
Image,
|
||||
/// <summary>
|
||||
/// Date and time
|
||||
/// </summary>
|
||||
DateTime
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using OneSTools.BracketsFile;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace OneSTools.FileDatabase.HighLevel
|
||||
{
|
||||
public class Index
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal name of the index
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary>
|
||||
/// Collection of the index fields
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<IndexField> Fields { get; private set; } = null;
|
||||
|
||||
internal void Read(BracketsNode node)
|
||||
{
|
||||
var fields = new List<IndexField>();
|
||||
|
||||
Name = (string)node[0];
|
||||
|
||||
for (int i = 2; i < node.Count; i++)
|
||||
{
|
||||
var indexField = new IndexField();
|
||||
indexField.Read(node[i]);
|
||||
|
||||
fields.Add(indexField);
|
||||
}
|
||||
|
||||
Fields = fields.AsReadOnly();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using OneSTools.BracketsFile;
|
||||
|
||||
namespace OneSTools.FileDatabase.HighLevel
|
||||
{
|
||||
public class IndexField
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal name of the index field
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary>
|
||||
/// The length of the value
|
||||
/// </summary>
|
||||
public int Length { get; private set; }
|
||||
|
||||
internal void Read(BracketsNode node)
|
||||
{
|
||||
Name = node[0];
|
||||
Length = node[1];
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using OneSTools.BracketsFile;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System;
|
||||
using OneSTools.FileDatabase.LowLevel;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace OneSTools.FileDatabase.HighLevel
|
||||
{
|
||||
public class Table
|
||||
{
|
||||
internal int MaxRowSize { get; private set; }
|
||||
internal uint DataFilePage { get; private set; }
|
||||
internal uint UnlimitedLengthDataFilePage { get; private set; }
|
||||
internal uint IndexFilePage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal name of the table
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary>
|
||||
/// Collection of table fields
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<Field> Fields { get; private set; } = null;
|
||||
/// <summary>
|
||||
/// Collection of table indexes
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<Index> Indexes { get; private set; } = null;
|
||||
public bool RecordLock { get; private set; }
|
||||
/// <summary>
|
||||
/// Collection of table rows
|
||||
/// </summary>
|
||||
public IReadOnlyList<object[]> Rows { get; private set; } = null;
|
||||
|
||||
internal Table(FileDatabaseStream stream, BracketsNode node)
|
||||
{
|
||||
Name = node[0];
|
||||
|
||||
for (int i = 2; i < node.Count; i++)
|
||||
{
|
||||
var currentNode = node[i];
|
||||
string nodeName = currentNode[0];
|
||||
|
||||
switch (nodeName)
|
||||
{
|
||||
case "Fields":
|
||||
ReadFields(currentNode);
|
||||
break;
|
||||
case "Indexes":
|
||||
ReadIndexes(currentNode);
|
||||
break;
|
||||
case "Recordlock":
|
||||
RecordLock = currentNode[1];
|
||||
break;
|
||||
case "Files":
|
||||
DataFilePage = currentNode[1];
|
||||
UnlimitedLengthDataFilePage = currentNode[2];
|
||||
IndexFilePage = currentNode[3];
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"{nodeName} is unknown table description node");
|
||||
}
|
||||
}
|
||||
|
||||
Rows = new TableRows(stream, this);
|
||||
}
|
||||
|
||||
private void ReadFields(BracketsNode node)
|
||||
{
|
||||
var fields = new List<Field>();
|
||||
|
||||
for (int i = 1; i < node.Count; i++)
|
||||
{
|
||||
var field = new Field();
|
||||
field.Read(node[i]);
|
||||
|
||||
if (field.Type == FieldType.RowVersion)
|
||||
{
|
||||
fields.Insert(0, field);
|
||||
}
|
||||
else
|
||||
fields.Add(field);
|
||||
|
||||
MaxRowSize += field.MaxSize;
|
||||
}
|
||||
|
||||
// add "free row" mark length
|
||||
MaxRowSize++;
|
||||
|
||||
// add "short version" data length
|
||||
if (fields.Count > 0 && fields[0].Type == FieldType.RowVersion && RecordLock)
|
||||
MaxRowSize += 8;
|
||||
|
||||
Fields = fields.AsReadOnly();
|
||||
}
|
||||
|
||||
private void ReadIndexes(BracketsNode node)
|
||||
{
|
||||
var indexes = new List<Index>();
|
||||
|
||||
if (node.Count > 1)
|
||||
{
|
||||
for (int i = 1; i < node.Count; i++)
|
||||
{
|
||||
var index = new Index();
|
||||
index.Read(node[i]);
|
||||
|
||||
indexes.Add(index);
|
||||
}
|
||||
}
|
||||
|
||||
Indexes = indexes.AsReadOnly();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
using OneSTools.FileDatabase.LowLevel;
|
||||
using OneSTools.FileDatabase.LowLevel.Files;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OneSTools.FileDatabase.HighLevel
|
||||
{
|
||||
internal class TableRows : IReadOnlyList<object[]>
|
||||
{
|
||||
private readonly FileDatabaseStream _stream;
|
||||
private readonly Table _table;
|
||||
private DataFile _dataFile;
|
||||
private UnlimitedLengthDataFile _unlimitedLengthDataFile;
|
||||
|
||||
internal TableRows(FileDatabaseStream stream, Table table)
|
||||
{
|
||||
_stream = stream;
|
||||
_table = table;
|
||||
}
|
||||
|
||||
public object[] this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index >= Count)
|
||||
throw new IndexOutOfRangeException();
|
||||
else
|
||||
{
|
||||
return Get(index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
InitializeFiles();
|
||||
|
||||
return (int)(_dataFile?.RootPage.DataLength / Convert.ToUInt64(_table.MaxRowSize));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<object[]> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
yield return Get(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
private void InitializeFiles()
|
||||
{
|
||||
if (_dataFile == null)
|
||||
{
|
||||
if (_table.DataFilePage != 0)
|
||||
{
|
||||
_dataFile = new DataFile(_stream, _table.DataFilePage, _table.MaxRowSize, _table.Fields.Count > 0 && _table.Fields[0].Type != FieldType.RowVersion && _table.RecordLock);
|
||||
|
||||
if (_dataFile.HasData())
|
||||
{
|
||||
_dataFile.GoToDataStartingPosition();
|
||||
_dataFile.GoToRow(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (_table.UnlimitedLengthDataFilePage != 0)
|
||||
{
|
||||
_unlimitedLengthDataFile = new UnlimitedLengthDataFile(_stream, _table.UnlimitedLengthDataFilePage);
|
||||
|
||||
if (_unlimitedLengthDataFile.HasData())
|
||||
_dataFile.GoToDataStartingPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object[] Get(int rowNumber)
|
||||
{
|
||||
InitializeFiles();
|
||||
|
||||
_dataFile.GoToRow(rowNumber);
|
||||
|
||||
var values = new object[_table.Fields.Count];
|
||||
|
||||
var rawData = _dataFile.ReadRow();
|
||||
|
||||
if (rawData == null)
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var currentOffset = 0;
|
||||
|
||||
for (int i = 0; i < _table.Fields.Count; i++)
|
||||
{
|
||||
var field = _table.Fields[i];
|
||||
|
||||
var fieldData = rawData[currentOffset..(currentOffset + field.MaxSize)];
|
||||
|
||||
currentOffset += field.MaxSize;
|
||||
|
||||
values[i] = field.Type switch
|
||||
{
|
||||
FieldType.Binary => ReadBinary(fieldData, field.Nullable),
|
||||
FieldType.Logical => ReadLogical(fieldData, field.Nullable),
|
||||
FieldType.Numeric => ReadNumericValue(fieldData, field.Precision, field.Nullable),
|
||||
FieldType.NChar => ReadNChar(fieldData, field.Nullable),
|
||||
FieldType.NVarChar => ReadNVarChar(fieldData, field.Nullable),
|
||||
FieldType.RowVersion => ReadRowVersion(fieldData, field.Nullable),
|
||||
FieldType.NText => ReadNText(fieldData, field.Nullable),
|
||||
FieldType.Image => ReadImage(fieldData, field.Nullable),
|
||||
FieldType.DateTime => ReadDateTime(fieldData, field.Nullable),
|
||||
_ => throw new Exception($"Reading value for a field with type {field.Type} is not implemented")
|
||||
};
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] ReadBinary(byte[] data, bool nullable)
|
||||
{
|
||||
if (!HasValue(data, nullable))
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var valueData = GetValueData(data, nullable);
|
||||
|
||||
return valueData;
|
||||
}
|
||||
}
|
||||
|
||||
private bool? ReadLogical(byte[] data, bool nullable)
|
||||
{
|
||||
if (!HasValue(data, nullable))
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var valueData = GetValueData(data, nullable);
|
||||
|
||||
return valueData[0] != 0;
|
||||
}
|
||||
}
|
||||
|
||||
private decimal? ReadNumericValue(byte[] data, int precision, bool nullable)
|
||||
{
|
||||
if (!HasValue(data, nullable))
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var valueData = GetValueData(data, nullable);
|
||||
var doubleStr = new StringBuilder();
|
||||
|
||||
var negative = ReadTetrad(valueData[0]) == 0;
|
||||
if (negative)
|
||||
doubleStr.Append('-');
|
||||
|
||||
doubleStr.Append(ReadTetrad(valueData[0], true));
|
||||
|
||||
for (int i = 1; i < valueData.Length; i++)
|
||||
{
|
||||
doubleStr.Append(ReadTetrad(valueData[i]));
|
||||
doubleStr.Append(ReadTetrad(valueData[i], true));
|
||||
}
|
||||
|
||||
if (precision > 0)
|
||||
doubleStr.Insert(doubleStr.Length - 1 - precision, '.');
|
||||
|
||||
// remove last zero, I don't know what is it
|
||||
doubleStr.Remove(doubleStr.Length - 1, 1);
|
||||
|
||||
return decimal.Parse(doubleStr.ToString(), new NumberFormatInfo() { NumberDecimalSeparator = "." });
|
||||
}
|
||||
}
|
||||
|
||||
private void AddNumberToNumeric(StringBuilder doubleStr, int value)
|
||||
{
|
||||
// don't add leading zeros
|
||||
if (value != 0)
|
||||
doubleStr.Append(value);
|
||||
else if (doubleStr.Length > 0)
|
||||
doubleStr.Append(value);
|
||||
}
|
||||
|
||||
private string ReadNChar(byte[] data, bool nullable)
|
||||
{
|
||||
if (!HasValue(data, nullable))
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var valueData = GetValueData(data, nullable);
|
||||
|
||||
return Encoding.Unicode.GetString(valueData);
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadNVarChar(byte[] data, bool nullable)
|
||||
{
|
||||
if (!HasValue(data, nullable))
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var valueData = GetValueData(data, nullable);
|
||||
|
||||
var length = BinaryPrimitives.ReadUInt16LittleEndian(valueData);
|
||||
|
||||
if (length == 0)
|
||||
return "";
|
||||
else
|
||||
return Encoding.Unicode.GetString(valueData[2..(length * 2 + 2)]);
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime? ReadDateTime(byte[] data, bool nullable)
|
||||
{
|
||||
if (!HasValue(data, nullable))
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var valueData = GetValueData(data, nullable);
|
||||
|
||||
var year1 = ReadTetrad(valueData[0]);
|
||||
var year2 = ReadTetrad(valueData[0], true);
|
||||
var year3 = ReadTetrad(valueData[1]);
|
||||
var year4 = ReadTetrad(valueData[1], true);
|
||||
var year = int.Parse($"{year1}{year2}{year3}{year4}");
|
||||
|
||||
var month1 = ReadTetrad(valueData[2]);
|
||||
var month2 = ReadTetrad(valueData[2], true);
|
||||
var month = int.Parse($"{month1}{month2}");
|
||||
|
||||
var day1 = ReadTetrad(valueData[3]);
|
||||
var day2 = ReadTetrad(valueData[3], true);
|
||||
var day = int.Parse($"{day1}{day2}");
|
||||
|
||||
var hour1 = ReadTetrad(valueData[4]);
|
||||
var hour2 = ReadTetrad(valueData[4], true);
|
||||
var hour = int.Parse($"{hour1}{hour2}");
|
||||
|
||||
var minute1 = ReadTetrad(valueData[5]);
|
||||
var minute2 = ReadTetrad(valueData[5], true);
|
||||
var minute = int.Parse($"{minute1}{minute2}");
|
||||
|
||||
var second1 = ReadTetrad(valueData[6]);
|
||||
var second2 = ReadTetrad(valueData[6], true);
|
||||
var second = int.Parse($"{second1}{second2}");
|
||||
|
||||
if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0)
|
||||
return DateTime.MinValue;
|
||||
else
|
||||
return new DateTime(year, month, day, hour, minute, second);
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadRowVersion(byte[] data, bool nullable)
|
||||
{
|
||||
if (!HasValue(data, nullable))
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var valueData = GetValueData(data, nullable);
|
||||
|
||||
var v1 = BinaryPrimitives.ReadUInt32LittleEndian(valueData[..5]);
|
||||
var v2 = BinaryPrimitives.ReadUInt32LittleEndian(valueData[5..9]);
|
||||
var v3 = BinaryPrimitives.ReadUInt32LittleEndian(valueData[9..13]);
|
||||
var v4 = BinaryPrimitives.ReadUInt32LittleEndian(valueData[12..]);
|
||||
|
||||
return $"{v1}.{v2}.{v3}.{v4}";
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadNText(byte[] data, bool nullable)
|
||||
{
|
||||
var valueData = ReadUnlimitedData(data, nullable);
|
||||
|
||||
if (valueData is null)
|
||||
return null;
|
||||
else
|
||||
return Encoding.Unicode.GetString(valueData);
|
||||
}
|
||||
|
||||
private byte[] ReadImage(byte[] data, bool nullable)
|
||||
{
|
||||
var valueData = ReadUnlimitedData(data, nullable);
|
||||
|
||||
if (valueData is null)
|
||||
return null;
|
||||
else
|
||||
return valueData;
|
||||
}
|
||||
|
||||
private byte[] ReadUnlimitedData(byte[] data, bool nullable)
|
||||
{
|
||||
if (!HasValue(data, nullable))
|
||||
return null;
|
||||
|
||||
var valueData = GetValueData(data, nullable);
|
||||
|
||||
var blockNumber = BinaryPrimitives.ReadUInt32LittleEndian(valueData);
|
||||
var dataLength = BinaryPrimitives.ReadUInt32LittleEndian(valueData[4..]);
|
||||
|
||||
_unlimitedLengthDataFile.GoToBlock(blockNumber);
|
||||
|
||||
var valueRawData = _unlimitedLengthDataFile.ReadBlockChain();
|
||||
|
||||
return valueRawData[..(int)dataLength];
|
||||
}
|
||||
|
||||
private bool HasValue(byte[] data, bool nullable)
|
||||
{
|
||||
if (nullable)
|
||||
return (bool)ReadLogical(data, false);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
private byte[] GetValueData(byte[] data, bool nullable)
|
||||
{
|
||||
if (nullable)
|
||||
return data[1..];
|
||||
else
|
||||
return data;
|
||||
}
|
||||
|
||||
private int ReadTetrad(byte b, bool second = false)
|
||||
{
|
||||
if (second)
|
||||
return b & 0b_0000_1111;
|
||||
else
|
||||
return b >> 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel
|
||||
{
|
||||
internal class FileDatabaseStream : IDisposable
|
||||
{
|
||||
private readonly BinaryReader reader;
|
||||
|
||||
public long Position
|
||||
{
|
||||
get => reader.BaseStream.Position;
|
||||
set => reader.BaseStream.Position = value;
|
||||
}
|
||||
public uint PageSize { get; set; }
|
||||
public uint CurrentPage
|
||||
{
|
||||
get => (uint)(Position / PageSize);
|
||||
set => reader.BaseStream.Position = PageSize * value;
|
||||
}
|
||||
public uint PositionOnPage => (uint)(Position - (CurrentPage * PageSize));
|
||||
|
||||
public FileDatabaseStream(Stream stream)
|
||||
{
|
||||
reader = new BinaryReader(stream);
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(int count)
|
||||
=> reader.ReadBytes(count);
|
||||
|
||||
public int ReadBytes(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var forReading = MaxBytesCanTake(count);
|
||||
|
||||
return reader.Read(buffer, offset, (int)forReading);
|
||||
}
|
||||
|
||||
public long SkipBytes(long count)
|
||||
{
|
||||
var forSkip = MaxBytesCanTake(count);
|
||||
|
||||
reader.BaseStream.Position += forSkip;
|
||||
|
||||
return forSkip;
|
||||
}
|
||||
|
||||
private long MaxBytesCanTake(long requestedCount)
|
||||
=> Math.Min(((CurrentPage + 1) * PageSize) - Position, requestedCount);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
reader?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel.Files
|
||||
{
|
||||
internal class DataFile : InnerFile
|
||||
{
|
||||
private readonly int _rowSize;
|
||||
private readonly bool _hasShortVersion;
|
||||
|
||||
public DataFile(FileDatabaseStream stream, uint rootPageNumber, int rowSize, bool hasShortVersion) : base(stream, rootPageNumber)
|
||||
{
|
||||
_rowSize = rowSize;
|
||||
_hasShortVersion = hasShortVersion;
|
||||
}
|
||||
|
||||
public void GoToRow(int number)
|
||||
{
|
||||
GoToDataStartingPosition();
|
||||
|
||||
SkipBytes(_rowSize * number);
|
||||
}
|
||||
|
||||
public byte[] ReadRow()
|
||||
{
|
||||
while (!EndOfDataStream)
|
||||
{
|
||||
var data = ReadBytes(_rowSize);
|
||||
|
||||
// it's not a free row
|
||||
if (data[0] == 0)
|
||||
{
|
||||
_dataStreamPosition += Convert.ToUInt64(_rowSize);
|
||||
|
||||
if (_hasShortVersion)
|
||||
return data[9..];
|
||||
else
|
||||
return data[1..];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel.Files
|
||||
{
|
||||
internal class DatabaseDescriptionFile: UnlimitedLengthDataFile
|
||||
{
|
||||
private int _currentTableStartBlock = -1;
|
||||
|
||||
public string Language { get; private set; }
|
||||
public uint TablesCount { get; private set; }
|
||||
public uint[] TablesStartBlocks { get; private set; }
|
||||
|
||||
public DatabaseDescriptionFile(FileDatabaseStream stream) : base(stream, 2)
|
||||
{
|
||||
GoToDataStartingPosition();
|
||||
|
||||
SkipFirstBlock();
|
||||
|
||||
var ddfHeaderData = ReadBlockChain();
|
||||
|
||||
Language = Encoding.UTF8.GetString(ddfHeaderData[..(Array.IndexOf(ddfHeaderData, (byte)0))]);
|
||||
TablesCount = BinaryPrimitives.ReadUInt32LittleEndian(ddfHeaderData[32..36]);
|
||||
TablesStartBlocks = new uint[TablesCount];
|
||||
|
||||
for (int i = 0; i < TablesCount; i++)
|
||||
{
|
||||
var startIndex = 36 + i * 4;
|
||||
var number = BinaryPrimitives.ReadUInt32LittleEndian(ddfHeaderData[startIndex..(startIndex + 4)]);
|
||||
|
||||
if (number == 0)
|
||||
break;
|
||||
|
||||
TablesStartBlocks[i] = number;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ReadTableDefinitionData()
|
||||
{
|
||||
GoToNextTableStartBlock();
|
||||
|
||||
return ReadBlockChain();
|
||||
}
|
||||
|
||||
private void GoToNextTableStartBlock()
|
||||
{
|
||||
_currentTableStartBlock++;
|
||||
|
||||
if (TablesStartBlocks.Length <= _currentTableStartBlock)
|
||||
throw new Exception("There are no more data pages in the root page list");
|
||||
else
|
||||
GoToBlock(TablesStartBlocks[_currentTableStartBlock]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using OneSTools.FileDatabase.LowLevel.Pages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel.Files
|
||||
{
|
||||
|
||||
internal abstract class InnerFile
|
||||
{
|
||||
protected readonly FileDatabaseStream _stream;
|
||||
private uint _rootPageNumber;
|
||||
private readonly List<uint> _dataPages = new List<uint>();
|
||||
protected int _dataPage = -1;
|
||||
private long _position = 0;
|
||||
protected ulong _dataStreamPosition = 0;
|
||||
|
||||
public RootPage RootPage { get; private set; }
|
||||
public List<IndexPage> IndexPages { get; private set; }
|
||||
public bool EndOfDataStream => _dataStreamPosition >= RootPage.DataLength;
|
||||
|
||||
public InnerFile(FileDatabaseStream stream, uint rootPageNumber)
|
||||
{
|
||||
_stream = stream;
|
||||
_rootPageNumber = rootPageNumber;
|
||||
|
||||
ReadRootPage();
|
||||
|
||||
if (RootPage.Type == InnerFileType.Full)
|
||||
{
|
||||
ReadIndexPages();
|
||||
|
||||
IndexPages.ForEach(c => _dataPages.AddRange(c.PageNumbers));
|
||||
}
|
||||
else
|
||||
_dataPages.AddRange(RootPage.PageNumbers);
|
||||
}
|
||||
|
||||
public bool HasData()
|
||||
=> RootPage.DataLength > 0;
|
||||
|
||||
public void GoToDataStartingPosition()
|
||||
{
|
||||
if (!HasData())
|
||||
throw new Exception("The file doesn't contain data");
|
||||
|
||||
_dataPage = -1;
|
||||
|
||||
GoToNextDataPage();
|
||||
}
|
||||
|
||||
protected byte[] ReadBytes(int count)
|
||||
{
|
||||
_stream.Position = _position;
|
||||
|
||||
if (_dataPage == -1)
|
||||
GoToNextDataPage();
|
||||
|
||||
var buffer = new byte[count];
|
||||
|
||||
// Loop till read requested quantity
|
||||
var read = 0;
|
||||
|
||||
while (read < count)
|
||||
{
|
||||
read += _stream.ReadBytes(buffer, read, count - read);
|
||||
|
||||
if (read < count)
|
||||
GoToNextDataPage();
|
||||
}
|
||||
|
||||
if (PageReadingCompleted())
|
||||
GoToNextDataPage();
|
||||
|
||||
_position = _stream.Position;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
protected void SkipBytes(long count)
|
||||
{
|
||||
_stream.Position = _position;
|
||||
|
||||
if (_dataPage == -1)
|
||||
GoToNextDataPage();
|
||||
|
||||
// Loop till read requested quantity
|
||||
long skipped = 0;
|
||||
|
||||
while (skipped < count)
|
||||
{
|
||||
skipped += _stream.SkipBytes(count - skipped);
|
||||
|
||||
if (skipped < count)
|
||||
GoToNextDataPage();
|
||||
}
|
||||
|
||||
if (PageReadingCompleted())
|
||||
GoToNextDataPage();
|
||||
|
||||
_position = _stream.Position;
|
||||
}
|
||||
|
||||
private void ReadRootPage()
|
||||
{
|
||||
_stream.CurrentPage = _rootPageNumber;
|
||||
|
||||
RootPage = new RootPage();
|
||||
RootPage.Read(_stream);
|
||||
|
||||
_position = _stream.Position;
|
||||
}
|
||||
|
||||
private void ReadIndexPages()
|
||||
{
|
||||
IndexPages = new List<IndexPage>();
|
||||
|
||||
foreach (var pageNumber in RootPage.PageNumbers)
|
||||
{
|
||||
if (pageNumber == 0)
|
||||
break;
|
||||
else
|
||||
{
|
||||
_stream.CurrentPage = pageNumber;
|
||||
|
||||
var indexPage = new IndexPage();
|
||||
indexPage.Read(_stream);
|
||||
|
||||
IndexPages.Add(indexPage);
|
||||
}
|
||||
}
|
||||
|
||||
_position = _stream.Position;
|
||||
}
|
||||
|
||||
private bool PageReadingCompleted()
|
||||
=> _dataPages[_dataPage] != _stream.CurrentPage;
|
||||
|
||||
private void GoToNextDataPage()
|
||||
{
|
||||
_dataPage++;
|
||||
|
||||
if (_dataPages.Count <= _dataPage)
|
||||
throw new Exception("There are no more data pages in the root page list");
|
||||
else
|
||||
{
|
||||
var dataPage = _dataPages[_dataPage];
|
||||
|
||||
_stream.CurrentPage = dataPage;
|
||||
}
|
||||
|
||||
_position = _stream.Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace OneSTools.FileDatabase.LowLevel.Files
|
||||
{
|
||||
public enum InnerFileType
|
||||
{
|
||||
Short = 0xFD1C,
|
||||
Full = 0x01FD1C
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel.Files
|
||||
{
|
||||
|
||||
internal class UnlimitedLengthDataFile : InnerFile
|
||||
{
|
||||
private const int UNLIMITED_DATA_BLOCK_MAX_SIZE = 256;
|
||||
|
||||
public UnlimitedLengthDataFile(FileDatabaseStream stream, uint rootPageNumber) : base(stream, rootPageNumber)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void SkipFirstBlock()
|
||||
{
|
||||
// It skips the first block, cause it's a first block of the chain of free blocks
|
||||
SkipBytes(UNLIMITED_DATA_BLOCK_MAX_SIZE);
|
||||
}
|
||||
|
||||
public void GoToBlock(uint blockNumber)
|
||||
{
|
||||
GoToDataStartingPosition();
|
||||
|
||||
SkipBytes(blockNumber * UNLIMITED_DATA_BLOCK_MAX_SIZE);
|
||||
}
|
||||
|
||||
public byte[] ReadBlockChain()
|
||||
{
|
||||
var buffer = new List<byte>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var nextBlock = BinaryPrimitives.ReadUInt32LittleEndian(ReadBytes(4));
|
||||
var blockDatalength = BinaryPrimitives.ReadUInt16LittleEndian(ReadBytes(2));
|
||||
|
||||
if (blockDatalength > 0)
|
||||
{
|
||||
var blockData = ReadBytes(blockDatalength);
|
||||
buffer.AddRange(blockData);
|
||||
}
|
||||
|
||||
if (nextBlock == 0)
|
||||
break;
|
||||
else
|
||||
GoToBlock(nextBlock);
|
||||
}
|
||||
|
||||
return buffer.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel.Pages
|
||||
{
|
||||
internal class FreePagesPage
|
||||
{
|
||||
public uint Type { get; private set; }
|
||||
public uint PagesCount { get; private set; }
|
||||
public int Version { get; private set; }
|
||||
public int[] PageNumbers { get; private set; }
|
||||
|
||||
public void Read(FileDatabaseStream reader)
|
||||
{
|
||||
var type = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
if (!(type is 0x0000FF1C))
|
||||
throw new Exception($"{type} is not a type of \"Free pages\" block");
|
||||
|
||||
Type = type;
|
||||
PagesCount = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
Version = BinaryPrimitives.ReadInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
PageNumbers = new int[PagesCount];
|
||||
|
||||
for (int i = 0; i < PagesCount; i++)
|
||||
{
|
||||
var number = BinaryPrimitives.ReadInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
if (number == 0)
|
||||
break;
|
||||
|
||||
PageNumbers[i] = number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel.Pages
|
||||
{
|
||||
internal class HeaderPage
|
||||
{
|
||||
public string Signature { get; private set; }
|
||||
public string Version { get; private set; }
|
||||
public uint PagesCount { get; private set; }
|
||||
public uint PageSize { get; private set; }
|
||||
|
||||
public void Read(FileDatabaseStream reader)
|
||||
{
|
||||
var signature = Encoding.UTF8.GetString(reader.ReadBytes(8));
|
||||
if (signature != "1CDBMSV8")
|
||||
throw new Exception("This file is not a 1C database");
|
||||
|
||||
var versionData = reader.ReadBytes(4);
|
||||
var version = $"{versionData[0]}.{versionData[1]}.{versionData[2]}.{versionData[3]}";
|
||||
if (version != "8.3.8.0")
|
||||
throw new Exception($"{version} is unknown version");
|
||||
|
||||
Signature = signature;
|
||||
Version = version;
|
||||
PagesCount = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
// skip 4 bytes of unknown value
|
||||
reader.ReadBytes(4);
|
||||
|
||||
PageSize = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel.Pages
|
||||
{
|
||||
internal class IndexPage
|
||||
{
|
||||
public ulong DataLength { get; private set; }
|
||||
public uint[] PageNumbers { get; private set; }
|
||||
|
||||
public virtual void Read(FileDatabaseStream reader)
|
||||
{
|
||||
DataLength = BinaryPrimitives.ReadUInt64LittleEndian(reader.ReadBytes(8));
|
||||
|
||||
var pageNumbers = new List<uint>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var pageNumber = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
if (pageNumber == 0)
|
||||
break;
|
||||
|
||||
pageNumbers.Add(pageNumber);
|
||||
}
|
||||
|
||||
PageNumbers = pageNumbers.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using OneSTools.FileDatabase.LowLevel.Files;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OneSTools.FileDatabase.LowLevel.Pages
|
||||
{
|
||||
internal class RootPage
|
||||
{
|
||||
public InnerFileType Type { get; private set; }
|
||||
public string Version { get; private set; }
|
||||
public ulong DataLength { get; private set; }
|
||||
public uint[] PageNumbers { get; private set; }
|
||||
|
||||
public void Read(FileDatabaseStream reader)
|
||||
{
|
||||
Type = (InnerFileType)BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
var v1 = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
var v2 = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
var v3 = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
Version = $"{v1}.{v2}.{v3}";
|
||||
|
||||
DataLength = BinaryPrimitives.ReadUInt64LittleEndian(reader.ReadBytes(8));
|
||||
|
||||
var pageNumbers = new List<uint>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var pageNumber = BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
if (pageNumber == 0)
|
||||
break;
|
||||
|
||||
pageNumbers.Add(pageNumber);
|
||||
}
|
||||
|
||||
PageNumbers = pageNumbers.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AssemblyName>OneSTools.FileDatabase</AssemblyName>
|
||||
<RootNamespace>OneSTools.FileDatabase</RootNamespace>
|
||||
<Authors>Akpaev Evgeny</Authors>
|
||||
<Company>Akpaev Evgeny</Company>
|
||||
<Description>Библиотека для чтения данных файловых информационных баз 1С</Description>
|
||||
<Copyright>Akpaev Evgeny</Copyright>
|
||||
<PackageIcon>onestools_icon_nuget.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OneSTools.BracketsFile" Version="2.1.4" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\onestools_icon_nuget.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<AssemblyName>OneSTools.FileDatabaseTestApp</AssemblyName>
|
||||
<RootNamespace>OneSTools.FileDatabaseTestApp</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OneSTools.FileDatabase\OneSTools.FileDatabase.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,46 @@
|
||||
using OneSTools.FileDatabase;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OneSTools.FileDatabase.HighLevel;
|
||||
|
||||
namespace OneSTools.FileDatabaseTestApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var filePath = @"C:\Users\akpaev.e.ENTERPRISE\Desktop\Новая папка (2)\1Cv8.1CD";
|
||||
var filePath2 = @"C:\Users\akpaev.e.ENTERPRISE\Documents\InfoBase10\1Cv8.1CD";
|
||||
|
||||
using var database = new FileDatabaseConnection(filePath2);
|
||||
database.Open();
|
||||
|
||||
var table = database.Tables.FirstOrDefault(c => c.Name == "_Document38");
|
||||
|
||||
if (table != null)
|
||||
{
|
||||
foreach (var values in table.Rows)
|
||||
{
|
||||
for (int i = 0; i < table.Fields.Count; i++)
|
||||
{
|
||||
var field = table.Fields[i];
|
||||
var value = values[i];
|
||||
|
||||
// Or another one what you need
|
||||
if (field.Type == FieldType.Numeric)
|
||||
{
|
||||
var typedValue = (decimal?)value;
|
||||
}
|
||||
if (field.Type == FieldType.NChar
|
||||
|| field.Type == FieldType.NText
|
||||
|| field.Type == FieldType.NVarChar)
|
||||
{
|
||||
var typedValue = (string)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user