Reading ini file in C# - c#

I am trying to read an ini file that has the following format:
SETTING=VALUE
SETTING2=VALUE2
I currently have the following code:
string cache = sr.ReadToEnd();
string[] splitCache = cache.Split(new string[] {"\n", "\r\n"}, StringSplitOptions.RemoveEmptyEntries);
Which gives me a list of settings, however, what I would like to do is read this into a dictionary. My question is, is there a way to do this without iterating through the entire array and manually populating the dictionary?

Well, you could use LINQ and do
Dictionary<string, string> ini = (from entry in splitCache
let key = entry.Substring(0, entry.FirstIndexOf("="))
let value = entry.Substring(entry.FirstIndexOf("="))
select new { key, value }).ToDictionary(e => e.key, e => e.value);
As Binary Worrier points out in the comments, this way of doing this has no advantages over the simple loop suggested by the other answers.
Edit: A shorter version of the block above would be
Dictionary<string, string> ini = splitCache.ToDictionary(
entry => entry.Substring(0, entry.FirstIndexOf("="),
entry => entry.Substring(entry.FirstIndexOf("="));

What is wrong with iterating?
var lines = File.ReadAllLines("pathtoyourfile.ini");
var dict = new Dictionary<string, string>();
foreach(var s in lines)
{
var split = s.Split("=");
dict.Add(split[0], split[1]);
}

There's actually a Windows API for reading/writing INI files in kernel32.dll; see this CodeProject article for an example.

INI files are a bit tricky so I wouldn't recommend rolling your own. I wrote Nini which is a configuration library that includes a very fast parser.
Sample INI file:
; This is a comment
[My Section]
key 1 = value 1
key 2 = value 2
[Pets]
dog = rover
cat = muffy
Same C# code:
// Load the file
IniDocument doc = new IniDocument ("test.ini");
// Print the data from the keys
Console.WriteLine ("Key 1: " + doc.Get ("My Section", "key 1"));
Console.WriteLine ("Key 2: " + doc.Get ("Pets", "dog"));
// Create a new section
doc.SetSection ("Movies");
// Set new values in the section
doc.SetKey ("Movies", "horror", "Scream");
doc.SetKey ("Movies", "comedy", "Dumb and Dumber");
// Remove a section or values from a section
doc.RemoveSection ("My Section");
doc.RemoveKey ("Pets", "dog");
// Save the changes
doc.Save("test.ini");

Try like this
[DllImport("kernel32.dll", EntryPoint = "GetPrivateProfileString")]
public static extern int GetPrivateProfileString(string SectionName, string KeyName, string Default, StringBuilder Return_StringBuilder_Name, int Size, string FileName);
and call the function like this
GetPrivateProfileString(Section_Name, "SETTING", "0", StringBuilder_Name, 10, "filename.ini");
Value can be accessed from StringBuilder_Name.

Why not read the file as separate lines, then loop over them splitting on the first =?
var dict = new Dictionary<string,string>();
foreach (var line in File.ReadAllLines(filename)) {
var parts = line.Split('=', 2); // Maximum of 2 parts, so '=' in value ignored.
dict.Add(parts[0], parts[1]);
}
(In .NET 4 replace ReadAllLines with ReadLines, to avoid creating the array, ReadLines returns IEnumerable<String> and reads the file lazily.)

Related

Read and Write INI file without sections

There is an ini file format
########## Order section ##########
KeyOne=Value1
....
....
....
...
KeyMore=999
Key = 88888*
......
......
......
how can I edit what is behind the = ?
I tried in many ways, but since there are no sections in the ini file, I can’t figure out how to change the values Value1 and 999 and 88888*?
Tried splitting strings with split "=" , but failed to change values. and also add new lines to the file? after Key = 88888*
1.If there are spaces or text in the textbox, then it does not work.
const string filepath = #"C:\Users\123\Desktop\Config.ini";
string text = File.ReadAllText(filepath);
const string PATTERN = #"KeyOne=(?<Number>[\d\.]+)";
Match match = Regex.Match(text, PATTERN, RegexOptions.IgnoreCase);
if (match.Success)
{
int index = match.Groups["Number"].Index;
int length = match.Groups["Number"].Length;
text = text.Remove(index, length);
text = text.Insert(index, Program.form1.textBox11.Text);
File.WriteAllText(filepath, text);
}
An INI file has a well defined structure composed by one or many [Sections] and zero or many pairs of [Key]=[Value] under each section. A file without at least a section is not a proper INI file and the standard windows API GetPrivateProfileString and WritePrivateProfileString are not able to read a file with that format.
However, your file is a lot simpler than a standard INI file. It is just composed by pairs of [Key]=[Value] so it is a naturally perfect fit for a Dictionary<string, string>
Reading it coud be simple as
Dictionary<string, string> GetConfigData(string fileName)
{
Dictionary<string, string> data = new Dictionary<string, string>();
foreach (string line in File.ReadLines(fileName))
{
var lineData = line.Split('=');
data.Add(lineData[0], lineData[1]);
}
return data;
}
and writing back that dictionary is even simpler
void WriteConfigData(string fileName, Dictionary<string, string> data)
{
File.WriteAllLines(fileName, data.Select(z => $"{z.Key}={z.Value}{Environment.NewLine}"));
}
Now if you want to change some value you could have
var data = GetConfigData("yourbadinifile.ini");
data["KeyValue"] = "a new value";
WriteConfigData("yourbadinifile.ini", data);
Warning: In the examples above there is no error checking. A robust solution should check if the file passed to the read method exists and if the split operation produces an array of two elements. Consider also that checking for binary files is also an unhandled problem and with complex solutions. So we could also consider to let exceptions bubble up to the client caller.

How to not include data in a text file if it is already in there

I want to add data into a text file based on a specific output, it will read an XML file and write a certain line to a text file. If the data is already written into the text file, i dont want to write it again.
Code:
public void output(string folder)
{
string S = "Data" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xml";
//Trades.Save(S);
string path = Path.Combine(folder, S);
Console.WriteLine(path);
XDocument f = new XDocument(Trades);
f.Save(path);
string[] lines = File.ReadAllLines(path);
File.WriteAllLines(path, lines);
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"H:\Test" + DateTime.Now.ToString("yyMMdd") + ".txt", true))
{
foreach (string line in lines)
{
if (line.Contains("CertainData"))
{
file.WriteLine(line);
if (File.ReadAllLines(path).Any(x => x.Equals(line)))
{
}
else
{
string[] tradeRefLines = File.ReadAllLines(path);
File.WriteAllLines(path, tradeRefLines); ;
}
}
}
}
}
The problem is it will still write the line even if the data is exactly the same elsewhere. I don't want duplicate lines
Any advice?
CLARIFICATION UPDATE
The "CertainData" is a reference number
I have a bunch of files that has data in it and the piece i want to seperate and put into a text file is "CertainData" field, which will have a reference number
Sometimes the files i get sent will have the same formatted information inside it with the "CertainData" appearing in their for reference
When i run this programme, if the text file i have already contains the "CertainData" reference number inside it, i dont want it to be written
If you need anymore clarification let me know and i will update the post
I think you want this: read all lines, filter out those containing a keyword and write it to a new file.
var lines = File.ReadAllLines(path).ToList();
var filteredLines = lines.Where(!line.Contains("CertainData"));
File.WriteAllLines(path, filteredLines);
If you also want to remove duplicate lines, you can add a distinct like this:
filteredLines = filteredLines.Distinct();
Why you don't use Distinct before for loop. This will filter your lines before write in file.
Try something like this
string[] lines = new string[] { "a", "b", "c", "a" };
string[] filterLines = lines.Distinct().ToArray<string>();

