Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Accessing an array in Memo field
Message
From
13/08/2012 08:08:07
 
 
General information
Forum:
Visual FoxPro
Category:
Databases,Tables, Views, Indexing and SQL syntax
Miscellaneous
Thread ID:
01550185
Message ID:
01550384
Views:
109
>Don't think I'll get a chance to pursue this until after next week but I may take you up on your offer of extra help if I'm stuck....

Ok, here it comes

(1) Sample input and output

(2) Explanation, design, choices

(3) Full code at the end




(1) Sample input and output

vfp code
	local abc
	
	abc=repl('x', 256)
	abcshort = '1234546'
	a2345678900=.f.
	a2345678901=.t.
	a2345678902=804.3
	a2345678903={^2012/01/01}
	a2345678904={^2012/01/01 01:02:03}
	a2345678905=$-1234.5678
	a2345678906 = null
	
	
	dime aa[2]
	aa[1] = 1
	aa[2] = 2

	dime LongNameArray[4, 3]
	local i, j
	for i = 1 to alen(LongNameArray, 1)
		for j = 1 to alen(LongNameArray, 2)
			LongNameArray[i, j] = i + j/100
		endfor
	endfor
	
	save to 'd:\tmp\1.txt'
Code to produce the output below
	class VfpVariableTest
	{
		public static void Main()
		{
			byte[] bytes = File.ReadAllBytes(@"d:\tmp\1.txt");

			Dictionary<String, VfpVariable> dict = VfpMemoryRestore.GetVariables(bytes, false);

			foreach (var v in dict)
			{
				Console.Write("Name: {0} Type: {1}", v.Value.Name, v.Value.Type);

				if (v.Value.Type == VfpValueType.Array)
				{
					if (v.Value.Value is VfpVariable[])
					{	// one dimensional array
						VfpVariable[] vfpArray = v.Value.Value as VfpVariable[];
						Console.WriteLine();
						for (int i = 0; i < vfpArray.Length; i++)
							Console.WriteLine("{0}[{1}] = {2}", vfpArray[i].Name, i, vfpArray[i].Value);

					}
					else if (v.Value.Value is VfpVariable[,])
					{	// two dimensional array
						VfpVariable[,] vfpArray = v.Value.Value as VfpVariable[,];
						Console.WriteLine();
						for (int i = 0; i <= vfpArray.GetUpperBound(0); i++)
							for (int j = 0; j <= vfpArray.GetUpperBound(1); j++)
								Console.WriteLine("{0}[{1},{2}] = {3}", vfpArray[i, j].Name, i, j, vfpArray[i, j].Value);
					}
				}
				else if (v.Value.Value == null)
				{
					Console.WriteLine(" Value: {0}", "null");
				}
				else if (v.Value.Type == VfpValueType.Char)
				{ // treat as chars here
					byte[] s = (byte[])v.Value.Value;

					Console.WriteLine(" Value: {0}", Encoding.Default.GetString(s, 0, s.Length));
				}
				else // no array
				{
					Console.WriteLine(" Value: {0}", v.Value.Value);
				}
			}
			Console.ReadLine();
		}
	}
output
Name: ABC Type: Char Value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Name: A2345678901 Type: Logical Value: True
Name: A2345678902 Type: Numeric Value: 804.3
Name: A2345678903 Type: Date Value: 01/01/2012 00:00:00
Name: A2345678904 Type: DateTime Value: 01/01/2012 01:02:03
Name: I Type: Numeric Value: 5
Name: AA Type: Array
AA[0] = 1
AA[1] = 2
Name: J Type: Numeric Value: 4
Name: ABCSHORT Type: Char Value: 1234546
Name: A2345678905 Type: Currency Value: -1234.5678
Name: A2345678900 Type: Logical Value: False
Name: A2345678906 Type: Logical Value: null
Name: LONGNAMEARRAY Type: Array
LONGNAMEARRAY[0,0] = 1.01
LONGNAMEARRAY[0,1] = 1.02
LONGNAMEARRAY[0,2] = 1.03
LONGNAMEARRAY[1,0] = 2.01
LONGNAMEARRAY[1,1] = 2.02
LONGNAMEARRAY[1,2] = 2.03
LONGNAMEARRAY[2,0] = 3.01
LONGNAMEARRAY[2,1] = 3.02
LONGNAMEARRAY[2,2] = 3.03
LONGNAMEARRAY[3,0] = 4.01
LONGNAMEARRAY[3,1] = 4.02
LONGNAMEARRAY[3,2] = 4.03
(2) Explanation, design, choices
We start off with a class
class VfpVariable
	{
		public readonly string Name;
		public VfpValueType Type { get; private set; }
		public VfpVariableScope Scope { get; private set; }

		private object _Value;
		public object Value { get; private set;}
	// rest of code is at the end

	}
