C# — работа с ini файлами.

2011-03-19 papirosnik C#

 

Наслаждаясь программированием на языке C#  и исповедуя традиционные подходы в решении повседневных задач, столкнулся в своей практике только с двумя моментам, которые немного напрягли: отсутсвие полноценной поддержки старых добрых ini-файлов и неожиданно неочевидный способ для кооперирования консольного приложения и системного трея (об этом здесь: ).

Что же касается первого пункта, то тут объясение таково: ini-файлы — это пережиток прошлых времён, порождение доисторических 16-ти разрядых версий Windows и даже DOS (про другие ОС мелкософт скромно «забывает»). Теперь мол 21-й век на дворе, используйте cериализацию в XML и даже Маршализацию. Ну на худой конец Билли предлагает нам задействовать реестр Windows. Опять же скромно забывая при этом упомянуть, что если писать «каждый чих» в реестр, то он скоро раздуется до неимоверных размеров, чистить его можно только стороними средствами и в итоге после этой операции (да и без неё со временем) вы вынуждены переустанавливать всю систему заново на чистый «винт». Как ослушаться корпорацию и по-прежнему использовать Ini-файлы без стыда и совести в .NET приложениях — решений немало.

Все они в основном предлагают или импортировать функции для работы с такими файлами из kernel32.dll или читать ini файл как текст, конвертировать его в XML штатными средствами .NET, работать как с XML, а при записи — снова в текст. Я не шучу — это третий «официальный» вариант решения проблемы от мягкотелых, на случай, если Вам уж совсем невмоготу без ini-файлов в C#.

Справедливости ради надо отметить, что и без платформы .NET Microsoft™ особо не жаловала ini файлы. Во всей хрени, которую они предлагают нам использовать (MFC, ATL, WTL) не уделяется должного внимания проблеме. Здесь http://papirosnik.info/2010/06/ini-fajl-na-osnove-stdmap/ я уже предлагал своё решение для С++. Опять же ради той пресловутой справедливости отмечу, что это решение довольно примитивное, не учитывает комментарии, дефолтные секции и вообще — может быть реализовано при помощи шаблонов более элегантно и для любых типов. Так — образчик кода, призванный показать саму альтернативную идею «тормознутым» API-шным функциям.

Теперь настала очередь C#. Здесь будем исповедовать такой же подход. При открытии — читаем весь файл в память. Работаем с секциями и значениями как угодно: удаляем, добавляем, изменяем, клонируем,  копируем и т.п. Всё это происходит очень быстро, ибо в памяти и кэшировано, хэшировано и ещё как-то -ировано. При желании — сбрасываем изменения в тот же файл, сохраняем в новый файл, экспортируем в XML или в реестр, отправляем по Интернету, засылаем на околоземную орбиту или на плазму на стене, на Луну или во внешний космос (через аську)… Тут возможности C# безграничны.

Вот блин — пока писал преамбулу — уже устал… Так что допишу как-нибудь потом.

Короче, охладел окончательно к дописанию этой статьи.
Может как-нибудь потом. А пока вот code snippet для размышления. Этот отрывок первого этапа нашей эволюции в продвижении к фамильярности с ini-файлами. На последнем этапе используются сохранение, экспорт, IEnnumerable итераторы, generic’и и LINQ. Вдруг что-нибудь меня подвигнет на дописание статьи, то выложу, покажу и расскажу. Пока же пример для начальных шагов на этом пути, без комментариев:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
 
namespace Papirosnik.Utils
{
	class IniFile
	{
		// ----------------------- CTORS ----------------------------
		public IniFile()
		{
			FileName = "";
			CurrentSection = new Section("");
			mSections.Add(CurrentSection);
		}
		// ----------------------------------------------------------
		public IniFile(string filename): this()
		{
			ReadFromFile(filename);
		}
 
		// ----------------------- PUBLIC ---------------------------
 
		public string GetStr(string keyName, string defValue = "", string sectName = "")
		{
			Section sect = findSection(sectName);
			return sect != null ? sect[keyName].asString : defValue;
		}
 
		// ----------------------------------------------------------------
		public bool GetBool(string keyName, bool defValue = false, string sectName = "")
		{
			Section sect = findSection(sectName);
			return sect != null ? sect[keyName].asBool : defValue;
		}
 
 
		// ----------------------------------------------------------------
		public int GetInt(string keyName, int defValue = 0, string sectName = "")
		{
			Section sect = findSection(sectName);
			return sect != null ? sect[keyName].asInt : defValue;
		}
 