Dictionary re-adding words as new keys instead of increasing value

I am writing a program that finds every unique word in a text and prints it in a text box. I do this by printing each key in a dictionary however my dictionary is adding each word as a separate key instead of ignoring words that are already there.
The function is being called correctly and it does work it simpy prints the entire text I hand it however.
EDIT: I am reading the string from a text file then sending it to the function.
This is the input string and the output:
Output:
To be or not to that is the question Whether tis nobler in mind suffer
The slings and arrows of outrageous fortune Or take arms against a sea
troubles And by opposing end them die sleep No more sleep say we end
The heartache thousand natural shocks That flesh heir Tis consummation
public string FindUniqueWords(string text)
{
Dictionary<string, int> dictionary = new Dictionary<string, int>();
string uniqueWord = "";
text = text.Replace(",", ""); //Just cleaning up a bit
text = text.Replace(".", ""); //Just cleaning up a bit
string[] arr = text.Split(' '); //Create an array of words
foreach (string word in arr) //let's loop over the words
{
if (dictionary.ContainsKey(word)) //if it's in the dictionary
dictionary[word] = dictionary[word] + 1; //Increment the count
else
dictionary[word] = 1; //put it in the dictionary with a count 1
}
foreach (KeyValuePair<string, int> pair in dictionary) //loop through the dictionary
{
uniqueWord += (pair.Key + " ");
}
uniqueWords.Text = uniqueWord;
return ("");
}
You're reading the text with System.IO.File.ReadAllText, so text may also contain newline characters.
Replace arr = text.Split(' ') by arr = text.Split(' ', '\r', '\n') or add another replace: text = text.Replace(Environment.NewLine, " ");
Of course, by looking at arr in the debugger, you could have found out by yourself.
A shorter way: (Dont forget to use Using System.Linq)
string strInput = "TEST TEST Text 123";
var words = strInput.Split().Distinct();
foreach (var word in words )
{
Console.WriteLine(word);
}
Your code works as it's supposed to (ignoring case though). The problem almost certainly lies with showing the results in your application, or with how you are calling the FindUniqueWords method (not the complete text at once).
Also, pretty important to note here: a Dictionary<TKey, TValue> by default simply cannot contain a single key multiple times. It would defeat the whole purpose of the dictionary in the first place. It's only possible if you override the Equality comparison somewhere, which you aren't doing.
If I try your code, with the following input:
To be or not to that is is is is is is is the question
The output becomes :
To be or not to that is the question
It works like it's supposed to.