VfpValueType is an enum
You'll see that the vfp string is mapped to a byte array - since there is no way of knowing whether the string contains bytes or characters

The vfp array is implemented as a one or two dimensional C# array of VfpVariable[]
	enum VfpValueType
	{
		Logical = 0,	// bool
		Numeric,		// double
		Currency,		// decimal
		Char,			// byte[]
		Date,			// datetime
		DateTime,		// datetime
		Array,			// VfpVariable[] or VfpVariable[ , ]
		Object			// not implemented 
	}
The scope is an enum - but I doubt you will care
	enum VfpVariableScope
	{
		Public,
		Private,
		Local
	}
The Value is implemented as an object - could have subclassed - but I found it too much of a burden
So casts ( after testing whether the Value == null) are needed

There is another class which collects the variables from a byte array and returns a dictionary of variables

You put the contents of the file or memo field into a byte[]. You need to tell whether the source was from a memo or not since memo fields contain two extra bytes at the beginning
static class VfpMemoryRestore
	{
                            // rest of the code at the end

		internal static Dictionary<String, VfpVariable> GetVariables(byte[] buffer, bool isMemoField)
		{  / code at the end
		}
	}
(3) Full code at the end
	enum VfpValueType
	{
		Logical = 0,	// bool
		Numeric,		// double
		Currency,		// decimal
		Char,			// byte[]
		Date,			// datetime
		DateTime,		// datetime
		Array,			// VfpVariable[] or VfpVariable[ , ]
		Object			// not implemented 
	}
	enum VfpVariableScope
	{
		Public,
		Private,
		Local
	}