		// ----------------------------------------------------------------
		public bool Read()
		{
			if (FileName == null) return false;
			string line;
			System.IO.StreamReader reader = null;
			try
			{
				reader = new System.IO.StreamReader(FileName);
				while (!reader.EndOfStream)
				{
					line = reader.ReadLine();
					if (line == null) continue;
					if (line.Length > 2 && line[0] =='[' && line[line.Length-1]==']')
					{
						CurrentSection = new Section(line.Trim(new char[] {'[',']'}));
						mSections.Add(CurrentSection);
						continue;
					}
 
					string[] pair = line.Split(new char[]{'='}, 2, StringSplitOptions.RemoveEmptyEntries);
					if (pair.Length == 2)
					{
						CurrentSection.Add(pair[0].Trim(), pair[1].Trim());
					}
				}
			}
			catch (System.IO.FileNotFoundException)
			{
			}
			finally
			{
				if (reader != null) reader.Close();
			}
 
			return true;
		}
 
		// ----------------------------------------------------------
		public bool ReadFromFile(string fileName)
		{
			FileName = fileName;
			return Read();
		}
 
		// ----------------------------------------------------------
		Section findSection(string name)
		{
			return mSections.Find(delegate(Section sect) { return sect.Name == name; });
		}
 
 
		// ----------------------- members --------------------------
		public string FileName { get; internal set; }
		public Section CurrentSection { get; internal set; }
		public ReadOnlyCollection<Section> Channels { get { return mSections.AsReadOnly(); } }
		private List<Section> mSections = new List<Section>();
 
 
 
 
		// -------------------- internal classes --------------------
		public class Section
		{
			public Section(string name)
			{
				Name = name;
			}
 
			public string Name { get; internal set; }
			public bool IsEmpty { get { return mRecords.Count > 0; } }
 
			public Value this[int index]
			{
				get { return mRecords[index].mValue; }
				set { mRecords[index].mValue = value; }
			}
 
			public Value this[string keyName]
			{
				get
				{
					Record rec = findByKey(keyName);
					return rec != null ? rec.mValue : new Value();
				}
				set
				{
					Record rec = findByKey(keyName);
					if (rec != null) rec.mValue = value;
					else Add(keyName, value.asString);
				}
			}
 
			// -----------------------------------------------------------------------------------------
			public void Add(string keyName, string value) { mRecords.Add(new Record(keyName,value)); }
 
			// ===============- Private section =======================
			private List<Record> mRecords = new List<Record>();
 
			// -------------------------------
			Record findByKey(string keyName)
			{
				return mRecords.Find(delegate(Record rec) { return rec.mKeyName == keyName; });
			}
			// -------------------------------
			private class Record
			{
				internal Record(string keyName, string value)
				{
					mKeyName = keyName;
					mValue = new Value(value);
				}
				public string mKeyName { get; internal set; }
				public Value  mValue { get; internal set; }
			}
 
			//-----------------------------------------------------------
			public class Value
			{
				public Value() { mValue = ""; }
				public Value(string value) { mValue = value; }
				public Value(bool value) { asBool = value; }
				public Value(char value) { asChar = value; }
				public Value(byte value) { asByte = value; }
				public Value(int value) { asInt = value; }
				public Value(uint value) { asUint = value; }
				public Value(float value) { asFloat = value; }
				public Value(double value) { asDouble = value; }
				public Value(decimal value) { asDecimal = value; }
				public Value(DateTime value) { asDateTime = value; }
 
				// --------------------------------------------
				public bool asBool
				{
					get { return Convert.ToBoolean(mValue); }
					set { mValue = value.ToString(); }
				}
				// --------------------------------------------
				public char asChar
				{
					get { return Convert.ToChar(mValue); }
					set { mValue = value.ToString(); }
				}
				// --------------------------------------------
				public float asByte
				{
					get { return Convert.ToByte(mValue); }
					set { mValue = value.ToString(); }
				}
				// --------------------------------------------
				public int asInt
				{
					get { return Convert.ToInt32(mValue); }
					set { mValue = value.ToString(); }
				}
				// --------------------------------------------
				public uint asUint
				{
					get { return Convert.ToUInt32(mValue); }
					set { mValue = value.ToString(); }
				}
 
				// --------------------------------------------
				public float asFloat
				{
					get { return Convert.ToSingle(mValue); }
					set { mValue = value.ToString(); }
				}
 
				// --------------------------------------------
				public double asDouble
				{
					get { return Convert.ToDouble(mValue); }
					set { mValue = value.ToString(); }
				}
 
				// --------------------------------------------
				public decimal asDecimal
				{
					get { return Convert.ToDecimal(mValue); }
					set { mValue = value.ToString(); }
				}
 
				// --------------------------------------------
				public DateTime asDateTime
				{
					get { return Convert.ToDateTime(mValue); }
					set { mValue = value.ToString(); }
				}
 
				// --------------------------------------------
				public string asString
				{
					get { return mValue; }
					set { mValue = value; }
				}
				// --------------------------------------------
				private string mValue;
			} // class Value
		} // class Section
	} // class IniFile
} // namespace

.NET, ini-файл,


Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Powered by WordPress. Designed by elogi.