How to implement the complete INI file reading and writing class

Keywords: C# encoding Windows Attribute less

Author: Magic fudge

Date: February 27, 2020

Introduction

*************************************

The. ini file is an abbreviation of the Initialization File, which is the configuration file. yes windows System configuration file The storage format used.

It is easy to use, and has similar functions with the key value of the registry. Various applications often use INI to save various configurations and options.

In the case of simple only need to read, call the WINDOWS API, but in the case of complex need for visual editing, you need to establish your own processing class.

 

 

 

How to implement your INI class

First of all, we need to understand

◇ format of INI

 

 

◇ typical INI documents

; project notes
[.ShellClassInfo]
InfoTip = folder with Icon
; icon resources
IconResource="C:\Windows\system32\SHELL32.dll",4
#Folder view
[ViewState]
Mode=
Vid=
FolderType=General
#Tail note

A typical INI file consists of sections, comments, and items under sections in the form of key = value.

There are two kinds of annotation symbols in INI file, the specification is semicolon, and in some places, the semicolon is used.

◇ keep notes

In order not to lose any information when modifying the INI file, you need to save all the valid elements, including comments and even invalid lines in the INI file.

To achieve this, all comments and invalid lines are attributed to the valid elements after it.

Take desktop.ini above for example,

  • First line; item comment belongs to Section [. ShellClassInfo]
  • The fourth line; icon resource belongs to IconResource = item
  • #Folder views belong to the [ViewState] section
  • The last ා Trailer comment belongs to EndText of the entire INI document

◇ INIItem

Represents the item under the node in the INI file, which has three properties: Name, Value, and Comment

 1     /// <summary>
 2     /// Have name, value, and comment
 3     /// </summary>
 4     public class INIItem {
 5         /// <summary>
 6         /// instantiation INIItem. Specify the name, value, and comment.
 7         /// </summary>
 8         /// <param name="vName"></param>
 9         /// <param name="vValue"></param>
10         /// <param name="vComment"></param>
11         public INIItem(string vName, string vValue, string vComment = "") {
12             Name = vName;
13             Value = vValue;
14             Comment = vComment;
15         }
16         /// <summary>
17         /// Item name. for example Color = 202,104,0 Medium Color
18         /// </summary>
19         public string Name { get; set; }
20         /// <summary>
21         /// Value content. for example Color = 202,104,0 202 of,104,0
22         /// </summary>
23         public string Value { get; set; }
24         /// <summary>
25         /// All comment lines at the front. Generally speaking ; Start
26         /// </summary>
27         public string Comment { get; set; }
28         /// <summary>
29         /// Return INIItem Text form of.〈<see cref="string"/>30         /// <para>Name=Value</para>
31         /// </summary>
32         /// <returns>〈string〉Return INIItem Text form of.</returns>
33         public override string ToString() {
34             return Name + INI.U Equal sign + Value;
35         }        
36     }

◇ ININode class

Represents a node in the INI file that has a list of items list {of iItem}, Name name, and Comment comment.

 1     /// <summary>
 2     /// Express INI A node of a file that has a list of items, as well as names and comments
 3     /// <para></para>
 4     /// </summary>
 5     public class ININode {
 6         /// <summary>
 7         /// instantiation ININode. Specifies the initial name and comment.
 8         /// </summary>
 9         /// <param name="vName"></param>
10         /// <param name="vComment"></param>
11         public ININode(string vName, string vComment) { Name = vName; Comment = vComment; Items = new List<INIItem>(); }
12         /// <summary>
13         /// Node name. for example [Config]
14         /// </summary>
15         public string Name { get; set; }
16         /// <summary>
17         /// All comment lines at the front. Generally speaking ; Start
18         /// </summary>
19         public string Comment { get; set; }
20         /// <summary>
21         /// List of items contained
22         /// </summary>
23         public List<INIItem> Items { get; set; }
24         /// <summary>
25         /// Add a new item to this node.
26         /// </summary>
27         /// <param name="vName"></param>
28         /// <param name="vValue"></param>
29         /// <param name="vComment"></param>
30         /// <returns></returns>
31         public INIItem New(string vName, string vValue, string vComment = "") {
32             var k = new INIItem(vName, vValue, vComment);
33             Items.Add(k);
34             return k;
35         }
36         /// <summary>
37         /// Return ININode Text form of.〈<see cref="string"/>38         /// <para>[Name]</para>
39         /// </summary>
40         /// <returns>〈string〉Return ININode Text form of.</returns>
41         public override string ToString() {
42             return INI.U Left parenthesis + Name + INI.U Right bracket;
43         }
44     }