class VfpVariable
	{
		public readonly string Name;
		public VfpValueType Type { get; private set; }
		public VfpVariableScope Scope { get; private set; }

		private object _Value;
		public object Value
		{
			get
			{
				return _Value;
			}
			private set
			{

				if (value == null)
					; // ok
				else switch(Type)
				{
					case VfpValueType.Array:
						if( !(value is VfpVariable[]) && !(value is VfpVariable[,]))
							throw new ArgumentException("Wrong value for type");
						break;

					case VfpValueType.Char:
						if( !(value is byte[]) )
							throw new ArgumentException("Wrong value for type");
						break;

					case VfpValueType.Currency:
						if( ! (value is decimal) )
							throw new ArgumentException("Wrong value for type");
						break;

					case VfpValueType.Date:

						if( !(value is DateTime) || ((DateTime)value).Date != (DateTime)value)
							throw new ArgumentException("Wrong value for type");
						break;
					case VfpValueType.DateTime:
						if( !(value is DateTime) )
							throw new ArgumentException("Wrong value for type");
						break;
					case VfpValueType.Logical:
						if( !(value is bool) )
							throw new ArgumentException("Wrong value for type");
						break;
					case VfpValueType.Numeric:
						if (!(value is int) && !(value is uint) && !(value is Double))
							throw new ArgumentException("Wrong value for type");
						break;

					case VfpValueType.Object:
						throw new NotImplementedException("Object type");
						break;
					default:
						throw new NotImplementedException("Type");

				}
				
				_Value = value;
			}
		}
		//______________________________________________________________________
		public VfpVariable(string name, VfpValueType type, object value, VfpVariableScope scope)
		{
			CheckName(name);
			Name = name;
			Type = type;  // type must be assiged before value (see set accessor of Value)
			Value = value;
			Scope = scope;
		}
		//______________________________________________________________________
		// other checks to add 
		private void CheckName(string name)
		{
			if (String.IsNullOrWhiteSpace(name))
				throw new ArgumentException("Invalid name");
		}
		//______________________________________________________________________
	}
	static class VfpMemoryRestore
	{
		/*
		 * 	00-10	char[11] upper(name) '\0 terminated
		*	11-11	char Type, if lower(): extended name, except 0 (Null)
		*	12-15 	length strings > 254 ( type H, includes \0)
		*	16-16	Length for
		 *				- char <= 255 chars ( includes \0' )
		 *				
		 *			
		 *			not for
		 *				- logical
		 *				- numeric
		 *				- date
		 *				- dateTime
		 *				- null - not always
		 *				
		 *					
		 *			
		*	17-17	precision for numeric
		*	20-23 ??
		*	24-24	0x00 = Private/public, 0x01 = local
		*	25-25	constant : 0x03
		*	26-31 ?
		&& case extended name
		*	32-33	len Name if extended name
		*	34-...	name
		*	..-..	Value
		&& otherwise
		*	32 -	Value  (or type if 11-11 == '0'
		*
		 */
		private const int EndOfFileMarker = 0x1a;
		private const int VariableTypeOffset = 11;
		private const int ValueOffset = 0x20;
		private const int ScopeOffset = 24;
		private const int ContentLengthOffset = 16;
		private const int LongStringContentLengthOffset = 12;

		//______________________________________________________________________
		internal static Dictionary<String, VfpVariable> GetVariables(byte[] buffer, bool isMemoField)
		{
			Dictionary<String, VfpVariable> dict = new Dictionary<string, VfpVariable>();

			int offset = isMemoField ? 2 : 0;

			VfpVariable v;

			while (offset < buffer.Length)
			{
				if (offset == buffer.Length - 1 && buffer[offset] == EndOfFileMarker)
					break;

				v = GetVariable(buffer, ref offset);
				dict.Add(v.Name, v);
				}

			return dict;

		}
		//______________________________________________________________________
		private static VfpVariable GetVariable(byte[] buffer, ref int offset)
		{
			int valueOffset;

			// Name
			// valueOffset depends on the length of the name
			string name = GetName(buffer, offset, out valueOffset);

			// Scope
			VfpVariableScope scope = GetScope(buffer[offset + ScopeOffset]);
			

			//Type and value
			VfpValueType type;

			object value;
			int newOffset;
			byte[] x; // for strings
			int contentLength;

			byte varType = buffer[offset + VariableTypeOffset];

			if (varType == (byte)'0')
			{
				type = GetVarType(buffer[offset + valueOffset]);
				contentLength = 1;
				value = null;
				newOffset = offset + valueOffset + contentLength;
			}
			else
			{
				type = GetVarType(varType);

				switch (type)
				{
					case VfpValueType.Char:
						if (varType == (byte)'C' || varType == (byte)'c')
						{
							contentLength = buffer[offset + ContentLengthOffset];
							x = new byte[contentLength - 1];
							Array.Copy(buffer, offset + valueOffset, x, 0, contentLength - 1);
							value = x;

						}
						else
						{
							contentLength = BitConverter.ToInt32(buffer, offset + LongStringContentLengthOffset);
							x = new byte[contentLength - 1];
							Array.Copy(buffer, offset + valueOffset, x, 0, contentLength - 1);
							value = x;
						}
						newOffset = offset + valueOffset + contentLength;
						break;

					case VfpValueType.Logical:
						contentLength = 1;
						value = (buffer[offset +valueOffset] != 0x00);
						newOffset = offset + valueOffset + contentLength;
						break;

					case VfpValueType.Numeric:
						contentLength = 8;
						value = BitConverter.ToDouble(buffer, offset + valueOffset);
						newOffset = offset + valueOffset + contentLength;
						break;

					case VfpValueType.Date :
					case VfpValueType.DateTime:
						contentLength = 8;
						double d = BitConverter.ToDouble(buffer, offset + valueOffset);
						if (d == 0.0)
							value = null;
						else
						{
							if (type == VfpValueType.Date)
								d = d < 0.0 ? Math.Ceiling(d) : Math.Floor(d);
							value = DateTime.FromOADate(d - 2415019.0); //2415019 is julian day of 1899 dec 30 
						}
						newOffset = offset + valueOffset + contentLength;
						break;

					case VfpValueType.Currency:
						contentLength = 8;
						Int64 v64 = BitConverter.ToInt64(buffer, offset + valueOffset);
						value = (decimal)v64 / 10000.0m;
						newOffset = offset + valueOffset + contentLength;
						break;

					case VfpValueType.Array:
						contentLength = 4; // for rowCount and columnCount
						uint rowCount = BitConverter.ToUInt16(buffer, offset + valueOffset);
						uint colCount = BitConverter.ToUInt16(buffer, offset + valueOffset + sizeof(UInt16));
						newOffset = offset + valueOffset + contentLength;

						if (colCount == 0)
						{	// one dimensional array
							VfpVariable[] array = new VfpVariable[rowCount];
							for (int i = 0; i < rowCount; i++)
								array[i] =  GetVariable(buffer, ref newOffset);
			
							value = array;
						}
						else
						{	// two dimensional array
							VfpVariable[,] array = new VfpVariable[rowCount, colCount];
							for (int i = 0; i < rowCount; i++)
								for( int j = 0; j < colCount; j++)
									array[i, j] = GetVariable(buffer, ref newOffset);

							value = array;
						}
						break;

					default:
						throw new NotImplementedException(String.Format("Program error: type {0}", type));
				}
			}
			
			VfpVariable v = new VfpVariable(name, type, value, scope);
			offset = newOffset;
			return v;

		}
		//______________________________________________________________________
		private static VfpValueType GetVarType(byte varType)
		{
			VfpValueType type;

			switch (varType)
			{
				case (byte)'C':
				case (byte)'c':
				case (byte)'H':
				case (byte)'h':
					type = VfpValueType.Char;
					break;

				case (byte)'L':
				case (byte)'l':
					type = VfpValueType.Logical;
					break;

				case (byte)'N':
				case (byte)'n':
					type = VfpValueType.Numeric;
					break;

				case (byte)'D':
				case (byte)'d':
					type = VfpValueType.Date;
					break;

				case (byte)'T':
				case (byte)'t':
					type = VfpValueType.DateTime;
					break;

				case (byte)'Y':
				case (byte)'y':
					type = VfpValueType.Currency;
					break;

				case (byte)'A':
				case (byte)'a':
					type = VfpValueType.Array;
					break;

				default:
					throw new NotImplementedException(String.Format("Program error: GetVarType {0}", varType.ToString()));
			}
			return type;
		}
		//______________________________________________________________________
		private static string GetName(byte[] buffer, int offset, out int valueOffset)
		{
			int nameOffset, count;

			if (buffer[offset] != 0) //short name, null terminated 
			{	// name <= 10 bytes
				valueOffset = ValueOffset;
				nameOffset = offset;
				count = Array.IndexOf<byte>(buffer, 0, nameOffset) - nameOffset;
				if (count < 0 || count > 10)
					throw new Exception("Name inconsistency");

			}
			else
			{
				count = BitConverter.ToInt16(buffer, offset + ValueOffset);
				valueOffset = ValueOffset + count + 2;
				nameOffset = offset + ValueOffset + 2;
			}
			string name = Encoding.Default.GetString(buffer, nameOffset, count);

			return name;
		}
		//______________________________________________________________________
		private static VfpVariableScope GetScope(byte b)
		{
			VfpVariableScope scope;
			switch (b)
			{
				case 0x00:
					scope = VfpVariableScope.Private;
					break;

				case 0x01:
					scope = VfpVariableScope.Local;
					break;

				default:
					throw new NotImplementedException(String.Format("Program error: Scope {0}", b.ToString()));
			}
			return scope;
		}
		//______________________________________________________________________
	}
Gregory
Previous
Next
Reply
Map
View

Click here to load this message in the networking platform