Считать числовые данные из текстового SPICE файла и записать их в свои переменные - C#
Формулировка задачи:
Доброго времени суток!...возникла такая проблемка: имеется текстовый файл формата SPICE (пример ниже):
.model 2T3107E PNP (Is=991.3f Xti=3 Eg=1.11 Vaf=35 Bf=210 Ne=1.58
+ Ise=8.12p Ikf=80m Xtb=1.5 Br=2.86 Nc=2 Isc=100p Ikr=80m Rc=7
+ Cjc=7.37p Vjc=.7 Mjc=.333 Fc=.5 Cje=10.6p Vje=.75 Mje=.3333
+ Tr=80p Tf=113p Itf=8m Vtf=50 Xtf=1.2)
Нужно прочитать из такого файла ЧИСЛОВЫЕ значения параметров элемента (в данном случае описание модели транзистора) и записать каждый параметр в свою переменную, например: значение Is=991.3f в переменную, допустим, double IS, Xti=3 в переменную double Xti и т. д.
Особенности задачи определяются особеннностями синтаксиса языка SPICE, а именно:
а) параметры, которые необходимо записать в переменную заключены в круглые скобки; они (скобки) -
ОБЯЗАТЕЛЬНЫЙ элемент синтаксиса модели;
б) между наименованием параметра, знаком "равно" (знак "равно" является обязательным элементом синтаксиса) и числовым значением пробелов может не быть, может быть один или несколько, например: Is=991.3f или Is =991.3f или
Is= 991.3f или Is = 991.3f и т. д.;
в) распознать и определить множитель по буквенному индексу, например Ise=8.12p (пико-) это 8,12 умноженное на 10^-9 (т. е. занести в переменную double Ise = 8,12 / 10^9); Ikr=80m это 80 мили-, т .е. в переменную занести double Ikr=80 / 10^3 и т. д.;
г) между собой параметры ВСЕГДА разделены НЕ менее чем одним пробелом (больше одного возможно);
д) целая и дробная части числа могут разделяются как точкой, так и запятой, например: может быть так Br=2.86 или так Br=2,86 - при записи в переменную точки заменить на запятые;
е) при отсутствии у числа целой части - нуль в начале записи может присутствовать, а может и нет, например: может быть так Mjc=.333 или вот так Mjc=0.333 - при записи в перемнную автоматически подставлять "недостающий" нуль;
ж) последовательность перечисления параметров внутри круглых скобок - произвольная;
Дополнительные пояснения:
а) знак "плюс" - перенос строки (при чтении параметров игнорируется);
б) описание модели ВСЕГДА начинается с выражения ".model", а заканчивается закрывающей скобкой;
в) круглых скобок (с параметрами) всегда один "комплект".
Хотелость бы посмотреть похожий пример, причем не обязательно для SPICE, это может просто поиск в текстовом файле каких-нибудь именованных величин с их последующем занесем в переменные или, возможно, кто-то сможет показать (на примере одного-двух параметров) как реализовать поставленные задачи.
Заранее благодарен.
Решение задачи: «Считать числовые данные из текстового SPICE файла и записать их в свои переменные»
textual
Листинг программы
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
private static string Data = @".model 2T3107E PNP (Is=991.3f Xti= 3 Eg =1.11 Vaf = 35
+Bf=210 Ne=1.58
+ Ise=8.12p Ikf=80m Xtb=1.5 Br=2.86 Nc=2 Isc=100p Ikr=80m Rc=7
+ Cjc=7.37p Vjc=.7 Mjc=.333 Fc=.5 Cje=10.6p Vje=.75 Mje=.3333
+ Tr=80p Tf=113p Itf=8m Vtf=50 Xtf=1.2)
.model Q2N2222A NPN (IS=14.34F XTI=3 EG=1.11 VAF= 74.03 BF=255.9 NE=1.307 ISE=14.34F IKF=.2847 XTB=1.5 BR=6.092 NC=2 ISC=0 IKR=0 RC=1 CJC=7.306P MJC=.3416 VJC=.75 FC=.5 CJE=22.01P MJE=.377 VJE=.75 TR=46.91N TF=411.1P ITF=.6 VTF=1.7 XTF=3 RB=10)";
private static void Main(string[] args)
{
var ms = new StreamWriter(new MemoryStream());
ms.Write(Data);
ms.Flush();
ms.BaseStream.Position = 0L;
var sp = new SpiceParser(ms.BaseStream);
}
}
public class SpiceModelParam
{
public SpiceModelParam()
{
}
public SpiceModelParam(string name, float value = 0f)
{
Name = name;
Value = value;
}
public string Name { get; set; }
public float Value { get; set; }
public override string ToString()
{
return string.Format("{0} = {1}", Name, Value);
}
}
public class SpiceModel
{
public SpiceModel()
{
Parameters = new List<SpiceModelParam>();
}
public SpiceModel(string name)
: this()
{
Name = name;
}
public string Name { get; set; }
public List<SpiceModelParam> Parameters { get; private set; }
public override string ToString()
{
return string.Format("{0} [{1}]", Name, Parameters.Count);
}
}
public class SpiceParser
{
private const string ModelID = ".model";
private StreamReader mReader;
public SpiceParser(Stream stream)
{
if (!stream.CanRead || !stream.CanSeek)
throw new Exception();
mReader = new StreamReader(stream);
Models = new List<SpiceModel>();
Parse();
}
public List<SpiceModel> Models { get; private set; }
private void Parse()
{
while (!mReader.EndOfStream)
{
var line = ReadFullModel();
Models.Add(ParseModel(line));
}
}
private string ReadFullModel()
{
var sb = new StringBuilder();
while (true)
{
var line = mReader.ReadLine();
sb.Append(line);
if (line.EndsWith(")"))
break;
}
return sb.ToString();
}
unsafe private void FixLine(char* ptr, int len)
{
var end = ptr + len;
while (ptr != end)
{
if (*ptr == '+' || *ptr == '\n' || *ptr == '\r')
*ptr = ' ';
ptr++;
}
}
unsafe private SpiceModel ParseModel(string line)
{
int offset = 0;
if (!line.StartsWith(ModelID))
return null;
offset += ModelID.Length + 1;
var model = new SpiceModel();
fixed (char* ptr = line)
{
FixLine(ptr, line.Length);
char* pName = ptr + offset;
pName = ParseModelName(pName, model);
if (*pName == '(')
ParseParams(++pName, model);
}
return model;
}
unsafe private char* ParseModelName(char* pName, SpiceModel model)
{
int i = 0;
for (; pName[i] != '('; ++i)
;
model.Name = new string(pName, 0, i - 1);
return pName + i;
}
unsafe private void ParseParams(char* ptr, SpiceModel model)
{
while (true)
{
var p = new SpiceModelParam();
ptr = ParseParamName(ptr, p);
ptr = ParseParamEqual(ptr);
ptr = ParseParamValue(ptr, p);
model.Parameters.Add(p);
if (*ptr == ')')
return;
else
ptr++;
}
}
private const float P = -1 * 10f * 10f * 10f * 10f * 10f * 10f * 10f * 10f * 10f;
private const float M = -1f * 10f * 10f * 10f;
private readonly char DecimalSeparator = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator[0];
unsafe private float ParseParamFactor(char* ptr)
{
switch (*ptr)
{
case 'p':
case 'P':
return P;
case 'm':
case 'M':
return M;
default:
return 1f;
}
}
unsafe private char* ParseParamValue(char* ptr, SpiceModelParam p)
{
int i = 0;
while (*ptr == ' ')
ptr++;
while (true)
{
// Если . или , то заменяем её на символ
// который применяется в текущей локализации
if (ptr[i] == '.' || ptr[i] == ',')
{
ptr[i] = DecimalSeparator;
}
// Если текущий символ не цифра, то завершаемся
else if (!(ptr[i] >= '0' && ptr[i] <= '9'))
break;
i++;
}
var str = new string(ptr, 0, i);
p.Value = float.Parse(str);
ptr += i;
if (*ptr != ' ')
p.Value *= ParseParamFactor(ptr);
return ptr;
}
unsafe private char* ParseParamEqual(char* ptr)
{
// Пропускаем пробелы
while (*ptr == ' ')
ptr++;
if (*ptr != '=') // Проверка корректности позиции
throw new Exception();
return ++ptr;
}
unsafe private char* ParseParamName(char* ptr, SpiceModelParam p)
{
int i = 0;
// Пропускаем пробелы
while (*ptr == ' ')
ptr++;
// Имя параметра, до пробела или =
while (ptr[i] != ' ' && ptr[i] != '=')
i++;
p.Name = new string(ptr, 0, i);
// Перемещаемся за название параметра
return ptr + i;
}
}
}