C#从文本文件中读取多维变量
Reading multi-dimensional variables from text file in C#
我正在尝试在 C# 中创建一个 GUI(Windows 表单应用程序,Visual Studio)来读取和写入某些变量 from/to 文本文件。我对 GUI 本身没有问题,但我需要一个可以读取(并最终写入)文本文件、识别变量并将它们放入内部变量或直接放入 GUI 控件的函数。文本文件看起来像这样(虽然长得多):
/* Control */
/YD/YDC/MCSTEP 20001 /YD/YDC/NCSTEP 0
/YD/YDC/DCSIZC 0.075
/* Properties */
/YD/YDPE/D1PEFR 3
0.7 0.75 .5
/YD/YDPM/I2PMSET 21 3 2
1 3 0
1 0 1
/YD/YDPN/D3PNFAC 231 2 3 4
0 0
1e-010 0
9.2966e-008 0
1.8583e-007 0
0 0
1e-010 0
9.2966e-008 0.71221
1.8583e-007 1.4688
0 0
1e-010 0
9.2966e-008 0
1.8583e-007 0
我有一个 C 语言的代码可以读取文本文件,但是它太长了。我的印象是,这可以用 C# 中相对较短的代码优雅地完成。我已经尝试按照建议使用字典 here,但是我的文本文件的格式对它来说太复杂了……除非那里的一些大师知道一些可以帮助我的技巧。
欢迎您提出建议。示例代码将不胜感激。
下面定义了文本文件的格式:
- 所有变量都以
/YD/
开头。
/*
和*/
之间的文字是注释(无需阅读)。
- 分隔符可以是空格或制表符,您可以在变量和值之间使用一个或多个分隔符。你也可以有空行。
- 有时每一行有多个变量。
- 有时一个变量和它的值在不同的行。
- 一些变量是单值,其他变量是一维、二维或三维。
- 形式为 xxxxxx(不一定是固定字符数)的变量后跟它的单个值。
- 形式为 x1xxxxx 的变量是一维的。这些后面跟着一个
告诉您变量大小的整数,后跟
变量的值(例如变量
/YD/YDPE/D1PEFR 3 0.7 0.75 .5
是一维变量,具有三个值:0.7、0.75 和 0.5)。
- 形式为 x2xxxxx 的变量是二维的。首先是一个数字,告诉您如何读取变量,然后是变量的大小(2 个数字),然后是变量的实际值。例如。变量
/YD/YDPM/I2PMSET 21 3 2 1 3 0 1 0 1
是一个二维变量,其中数字 21 表示您首先阅读更改 "x" 坐标,然后是 "y" 坐标(12 表示您首先阅读更改 "y" then "x"),后面的数字3
和2
表示大小为[3,2],后面6个数字依次为变量的值(x1y1, x2y1, x3y1, x1y2, x2y2, x3y2)
.
- 最后,x3xxxx 形式的变量是三维的。这些后面跟着一个数字,告诉您如何阅读它,然后是大小(3 个数字),然后是值。例如。变量
/YD/YDPN/D3PNFAC 231 2 3 4 ...
是 3-D,首先读取 "x",然后读取 "z",然后读取 "y",大小为 [2,3,4]。然后按 (x1y1z1, x2y1z1, x1y1z2, x2y1z2, x1y1z3, x2y1z3, x1y1z4, x2y1z4, x1y2z1, x2y2z1, ...)
. 的顺序读取值
Mu 建议重构该文件的构建方式。这样您就可以使用 XML 结构并使所有代码 更易读 。
看看:XDocument (System.Xml.Linq
)
这是您可以执行的操作的示例:
<Root>
<Controls>
<Control/>
...
</Controls>
<Properties>
<Property/>
...
</Properties>
</Root>
但是如果您不能更改您应该使用的文件:
File.ReadLines
像这样:
foreach (var line in File.ReadLines("FilePath"))
{
//Do your work with the [line]
}
要获得 variables
,请查看 string class。更具体地看一下 Split
、Replace
和 Trim
方法。
我认为 C# 中的任何东西都不会神奇地为你做这件事,但为了避免我想象你在 C 中使用的意大利面条代码,我会将其分解成一些 classes每个负责给定的变量类型。抱歉,这有点伪代码,但我希望它能有所帮助。
首先是一个碱基class:
public abstract class DataValue {
public string Category { get; protected set; } // value after /YD
public string Name { get; protected set; } // value after /YD/XXX/
public int DimensionCount { get; protected set; } // number of dimensions
public int[] Dimensions { get; protected set; } // The size of each dimension
protected void ParseHeader(string header) {
// Split of the header into the Category/Name and set it
}
public virtual void ReadValue(string header, StreamReader reader) {
ParseHeader(header);
}
}
然后 class 为每个变量类型:
public class ScalarValue: DataValue {
public ScalarValue() {
DimensionCount = 1;
Dimensions = new [] { 1 };
}
public override void ReadValues(string header, StreamReader reader) {
base.ReadValues(header, reader);
// Do custom logic for reading a scalar here.
}
}
您可以为每种主要类型制作一个 class。他们各自负责自己的阅读逻辑。您需要一个主要的解析器循环来查找变量、创建对象并分派它们:
public static List<DataValue> ParseDataFile(string filePath) {
var reader = new StreamReader(filePath);
var values = new List<DataValue>();
var variableHeader = FindNextVariable(reader);
while(!string.IsNullOrEmpty(variableHeader)) {
DataValue v;
switch(FindVariableType(variableHeader)) {
case Scalar:
v = new ScalarValue();
case OneDimensionalArray:
v = new Array1DValue();
// etc
}
v.ReadValue(variableHeader, reader);
values.Add(v);
variableHeader = FindNextVariable(reader);
}
return values;
}
FindNextVariable 是:
public static string FindNextVariable(StreamReader reader) {
var i = reader.Read();
bool commentBlock = false;
string variable = "";
while (i >= 0) {
var c = (char)i;
// if it's white space, toss it
// if it's a / peek to see if the next is a * starting a comment block
// if in a comment block, read until you get */ consecutively
// if it's a / and not a * next, begin appending to variable until you hit white space again.
}
return variable;
}
FindVariableType 是:
public enum VariableType {
Scalar,
OneDimensionalValue,
TwoDimensionalValue,
ThreeDimensionalValue,
Other
}
public static VariableType FindVariableType(string header) {
var fields = string.Split('/', header);
// take the third item, parse it to determine type
}
实际实施的方法有很多,但这是一般的想法。
好的,这是我的解决方案。我在代码中包含了一些注释,因此它或多或少是不言自明的。
总而言之,我定义了一个class(一个class用于所有变量,即使它们是多维的!),然后我将所有变量存储在字典中。我认为这很简单。
class 存储我的变量:
public class Variable
{
// Properties.
public string Database { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public int Dimensions { get; set; }
public int Order { get; set; }
public int[] Size { get; set; }
public double[] Value { get; set; }
// Default constructor.
public Variable()
{
}
// Constructor.
public Y_Variable(string db, string nam, string typ, int dim, int ord,
int[] siz, double[] val)
{
this.Database = db;
this.Name = nam;
this.Type = typ;
this.Dimensions = dim;
this.Order = ord;
this.Size = siz;
this.Value = val;
}
}
读取文件并将变量存入字典的代码:
public class Y_File2Dictionary
{
public static Dictionary<string, Y_Variable> File2Dictionary(string fileName)
{
// Define delimiters and block comments.
char[] delimiterChars = { ' ', '\t', '\n', '\r' };
var blockComments = @"/\*(.*?)\*/";
// Read the file, remove comments, and store in array with no spaces.
string[] arrayAllVars = Regex
.Replace(File.ReadAllText(fileName), blockComments, "",
RegexOptions.Singleline)
.Split(delimiterChars, StringSplitOptions.RemoveEmptyEntries);
// Initialize dictionary.
Dictionary<string, Y_Variable> dictAllVars = new
Dictionary<string, Y_Variable>();
// Loop to read variables and store them in dictionary.
int i = 0;
while (i < arrayAllVars.Length)
{
// Read only variables.
if (!char.Equals(arrayAllVars[i][0], '/'))
{
i++;
continue;
}
// Read variable name and separate into array
// e.g. /YD/YDC/DCSTEC => {"", YD, YDC, DCSTEC}.
string[] arrayVarName = Regex.Split(arrayAllVars[i++], "/");
// Identify variable.
string varDb = arrayVarName[2];
string varName = arrayVarName[3];
string varTyp = (varName[0] == 'D') ? "double" : "integer";
int varDim = ((int)Char.GetNumericValue(varName[1]) > 0) ?
((int)Char.GetNumericValue(varName[1])) : 0;
// Initiallization of variables to store order and size.
int varOrder = 0;
int[] varSize = new int[3] { 1, 1, 1 };
// Update order and size, depending on the number of dimensions.
switch (varDim)
{
case 1:
varOrder = 1;
Int32.TryParse(arrayAllVars[i++], out varSize[0]);
varSize[1] = 1;
varSize[2] = 1;
break;
case 2:
Int32.TryParse(arrayAllVars[i++], out varOrder);
Int32.TryParse(arrayAllVars[i++], out varSize[0]);
Int32.TryParse(arrayAllVars[i++], out varSize[1]);
varSize[2] = 1;
break;
case 3:
Int32.TryParse(arrayAllVars[i++], out varOrder);
Int32.TryParse(arrayAllVars[i++], out varSize[0]);
Int32.TryParse(arrayAllVars[i++], out varSize[1]);
Int32.TryParse(arrayAllVars[i++], out varSize[2]);
break;
default:
varOrder = 0;
varSize[0] = 1;
varSize[1] = 1;
varSize[2] = 1;
break;
}
// Determine total size of variable, get values as strings.
var varTotalSize = varSize[0] * varSize[1] * varSize[2];
string[] varValStr = new string[varTotalSize];
Array.Copy(arrayAllVars, i, varValStr, 0, varTotalSize);
// Convert values from string to double.
double[] varValDbl = new double[varTotalSize];
varValDbl = Array.ConvertAll(varValStr, Double.Parse);
// Add variable to dictionary.
if (dictAllVars.ContainsKey(varDb + "_" + varName))
dictAllVars.Remove(varDb + "_" + varName);
dictAllVars.Add(varDb + "_" + varName, new Y_Variable(varDb, varName,
varTyp, varDim, varOrder, varSize, varValDbl));
i += varTotalSize;
}
return dictAllVars;
}
}
我正在尝试在 C# 中创建一个 GUI(Windows 表单应用程序,Visual Studio)来读取和写入某些变量 from/to 文本文件。我对 GUI 本身没有问题,但我需要一个可以读取(并最终写入)文本文件、识别变量并将它们放入内部变量或直接放入 GUI 控件的函数。文本文件看起来像这样(虽然长得多):
/* Control */
/YD/YDC/MCSTEP 20001 /YD/YDC/NCSTEP 0
/YD/YDC/DCSIZC 0.075
/* Properties */
/YD/YDPE/D1PEFR 3
0.7 0.75 .5
/YD/YDPM/I2PMSET 21 3 2
1 3 0
1 0 1
/YD/YDPN/D3PNFAC 231 2 3 4
0 0
1e-010 0
9.2966e-008 0
1.8583e-007 0
0 0
1e-010 0
9.2966e-008 0.71221
1.8583e-007 1.4688
0 0
1e-010 0
9.2966e-008 0
1.8583e-007 0
我有一个 C 语言的代码可以读取文本文件,但是它太长了。我的印象是,这可以用 C# 中相对较短的代码优雅地完成。我已经尝试按照建议使用字典 here,但是我的文本文件的格式对它来说太复杂了……除非那里的一些大师知道一些可以帮助我的技巧。
欢迎您提出建议。示例代码将不胜感激。
下面定义了文本文件的格式:
- 所有变量都以
/YD/
开头。 /*
和*/
之间的文字是注释(无需阅读)。- 分隔符可以是空格或制表符,您可以在变量和值之间使用一个或多个分隔符。你也可以有空行。
- 有时每一行有多个变量。
- 有时一个变量和它的值在不同的行。
- 一些变量是单值,其他变量是一维、二维或三维。
- 形式为 xxxxxx(不一定是固定字符数)的变量后跟它的单个值。
- 形式为 x1xxxxx 的变量是一维的。这些后面跟着一个
告诉您变量大小的整数,后跟
变量的值(例如变量
/YD/YDPE/D1PEFR 3 0.7 0.75 .5
是一维变量,具有三个值:0.7、0.75 和 0.5)。 - 形式为 x2xxxxx 的变量是二维的。首先是一个数字,告诉您如何读取变量,然后是变量的大小(2 个数字),然后是变量的实际值。例如。变量
/YD/YDPM/I2PMSET 21 3 2 1 3 0 1 0 1
是一个二维变量,其中数字 21 表示您首先阅读更改 "x" 坐标,然后是 "y" 坐标(12 表示您首先阅读更改 "y" then "x"),后面的数字3
和2
表示大小为[3,2],后面6个数字依次为变量的值(x1y1, x2y1, x3y1, x1y2, x2y2, x3y2)
. - 最后,x3xxxx 形式的变量是三维的。这些后面跟着一个数字,告诉您如何阅读它,然后是大小(3 个数字),然后是值。例如。变量
/YD/YDPN/D3PNFAC 231 2 3 4 ...
是 3-D,首先读取 "x",然后读取 "z",然后读取 "y",大小为 [2,3,4]。然后按(x1y1z1, x2y1z1, x1y1z2, x2y1z2, x1y1z3, x2y1z3, x1y1z4, x2y1z4, x1y2z1, x2y2z1, ...)
. 的顺序读取值
Mu 建议重构该文件的构建方式。这样您就可以使用 XML 结构并使所有代码 更易读 。
看看:XDocument (System.Xml.Linq
)
这是您可以执行的操作的示例:
<Root>
<Controls>
<Control/>
...
</Controls>
<Properties>
<Property/>
...
</Properties>
</Root>
但是如果您不能更改您应该使用的文件: File.ReadLines
像这样:
foreach (var line in File.ReadLines("FilePath"))
{
//Do your work with the [line]
}
要获得 variables
,请查看 string class。更具体地看一下 Split
、Replace
和 Trim
方法。
我认为 C# 中的任何东西都不会神奇地为你做这件事,但为了避免我想象你在 C 中使用的意大利面条代码,我会将其分解成一些 classes每个负责给定的变量类型。抱歉,这有点伪代码,但我希望它能有所帮助。
首先是一个碱基class:
public abstract class DataValue {
public string Category { get; protected set; } // value after /YD
public string Name { get; protected set; } // value after /YD/XXX/
public int DimensionCount { get; protected set; } // number of dimensions
public int[] Dimensions { get; protected set; } // The size of each dimension
protected void ParseHeader(string header) {
// Split of the header into the Category/Name and set it
}
public virtual void ReadValue(string header, StreamReader reader) {
ParseHeader(header);
}
}
然后 class 为每个变量类型:
public class ScalarValue: DataValue {
public ScalarValue() {
DimensionCount = 1;
Dimensions = new [] { 1 };
}
public override void ReadValues(string header, StreamReader reader) {
base.ReadValues(header, reader);
// Do custom logic for reading a scalar here.
}
}
您可以为每种主要类型制作一个 class。他们各自负责自己的阅读逻辑。您需要一个主要的解析器循环来查找变量、创建对象并分派它们:
public static List<DataValue> ParseDataFile(string filePath) {
var reader = new StreamReader(filePath);
var values = new List<DataValue>();
var variableHeader = FindNextVariable(reader);
while(!string.IsNullOrEmpty(variableHeader)) {
DataValue v;
switch(FindVariableType(variableHeader)) {
case Scalar:
v = new ScalarValue();
case OneDimensionalArray:
v = new Array1DValue();
// etc
}
v.ReadValue(variableHeader, reader);
values.Add(v);
variableHeader = FindNextVariable(reader);
}
return values;
}
FindNextVariable 是:
public static string FindNextVariable(StreamReader reader) {
var i = reader.Read();
bool commentBlock = false;
string variable = "";
while (i >= 0) {
var c = (char)i;
// if it's white space, toss it
// if it's a / peek to see if the next is a * starting a comment block
// if in a comment block, read until you get */ consecutively
// if it's a / and not a * next, begin appending to variable until you hit white space again.
}
return variable;
}
FindVariableType 是:
public enum VariableType {
Scalar,
OneDimensionalValue,
TwoDimensionalValue,
ThreeDimensionalValue,
Other
}
public static VariableType FindVariableType(string header) {
var fields = string.Split('/', header);
// take the third item, parse it to determine type
}
实际实施的方法有很多,但这是一般的想法。
好的,这是我的解决方案。我在代码中包含了一些注释,因此它或多或少是不言自明的。
总而言之,我定义了一个class(一个class用于所有变量,即使它们是多维的!),然后我将所有变量存储在字典中。我认为这很简单。
class 存储我的变量:
public class Variable
{
// Properties.
public string Database { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public int Dimensions { get; set; }
public int Order { get; set; }
public int[] Size { get; set; }
public double[] Value { get; set; }
// Default constructor.
public Variable()
{
}
// Constructor.
public Y_Variable(string db, string nam, string typ, int dim, int ord,
int[] siz, double[] val)
{
this.Database = db;
this.Name = nam;
this.Type = typ;
this.Dimensions = dim;
this.Order = ord;
this.Size = siz;
this.Value = val;
}
}
读取文件并将变量存入字典的代码:
public class Y_File2Dictionary
{
public static Dictionary<string, Y_Variable> File2Dictionary(string fileName)
{
// Define delimiters and block comments.
char[] delimiterChars = { ' ', '\t', '\n', '\r' };
var blockComments = @"/\*(.*?)\*/";
// Read the file, remove comments, and store in array with no spaces.
string[] arrayAllVars = Regex
.Replace(File.ReadAllText(fileName), blockComments, "",
RegexOptions.Singleline)
.Split(delimiterChars, StringSplitOptions.RemoveEmptyEntries);
// Initialize dictionary.
Dictionary<string, Y_Variable> dictAllVars = new
Dictionary<string, Y_Variable>();
// Loop to read variables and store them in dictionary.
int i = 0;
while (i < arrayAllVars.Length)
{
// Read only variables.
if (!char.Equals(arrayAllVars[i][0], '/'))
{
i++;
continue;
}
// Read variable name and separate into array
// e.g. /YD/YDC/DCSTEC => {"", YD, YDC, DCSTEC}.
string[] arrayVarName = Regex.Split(arrayAllVars[i++], "/");
// Identify variable.
string varDb = arrayVarName[2];
string varName = arrayVarName[3];
string varTyp = (varName[0] == 'D') ? "double" : "integer";
int varDim = ((int)Char.GetNumericValue(varName[1]) > 0) ?
((int)Char.GetNumericValue(varName[1])) : 0;
// Initiallization of variables to store order and size.
int varOrder = 0;
int[] varSize = new int[3] { 1, 1, 1 };
// Update order and size, depending on the number of dimensions.
switch (varDim)
{
case 1:
varOrder = 1;
Int32.TryParse(arrayAllVars[i++], out varSize[0]);
varSize[1] = 1;
varSize[2] = 1;
break;
case 2:
Int32.TryParse(arrayAllVars[i++], out varOrder);
Int32.TryParse(arrayAllVars[i++], out varSize[0]);
Int32.TryParse(arrayAllVars[i++], out varSize[1]);
varSize[2] = 1;
break;
case 3:
Int32.TryParse(arrayAllVars[i++], out varOrder);
Int32.TryParse(arrayAllVars[i++], out varSize[0]);
Int32.TryParse(arrayAllVars[i++], out varSize[1]);
Int32.TryParse(arrayAllVars[i++], out varSize[2]);
break;
default:
varOrder = 0;
varSize[0] = 1;
varSize[1] = 1;
varSize[2] = 1;
break;
}
// Determine total size of variable, get values as strings.
var varTotalSize = varSize[0] * varSize[1] * varSize[2];
string[] varValStr = new string[varTotalSize];
Array.Copy(arrayAllVars, i, varValStr, 0, varTotalSize);
// Convert values from string to double.
double[] varValDbl = new double[varTotalSize];
varValDbl = Array.ConvertAll(varValStr, Double.Parse);
// Add variable to dictionary.
if (dictAllVars.ContainsKey(varDb + "_" + varName))
dictAllVars.Remove(varDb + "_" + varName);
dictAllVars.Add(varDb + "_" + varName, new Y_Variable(varDb, varName,
varTyp, varDim, varOrder, varSize, varValDbl));
i += varTotalSize;
}
return dictAllVars;
}
}