Searching strings in txt file

I have a .txt file with a list of 174 different strings. Each string has an unique identifier.
For example:
123|this data is variable|
456|this data is variable|
789|so is this|
etc..
I wish to write a programe in C# that will read the .txt file and display only one of the 174 strings if I specify the ID of the string I want. This is because in the file I have all the data is variable so only the ID can be used to pull the string. So instead of ending up with the example about I get just one line.
eg just
123|this data is variable|
I seem to be able to write a programe that will pull just the ID from the .txt file and not the entire string or a program that mearly reads the whole file and displays it. But am yet to wirte on that does exactly what I need. HELP!
Well the actual string i get out from the txt file has no '|' they were just in the example. An example of the real string would be: 0111111(0010101) where the data in the brackets is variable. The brackets dont exsist in the real string either.
namespace String_reader
{
class Program
{
static void Main(string[] args)
{
String filepath = #"C:\my file name here";
string line;
if(File.Exists(filepath))
{
StreamReader file = null;
try
{
file = new StreamReader(filepath);
while ((line = file.ReadLine()) !=null)
{
string regMatch = "ID number here"; //this is where it all falls apart.
Regex.IsMatch (line, regMatch);
Console.WriteLine (line);// When program is run it just displays the whole .txt file
}
}
}
finally{
if (file !=null)
file.Close();
}
}
Console.ReadLine();
}
}
}
Use a Regex. Something along the lines of Regex.Match("|"+inputString+"|",#"\|[ ]*\d+\|(.+?)\|").Groups[1].Value
Oh, I almost forgot; you'll need to substitute the d+ for the actual index you want. Right now, that'll just get you the first one.
The "|" before and after the input string makes sure both the index and the value are enclosed in a | for all elements, including the first and last. There's ways of doing a Regex without it, but IMHO they just make your regex more complicated, and less readable.
Assuming you have path and id.
Console.WriteLine(File.ReadAllLines(path).Where(l => l.StartsWith(id + "|")).FirstOrDefault());
Use ReadLines to get a string array of lines then string split on the |
You could use Regex.Split method
FileInfo info = new FileInfo("filename.txt");
String[] lines = info.OpenText().ReadToEnd().Split(' ');
foreach(String line in lines)
{
int id = Convert.ToInt32(line.Split('|')[0]);
string text = Convert.ToInt32(line.Split('|')[1]);
}
Read the data into a string
Split the string on "|"
Read the items 2 by 2: key:value,key:value,...
Add them to a dictionary
Now you can easily find your string with dictionary[key].
first load the hole file to a string.
then try this:
string s = "123|this data is variable| 456|this data is also variable| 789|so is this|";
int index = s.IndexOf("123", 0);
string temp = s.Substring(index,s.Length-index);
string[] splitStr = temp.Split('|');
Console.WriteLine(splitStr[1]);
hope this is what you are looking for.
private static IEnumerable<string> ReadLines(string fspec)
{
using (var reader = new StreamReader(new FileStream(fspec, FileMode.Open, FileAccess.Read, FileShare.Read)))
{
while (!reader.EndOfStream)
yield return reader.ReadLine();
}
}
var dict = ReadLines("input.txt")
.Select(s =>
{
var split = s.Split("|".ToArray(), 2);
return new {Id = Int32.Parse(split[0]), Text = split[1]};
})
.ToDictionary(kv => kv.Id, kv => kv.Text);
Please note that with .NET 4.0 you don't need the ReadLines function, because there is ReadLines
You can now work with that as any dictionary:
Console.WriteLine(dict[12]);
Console.WriteLine(dict[999]);
No error handling here, please add your own
You can use Split method to divide the entire text into parts sepparated by '|'. Then all even elements will correspond to numbers odd elements - to strings.
StreamReader sr = new StreamReader(filename);
string text = sr.ReadToEnd();
string[] data = text.Split('|');
Then convert certain data elements to numbers and strings, i.e. int[] IDs and string[] Strs. Find the index of the given ID with idx = Array.FindIndex(IDs, ID.Equals) and the corresponding string will be Strs[idx]
List <int> IDs;
List <string> Strs;
for (int i = 0; i < data.Length - 1; i += 2)
{
IDs.Add(int.Parse(data[i]));
Strs.Add(data[i + 1]);
}
idx = Array.FindIndex(IDs, ID.Equals); // we get ID from input
answer = Strs[idx];

C#: Read data from txt file

I have an .EDF (text) file. The file's contents are as follows:
ConfigFile.Sample, Software v0.32, CP Version 0.32
[123_Float][2]
[127_Number][0]
[039_Code][70]
I wnat to read these items and parse them like this:
123_Float - 2
127_Number - 0
039_Code - 70
How can I do this using C#?
Well, you might start with the File.ReadAllLines() method. Then, iterate through the lines in that file, checking to see if they match a pattern. If they do, extract the necessary text and do whatever you want with it.
Here's an example that assumes you want lines in the format [(field 1)][(field 2)]:
// Or wherever your file is located
string path = #"C:\MyFile.edf";
// Pattern to check each line
Regex pattern = new Regex(#"\[([^\]]*?)\]");
// Read in lines
string[] lines = File.ReadAllLines(path);
// Iterate through lines
foreach (string line in lines)
{
// Check if line matches your format here
var matches = pattern.Matches(line);
if (matches.Count == 2)
{
string value1 = matches[0].Groups[1].Value;
string value2 = matches[1].Groups[1].Value;
Console.WriteLine(string.Format("{0} - {1}", value1, value2));
}
}
This outputs them to the console window, but you could obviously do whatever you want with value1 and value2 (write them to another file, store them in a data structure, etc).
Also, please note that regular expressions are not my strong point -- there's probably a much more elegant way to check if a line matches your pattern :)
If you want more info, check out MSDN's article on reading data from a text file as a starting point.
Let us assume your file really is as simple as you describe it. Then you could drop the first line and parse the data lines like this:
foreach (string line in File.ReadAllLines(#"C:\MyFile.edf").Skip(1))
{
var parts = line.Split("][");
var value1 = parts[0].Replace("[", "");
var value2 = parts[1].Replace("]", "");
Console.WriteLine(string.Format("{0} - {1}", value1, value2));
}
Another variation.
var lines = File.ReadAllLines(file)
.Skip(1)
.Select(x => x.Split(new[] { '[', ']' },
StringSplitOptions.RemoveEmptyEntries));
foreach(var pair in lines)
{
Console.WriteLine(pair.First()+" - "+pair.Last());
}

Categories

Resources