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,但是我的文本文件的格式对它来说太复杂了……除非那里的一些大师知道一些可以帮助我的技巧。

欢迎您提出建议。示例代码将不胜感激。

下面定义了文本文件的格式:

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。更具体地看一下 SplitReplaceTrim 方法。

我认为 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;
        }
    }