◇ class INI

It represents all the contents of the entire INI file, with the properties of List{Of ININode}, EndText, FileName, StartLine, etc

 1     /// <summary>
 2     /// Express INI Papers. Has methods to read and write files.
 3     /// <para>Store in <see cref="List{ININode}"/>&lt;<see cref="ININode"/>&gt;</para>
 4     /// </summary>
 5     public class INI {
 6         /// <summary>
 7         /// instantiation INI Papers.
 8         /// </summary>
 9         public INI() { }
10 
11         #region "↓Global constant"
12         /// <summary>Standard symbols for notes</summary>
13         public static string U Notes = ";";
14         /// <summary>Standard symbol for note 2</summary>
15         public static string U Note 2 = "#";
16         /// <summary>Standard symbol for section left parenthesis</summary>
17         public static string U Left parenthesis = "[";
18         /// <summary>Standard symbol for section closing bracket</summary>
19         public static string U Right bracket = "]";
20         /// <summary>Standard symbols for connecting items and values</summary>
21         public static string U Equal sign = "=";
22         /// <summary>Ignore meaningless comment lines when reading or writing(Do not include comments). </summary>
23         public static bool Ignore remarks = false;
24         /// <summary>Number of valid lines of the last file read(Do not include comments). </summary>
25         public static int Number of valid rows last read = 0;
26         #endregion
27 
28         /// <summary>
29         /// All nodes
30         /// <para>Each node contains items, values, and comments. When the item name is an empty string, the entire statement is treated as a comment</para>
31         /// </summary>
32         public List<ININode> Nodes { get; set; } = new List<ININode>();
33         /// <summary>
34         /// Attach to INI Meaningless text after document
35         /// </summary>
36         public string EndText { get; set; } = "";
37         /// <summary>
38         /// Attach to INI Text such as author information in the first line of the document
39         /// <para>The newline character will be replaced with two spaces</para>
40         /// </summary>
41         public string StartLine { get; set; } = "";
42         /// <summary>
43         /// read INI Acquired at time FileName. 
44         /// <para>This name can be used or not used when writing documents.</para>
45         /// </summary>
46         public string FileName { get; set; } = "";
47         /// <summary>
48         /// Ben Ben INI File to add a new node.
49         /// </summary>
50         /// <param name="vName"></param>
51         /// <param name="vComment"></param>
52         /// <returns></returns>
53         public ININode New(string vName, string vComment = "") {
54             var k = new ININode(vName, vComment);
55             Nodes.Add(k);
56             return k;
57         }
58     }

How to write INI file

  1. First, traverse each node, write the node's comment and node name (parentheses)
  2. Then traverse the items below each node, write the comment of the item and the name = value of the item.
  3. Write tail comment

Here is the write code

 1         #region "write file"
 2 
 3         /// <summary>Write document to specified path
 4         /// </summary>
 5         /// <param name="path">Specified path</param>
 6         public bool Write document(string path, Encoding encoding = null) {
 7             try {
 8                 if (encoding == null) { encoding = Encoding.Default; }
 9                 using (StreamWriter SW = new StreamWriter(path)) {
10                     SW.Write(ToString());
11                 }
12             } catch (Exception) {
13                 return false;
14             }       
15             return true;
16         }
17         /// <summary>
18         /// take INI When the document is converted to text format, the entire document is generated.
19         /// <para>Note: large documents can take a lot of time</para>
20         /// </summary>
21         /// <returns></returns>
22         public override string ToString() {
23             StringBuilder sb = new StringBuilder();
24             if (StartLine.Length > 0) { sb.AppendLine(StartLine.Replace("\r\n", "  ")); }
25             for (int i = 0; i < Nodes.Count; i++) {
26                 var node = Nodes[i];
27                 if (Ignore remarks == false) { sb.Append(node.Comment); }
28                 sb.AppendLine(node.ToString());
29                 for (int j = 0; j < node.Items.Count; j++) {
30                     var item = node.Items[j];
31                     if (Ignore remarks == false) { sb.Append(item.Comment); }
32                     sb.AppendLine(item.ToString());
33                 }
34             }
35             if (EndText.Length > 0) { sb.AppendLine(EndText); }         
36             return sb.ToString();
37         }
38 
39         #endregion

 

 

How to read INI file

Reading is usually more complex than writing. The soft candy code is also checked line by line, and it is completed after many debugging.

The process is as follows:

  1. Firstly, some local variables are defined to record the sections, items, accumulated comments and whether they are valid lines of the current analysis.
  2. Read line by line. First, judge whether it starts with or not. If it does, add it to the comment and enter it. Set it as a valid line.
  3. Judge whether the beginning is [, if it is, read it as A section, and further analyze. If it is in the form of [A], set the current section as A valid line. If [B is lack of anti bracket, carry out the next process, and it is still impossible to determine whether it is an item like [B=K] or A pure meaningless invalid line.
  4. Determine whether it contains =, if so, read as an item
  5. If it is not marked as a valid line, a comment will be added.
  6. If the comment is not empty after reading the full text, add it to INI.EndText as the ending comment.

Code

 #region "read file"
        /// <summary>
        ///Reads document content from a file with the specified path and character encoding to cost the document this lifetime.
        /// </summary>
        ///< param name = "path" > full path string < / param >
        ///< param name = "encoding" > encoding format: automatic recognition by default. (may recognize error for no bom) < / param >
        public bool reading document (string path, Encoding encoding = null){
            If (file. Exists (path) = = false) {return false;}
            try {
                If (encoding = = null) {encoding = txt.getfileencodetype (path);}
                Using (StreamReader sR = new StreamReader (path, encoding)){
                    bool return result = read document (New stringreader (sr.readtoend());
                    SR.Close();
                    return returns the result;
                }
            } catch (Exception) {
                return false;
            }
        }

        /// <summary>
        ///Read the document content from < see CREF = "stringreader" / > to cost the document this lifetime.
        /// </summary>  
        ///< param name = "mystringreader" > stringreader, which can be generated by string or StreamReader.ReadToEnd(). </param>
        ///< returns > < bool > returns whether the read is successful. </returns>
        public bool reading document (StringReader MyStringReader){
            ///< summary > section being analyzed < / summary >
            ININode current section = null;
            ///< summary > items being analyzed < / summary >
            INIItem current item = null;
            ///< summary > section name being analyzed < / summary >
            string current section name = null;
            ///< summary > name of item being analyzed < / summary >
            string current item name = null;
            ///< summary > cumulative count of attribute rows read < / summary >
            int count = 0;
            ///< summary > is the row valid or unrecognized. (unrecognized as comment) < / summary >
            bool valid line = false;
            ///< summary > the length of the line without spaces and tabs < / summary >
            int valid text length;
            ///< summary > comments before each entity < / summary >
            string comment = '';
            //* loop through each line*
            while (true) {
                string line text = MyStringReader.ReadLine();
                If (line text = = null) {if (note. Length > 0) {endtext = note;} number of valid lines last read = count; break;} else{
                    string row;

                    Valid line = false;
                    //* get text without spaces and tabs*
                    Line = line text. Trim (',' \ t ');
                    //* gets the length of text without spaces and tabs*
                    Valid text length = line. Length;
                    //* check comment*
                    if (line text. Contains){
                        int comment position = line text. IndexOf(U comment);
                        Line = line text. Substring(0, comment position);
                        int comment start position = comment position + U comment. Length - 1;
                        int comment length = line text.Length - Comment start position;
                        if (comment length > 0){
                            if (note. Length > 0) {note + = "\ r\n";}
                            Note + = line text. Substring (comment start position, comment length);
                        }
                        Valid line = true;
                    }
                    if (line text. Contains (ucomment 2)){
                        int comment position = line text. IndexOf(U comment 2);
                        Line = line text. Substring(0, comment position);
                        int comment start position = comment position + U comment 2.Length - 1;
                        int comment length = line text. Length - Comment start position;
                        if (comment length > 0){
                            if (note. Length > 0) {note + = "\ r\n";}
                            Note + = line text. Substring (comment start position, comment length);
                        }
                        Valid line = true;
                    }
                    //* check start character*
                    if (line. Length > = 2){
                        //[type definition] ===== first character: first character of U section[
                        if (line [0] == U open bracket [0]){
                            int right bracket position = row. IndexOf(U right bracket [0], 2);
                            if (right bracket position > 1){
                                Current section name = row. Substring(1, right bracket position - 1);
                                Current section = new (current section name, remarks);
                                Remarks = "";
                                Count + = 1;
                                Valid line = true;
                            }
                        }
                        //Item definition = = = row with equal sign
                        //- > get assignment symbol position
                        int assignor position = row. IndexOf(U equal sign, 2);
                        if (assignor position > 1){
                            //- > get the name and value, and create a new item
                            Current item name = row. Substring(0, Assignor position). Trim (',' \ t ');
                            string value = line. Substring (assignor position + 1, line. Length - assignor position - 1). Trim (',' \ t ');
                            if (current section! = null){
                                Current item = current section. New (current item name, value, remark);
                                Remarks = "";
                                Count + = 1;
                                Valid line = true;
                            }                                                      
                        }
                    }
                    //* invalid lines treated as comments*
                    if (valid line = = false){
                        if (ignore comment = = false){
                            if (line text. Length == 0) {comment + = "\ r\n";} else {comment + = line text + "\ r\n";}
                        }
                    }
                }                             
            }
            return true;
        }

        #endregion

◇ coding problems

 

 1 /// <summary>
 2         /// The first two bytes of the file header are used to distinguish which encoding a file belongs to.
 3         /// If the file length is less than 2 bytes, return null
 4         /// When FF FE Yes, it is. Unicode;
 5         /// When FE FF Yes, it is. BigEndianUnicode;
 6         /// When EF BB Yes, it is. UTF-8;
 7         /// When it's not for these, it's ANSI Code.
 8         /// </summary>
 9         public static Encoding GetFileEncodeType(string filename) {
10             FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
11             BinaryReader br = new BinaryReader(fs);
12             Byte[] buffer = br.ReadBytes(2);
13             if (buffer.Length < 2) { return null; }
14             if (buffer[0] >= 0xEF) {
15                 if (buffer[0] == 0xEF && buffer[1] == 0xBB) {
16                     return Encoding.UTF8;
17                 } else if (buffer[0] == 0xFE && buffer[1] == 0xFF) {
18                     return Encoding.BigEndianUnicode;
19                 } else if (buffer[0] == 0xFF && buffer[1] == 0xFE) {
20                     return Encoding.Unicode;
21                 } else {
22                     return Encoding.Default;
23                 }
24             } else {
25                 return Encoding.Default;
26             }
27         }

Form read INI demo

◇ demonstration effect

 

◇ INIListView class

Use a helper class to display the INI file content to ListView to show the effect.

Add a Group group to each node, and put the node itself and its items into the Group.

When the mouse selects an item, judge the Key and Group of the item to know which node it belongs to and what its name is.

 1     public class INIListView {
 2         public ListView view;
 3         public Color Section color = Color.FromArgb(0, 153, 153);
 4         public Color Pitch background = Color.FromArgb(255, 255, 255);
 5         public void Bound controls(ListView ListView) {
 6             view = ListView;
 7             Initialization();            
 8         }
 9         public void Load data(INI ini) {
10             Initialization group(ini);
11             Initialization data(ini);
12         }
13 
14         private void Initialization() {
15             view.View = View.Tile;
16             view.ShowGroups = true;
17             Initialization column();
18         }
19 
20         private void Initialization column() {
21             view.Columns.Clear();
22             view.Columns.Add("A", "Name", 220);
23             view.Columns.Add("B", "value", 300);
24             view.Columns.Add("C", "Notes", 440);
25         }
26         private void Initialization group(INI ini) {
27             if (ini == null) { return; }
28             for (int i = 0; i < ini.Nodes.Count; i++) {
29                 string nodeName = ini.Nodes[i].Name;
30                 int cc = ini.Nodes[i].Items.Count;
31                 string nodeTitle = string.Format("{0} ({1})", nodeName, cc);
32                 view.Groups.Add(nodeName, nodeTitle);
33             }
34         }
35 
36         private void Initialization data(INI ini) {
37             view.Items.Clear();
38 
39             if (ini == null) { return; }
40             for (int i = 0; i < ini.Nodes.Count; i++) {
41                 string nodeName = ini.Nodes[i].Name;               
42                 var nodeitem = view.Items.Add(nodeName, "["+nodeName+"]",0);
43                 nodeitem.ForeColor = Section color;
44                 nodeitem.BackColor = Pitch background;
45          
46                 nodeitem.Group = view.Groups[nodeName];
47                
48 
49                 for (int j = 0; j < ini.Nodes[i].Items.Count; j++) {
50                     var iniitem = ini.Nodes[i].Items[j];
51                     string name = iniitem.Name;
52                     string value = iniitem.Value;
53                     string comment = iniitem.Comment;
54                     var item = view.Items.Add(name, name);
55                     item.Group = view.Groups[nodeName];
56                     item.SubItems.Add(value);
57                     item.SubItems.Add(comment);
58                 }
59             }
60         }
61 
62     }

Drag a listview and openfiledialog, and button on the form to read the file

 1 Public partial class edit Form: Form{
 2         INIListView INIListView = new INIListView();
 3. Ini current document;
 4         
 5 
 6 public edit form (){
 7             InitializeComponent();
 8         }
 9 
10 private void editing form ﹐ Load(object sender, EventArgs e){
11             Width = 1280;
12             Height = 720;
13 initialize data view ();
14             openINIFileDialog.InitialDirectory = Environment.CurrentDirectory;
15         }
16 private void initialize data view (){
17 inilistview. Binding control (data view);
18         }
19 
20 private void button read file Click(object sender, EventArgs e){
21             var result = openINIFileDialog.ShowDialog();
22             if (result == DialogResult.OK) {
23 current document = new INI();
24 var read result = current document. Read document (openINIFileDialog.FileName);
25 inilistview. Load data (current document);
26             } 
27 
28 
29         }
30 
31 private void view (object sender, EventArgs E){
32 data view. View = View.Details;            
33         }
34 
35 private void view 2 Click (object sender, EventArgs E){
36 data view. View = View.Tile;
37         }
38 
39 private void view (object sender, EventArgs E){
40 data view. View = View.List;
41         }
42 
43 private void View > Click (object sender, EventArgs E){
44 data view. View = View.SmallIcon;
45         }
46 
47 private void View > Click (object sender, EventArgs E){
48 data view. View = View.LargeIcon;
49         }
50     }

 

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Conclusion: This paper implements the construction, reading and writing of INI file.

In fact, more powerful data formats can be achieved through extension.

Posted by chalexan on Thu, 27 Feb 2020 21:32:58 -0800