How can I read a value from an INI file in C#? - c#

I have an INI file that I want to read the line DeviceName=PHJ01444-MC35 from group [DEVICE] and assign the value to a string.
[BEGIN-DO NOT CHANGE/MOVE THIS LINE]
[ExecList]
Exec4=PilotMobileBackup v1;Ver="1.0";NoUninst=1
Exec3=MC35 108 U 02;Ver="1.0.8";NoUninst=1
Exec2=FixMGa;Ver="1.0";Bck=1
Exec1=Clear Log MC35;Ver="1.0";Bck=1
[Kiosk]
Menu8=\Program Files\PilotMobile\Pilot Mobile Backup.exe
MenuCount=8
AdminPwd=D85F72A85AE65A71BF3178CC378B260E
MenuName8=Pilot Mobile Backup
Menu7=\Windows\SimManager.exe
MenuName7=Sim Manager
UserPwd=AF2163B24AF45971
PasswordPolicy=C34B3DE916AA052DCB2A63D7DCE83F17
DisableBeam=0
DisableBT=0
DisableSDCard=0
EnableAS=1
ActionCount=0
Url=file://\Application\MCPortal.htz
AutoLaunch=0
Menu6=\Windows\solitare.exe
MenuName6=Solitare
Menu5=\Windows\bubblebreaker.exe
MenuName5=Bubble Breaker
Menu4=\Windows\wrlsmgr.exe
MenuName4=Communications
Menu3=\Windows\Calendar.exe
MenuName3=Calendar
Menu2=\Windows\tmail.exe
MenuName2=Text Messaging
Menu1=\Program Files\PilotMobile\Pilot.Mobile.exe
MenuName1=Pilot Mobile
ShowStartMenu=1
CustomTaskBar=0
IdleTimeout=0
NoTaskbar=0
PPCKeys=1111111111111111
On=1
[Status]
MCLastConn=2006/10/01 00:50:56
[Connection]
DeploySvr1=********
[Locations]
Backup=Backup
Install=\Application
[Comm]
RetryDelay=60000
NoInBoundConnect=0
TLS=0
Broadcast=1
[Info]
LID=090128-117
PWDID=081212-10
TimeSyncID={249CEE72-5918-4D18-BEA8-11E8D8D972BF}
TimeSyncErrorInterval=5
TimeSyncInterval=120
AutoTimeSync=1
SecondarySNTPServer=ntp1.uk.uu.net
DefaultSNTPServer=ntp0.uk.uu.net
DepServerTimeSyncType=4
TimeSyncServerType=1
DFID=080717-8
Platform=PPC
Method=39
SiteName=*****
[Device]
SyncTimer=4
Ver=1
DeviceID={040171BD-3603-6106-A800-FFFFFFFFFFFF}
ShowTrayIcon=1
DeviceIDType=2
DeviceClass=AADE7ECE-DF8C-4AFC-89D2-DE7C73B579D0
DeviceName=PHJ01444-MC35
NameType=2
[END-DO NOT CHANGE/MOVE THIS LINE]

You could use Windows API for this. See http://jachman.wordpress.com/2006/09/11/how-to-access-ini-files-in-c-net/
Edit: As noted in the comments the page is no longer available on the original site, however it's still accessible on the Wayback Machine.
Additionally there is a more recent article on MSDN about accessing the required functions.

Because writing everything in one line makes me a better person than you:
string s = File.ReadAllText("inifile.ini").Split('\r', '\n').First(st => st.StartsWith("DeviceName"));

If you wanted the very simple but not very clean answer:
using System.IO;
StreamReader reader = new StreamReader(filename);
while(reader.ReadLine() != "[DEVICE]") { continue; }
const string DeviceNameString = "DeviceName=";
while(true) {
string line = reader.ReadLine();
if(line.Length < DeviceNameString.Length) { continue; }
else if(line.Substring(0, DeviceNameString.Length) != DeviceNameString) { continue; }
return line.Substring(DeviceNameString.Length);
}
If you're only intending to read one value from the file, it's a plausible option. I would probably combine the loops and add for some end of file checking though myself if you're serious about using this code.

string line;
string deviceName = string.Empty;
// Read the file and display it line by line.
using (System.IO.StreamReader file =
new System.IO.StreamReader("c:\\file.ini"))
{
while ((line = file.ReadLine()) != null)
{
if (line.ToLower().StartsWith("devicename"))
{
string[] fullName = line.Split('=');
deviceName = fullName[1];
break;
}
}
}
Console.WriteLine("Device Name =" + deviceName);
Console.ReadLine();
I am sure there are other ways.

Related

The end of stream is not being detected by reader.EndOfStream

I am reading strings from command line. But end of stream can not be detected by my program. How do I reconstruct this or is there a way to explicitly set EndOfStream to true?
List<String> str = new List<String>();
using (StreamReader reader = new StreamReader(Console.OpenStandardInput()))
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
if (line != string.Empty)
{
str.Add(line);
}
}
That cannot work out by design. The stdin/stdout console streams are opened as long as the program is active. The EndOfStream is executed just before you close the application in this case.
A good solution for your issue is.
using System;
public class Example
{
public static void Main()
{
string line;
do {
line = Console.ReadLine();
if (line != null)
Console.WriteLine("Now I have detected the end of stream.... " + line);
} while (line != null);
}
}
Your code is fine.
When reading from the console, EOF is indicated by entering:
^z then ENTER.
This is a standard as old as DOS. Unix has a similar standard, but it is ^d.
Bill Gates either didn't know Unix or chose to be different but not better. This is why it is ^z not ^d
Likewise, DOS and Windows treat "/" as a command line option delimiter, even though "/" and "" are handled by windows internals as path delimiters.

Search keyword in line and print the line and the following line

I am using the following approach to parse a text file and write the line that contains a specific keyword:
using (StreamReader sr = new StreamReader("C:/Users/Downloads/apple.txt"))
{
string appleLine;
bool lastLine = false;
// currentLine will be null when the StreamReader reaches the end of file
while ((appleLine = sr.ReadLine()) != null)
{
// Search, case insensitive, if the currentLine contains the searched keyword
if (appleLine.IndexOf("Apple", StringComparison.CurrentCultureIgnoreCase) >= 0 || lastLine)
{
Console.WriteLine(appleLine);
Console.WriteLine();
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"C:\Users\Downloads\parsed.txt", true))
{
file.WriteLine(appleLine);
file.WriteLine();
}
lastLine = true;
}
if (lastLine)
{
lastLine = false;
}
}
In apple.txt I have something like this:
--- Line1 Apple MacBook Pro ---
--- Line2 www.newegg.com ---
But this does not print out the line with the URL (Line 2). The apple.txt file could have something like 200 Lines.
Thank you very much for help!
Try to use a boolean that shows you found something to print and need to print the next line too. for example:
class Program
{
static void Main(string[] args)
{
bool nextLineToPrint = false;
using (StreamReader sr = new StreamReader("c:/temp/ESMDLOG.csv"))
{
string currentLine;
// currentLine will be null when the StreamReader reaches the end of file
while ((currentLine = sr.ReadLine()) != null)
{
if (nextLineToPrint)
{
Console.WriteLine(currentLine);
nextLineToPrint = false;
}
// Search, case insensitive, if the currentLine contains the searched keyword
if (currentLine.IndexOf("I/RPTGEN", StringComparison.CurrentCultureIgnoreCase) >= 0)
{
Console.WriteLine(currentLine);
nextLineToPrint = true;
}
}
}
Console.ReadLine();
}
}
You could try using a boolean that indicates if the 'currentline' has been printed, then break out of the loop after the next line is printed. This way not only the currentLine will be printed, but also the next line, after which the while-loop will be exited.
using (StreamReader sr = new StreamReader("c:/temp/ESMDLOG.csv"))
{
string currentLine;
**bool lastline = false;**
// currentLine will be null when the StreamReader reaches the end of file
while((currentLine = sr.ReadLine()) != null)
{
// Search, case insensitive, if the currentLine contains the searched keyword
if(currentLine.IndexOf("I/RPTGEN", StringComparison.CurrentCultureIgnoreCase) >= 0 **|| lastline**)
{
Console.WriteLine(currentLine);
lastline = true;
}
**//Optional, could also say lastline = false inside if, in case you want to keep looping after printing**
**if(lastline){
break;
}**
}
}
Without a more precise problem statement, it's impossible to know for sure what output you want. But something like the following will meet the goal as constrained so far:
using (StreamReader sr = new StreamReader("c:/temp/ESMDLOG.csv"))
{
bool printWindow = false;
Queue<string> window = new Queue<string>();
string currentLine;
// currentLine will be null when the StreamReader reaches the end of file
while((currentLine = sr.ReadLine()) != null)
{
bool nextPrintWindow = false
// Search, case insensitive, if the currentLine contains the searched keyword
if(currentLine.IndexOf("I/RPTGEN", StringComparison.CurrentCultureIgnoreCase) >= 0)
{
nextPrintWindow = true;
}
if (window.Count >= 3)
{
window.Dequeue();
}
window.Enqueue(currentLine);
if (printWindow)
{
Console.WriteLine(string.Join(Environment.NewLine, window));
}
printWindow = nextPrintWindow;
}
}
(Warning: browser-only code above. Please forgive typos and other oversights.)
The above maintains a window collection, i.e. a three-line "window" of the text in the file, by enqueuing the current line, and dequeuing the oldest line if the collection reaches a count of three elements.
At the same time, it maintains a flag that indicates whether to print the contents of the window after reading the next line. If this flag is set to true, it concatenates the lines in the window collection with newline characters and prints the whole thing out.
In this way, the current line along with the previous and next lines are all printed out if the current line meets your match criteria.
Note that the above will print three-line windows for every match. Lines may be printed more than once (as part of different three-line groups) if, for example, consecutive lines meet the match criteria. An alternative implementation would allow the window to grow beyond three elements if consecutive lines meet the match criteria, printing (and restarting) the window only once a line that doesn't meet the match criteria has been read.
Yet another alternative would require two non-matching lines before printing the window, i.e. if you want to make sure that even the non-matching lines are only ever printed once.
I leave those variation as an exercise for the reader. They would not be a lot different from the above, just minor changes in the maintenance of the window and management of the implied state machine encapsulated in the flag variables.

Best way StreamReader skipping Null or WhiteSpace line

After searching and trying the different ways I found I either wasn't happy with the way I was doing the code or it didn't work right for me. I'm new at programming so my understanding is limited. Please keep in mind with the answer.
I want to read a .csv file line by line and skipping lines that are blank. With the contents of the lines I want to put into a list of object. I have everything working except for the skipping line part. Also any feedback about improving any parts of my code are all welcome. I like constructive criticism.
public void CardaxCsvFileReader()
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
try
{
using (System.IO.StreamReader cardaxSR =
new System.IO.StreamReader(System.IO.File.OpenRead(cardaxCsvPath)))
{
string line = "";
string[] value = line.Split(',');
while (!cardaxSR.EndOfStream)
{ // this commented out part is what I would like to work but doesn't seem to work.
line = cardaxSR.ReadLine();//.Skip(1).Where(item => !String.IsNullOrWhiteSpace(item));
value = line.Split(',');
if (line != ",,,,,") // using this as temp to skip the line because the above commented out part doesn't work.
{
CardaxDataObject cardaxCsvTest2 = new CardaxDataObject();
cardaxCsvTest2.EventID = Convert.ToInt32(value[0]);
cardaxCsvTest2.FTItemID = Convert.ToInt32(value[1]);
cardaxCsvTest2.PayrollNumber = Convert.ToInt32(value[2]);
cardaxCsvTest2.EventDateTime = Convert.ToDateTime(value[3]);
cardaxCsvTest2.CardholderFirstName = value[4];
cardaxCsvTest2.CardholderLastName = value[5];
Globals.CardaxQueryResult.Add(cardaxCsvTest2);
}
}
}
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
EDITED
If you are lines are not truly blank and contain commas, you can split with RemoveEmptyEntries option and then check the column count.
while (!cardaxSR.EndOfStream)
{ // this commented out part is what I would like to work but doesn't seem to work.
line = cardaxSR.ReadLine();//.Skip(1).Where(item => !String.IsNullOrWhiteSpace(item));
value = line.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries); // <-- Remove empty columns while splitting. It has a side-effect: Any record with just a single blank column will also get discarded by the if that follows.
if (value.length < 6)
continue;
CardaxDataObject cardaxCsvTest2 = new CardaxDataObject();
cardaxCsvTest2.EventID = Convert.ToInt32(value[0]);
cardaxCsvTest2.FTItemID = Convert.ToInt32(value[1]);
cardaxCsvTest2.PayrollNumber = Convert.ToInt32(value[2]);
cardaxCsvTest2.EventDateTime = Convert.ToDateTime(value[3]);
cardaxCsvTest2.CardholderFirstName = value[4];
cardaxCsvTest2.CardholderLastName = value[5];
Globals.CardaxQueryResult.Add(cardaxCsvTest2);
}
Another improvement feedback I have: When you catch an exception, it's a good practice to log the exception in addition to your custom error line. A custom error line might be good for say website users, but as a developer running some service you will appreciate the actual exception stack trace. It will help you debug a bug easier.
catch (Exception ex)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\".\r\n Exception: {1}", cardaxCsvPath, ex.ToString());
}
Just check if value.Length == 6, this way it'll skip lines which don't contain enough data for your columns
Use a dedicated CSV parser, such as the EasyCSV class available here*:
https://github.com/jcoehoorn/EasyCSV
public void CardaxCsvFileReader()
{
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
Globals.CardaxQueryResult =
EasyCSV.FromFile(cardaxCsvPath)
.Where(r => r.Any(c => !string.IsNullOrEmpty(c)))
.Select(r => CardaxDataObject() {
cardaxCsvTest2.EventID = int.Parse(r[0]),
cardaxCsvTest2.FTItemID = int.Parse(r[1]),
cardaxCsvTest2.PayrollNumber = int.Parse(r[2]),
cardaxCsvTest2.EventDateTime = DateTinme.Parse(r[3]),
cardaxCsvTest2.CardholderFirstName = r[4],
cardaxCsvTest2.CardholderLastName = r[5]
}).ToList();
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
}
I also recommend re-thinking how you structure this. The code below is better practice:
public IEnumerable<CardaxDataObject> ReadCardaxCsvFile(string filename)
{
//no try block at this level. Catch that in the method that calls this method
return EasyCSV.FromFile(cardaxCsvPath)
.Where(r => r.Any(c => !string.IsNullOrEmpty(c)))
// You may want to put a try/catch inside the `Select()` projection, though.
// It would allow you continue if you fail to parse an individual record
.Select(r => CardaxDataObject() {
cardaxCsvTest2.EventID = int.Parse(r[0]),
cardaxCsvTest2.FTItemID = int.Parse(r[1]),
cardaxCsvTest2.PayrollNumber = int.Parse(r[2]),
cardaxCsvTest2.EventDateTime = DateTinme.Parse(r[3]),
cardaxCsvTest2.CardholderFirstName = r[4],
cardaxCsvTest2.CardholderLastName = r[5]
});
}
Suddenly the method boils down to one statement (albeit a very long statement). Code like this is better, because it's more powerful, for three reasons: it's not limited to using just the one input file, it's not limited to only sending it's output to the one location, and it's not limited to only one way to handle errors. You'd call it like this:
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
Globals.CardaxQueryResult = ReadCardaxCsvFile(cardaxCsvPath).ToList();
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
or like this:
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
foreach (var result in ReadCardaxCsvFile(cardaxCsvPath))
{
Globals.CardaxQueryResult.Add(result);
}
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
I also recommend against using a Globalsclass like this. Find a more meaningful object with which you can associate this data.
* Disclaimer: I am the author of that parser

Store a short string

I have many prompts to be used in the code, each prompt is a string such as "welcome to our company" or "goodby" etc.
Now I want to manage these prompts. There are two ways, one is to store each string in a file then load it in the code.
private string LoadPrompt(string inid, string PromptsPath,string promptFile)
{
StringBuilder filetopen = new StringBuilder();
StringBuilder content = new StringBuilder();
filetopen.Append(PromptsPath + inid + "_" + promptFile);
try
{
if (File.Exists(filetopen.ToString()))
{
using (StreamReader reader = File.OpenText(filetopen.ToString()))
{
string line;
while ((line = reader.ReadLine()) != null)
{
content.Append(line);
}
}
}
}
catch (Exception ex)
{
AppLogEx(ex, "Utilities:LoadPrompt");
content.Clear();
content.Append("ERROR");
}
return content.ToString();
}
The other one is put prompts in app.config. Which way is better and fast to load them?
Set up the strings as Resources.
The advantage to using resources over a flat file or app.config is that you can then localize your strings into various languages, and there is support and tooling available to make this process much easier. If you provide multiple languages, your program can automatically select the appropriate language/culture based on the local system without you needing to do any extra work.
About the code, you can do this simpler
instead
using (StreamReader reader = File.OpenText(filetopen.ToString()))
{
string line;
while ((line = reader.ReadLine()) != null)
{
content.Append(line);
}
}
you can write
File.ReadAllLines(filetopen.ToString())
.ToList() - added only for Foreach, I think you've your own Foreach extension for IEnumerable
.ForEach(x => content.AppendLine(x));
about prompts, config or resources
Well, if you do it through app.config, it's essentially an XML file. So you'll be able to find the right prompt fairly easily. Since it's a setting file, it would make sense to store in it any settings that you might change down the road. You can do the same thing with a regular text file, but then you'll need to somehow mark different prompts... in which case, might as well use XML.

finding a line that contains… etc

Ok, well i'm basically trying to find a certain line withing "Users.txt"
Heres my code so far.
if (ok == "b" || ok == "B")
{
using (StreamWriter w = File.AppendText("Users.txt"))
{
//Test
Out.WriteLine("Please state the username");
string user = Console.ReadLine();
Out.WriteLine("Checking..");
if (w.Equals(user))
{
Out.WriteLine("Username is taken");
}
Thread.Sleep(pause);
Out.WriteLine("Please state the password for the user");
string pass = Console.ReadLine();
Logger(user, pass, w);
// Close the writer and underlying file.
w.Close();
Out.WriteLine("Checking..");
Out.WriteBlank();
Thread.Sleep(pause);
Out.WriteLine("Anything else Mr." + Environment.UserName + " ?");
}
string choice = Console.ReadLine();
if (choice == "no")
{
Boot();
}
if (choice == "yes")
{
Console.Clear();
Console.Title = "Administrator Panel";
Panel();
}
}
want it to see if the "user" is taken, then stop them from executing the process.
Thanks for the help.
Try reading (StreamReader with File.Open) each existing username into an array/List and then comparing user input against that list.
Your current code doesn't actually read anything since you're using a StreamWriter with File.AppendText which just lets you write to the end of a file.
Examples:
Reading File into a List
List<string> users = new List<string>();
using (StreamReader r = new StreamReader("Users.txt"))
{
string line;
while ((line = r.ReadLine()) != null)
{
users.Add(line);
}
}
...
string user = Console.ReadLine();
Out.WriteLine("Checking..");
if (users.Contains(user))
{
Out.WriteLine("Username is taken");
}
There are various problems with your code. Let's see if we can break it down one piece at a time.
using (StreamWriter w = File.AppendText("Users.txt"))
This code would be useful if you wanted to open "Users.txt" and append text to it. Since you want to open a file and read from it, you need to use a different object, the StreamReader object:
using (StreamReader r = File.Open("Users.txt"))
Next, you want to check if the given Username is in the file. You're doing:
if (w.Equals(user))
{
Out.WriteLine("Username is taken");
}
This isn't going to work. You are comparing a StreamWriter object with a String object. They will never be equal.
What you need to do instead is change the order of your program like this:
First, read the entire contents of the file into memory. Then, outside of the Using statement, process your user input and your username/password checking.
Let's assume the file is organized like this:
username,password
username2,password2
johnsmith,mysecretcode
janedoe,blahblah
You could, for example, read each line into a Dictionary object, where the Key is the username and the Value is the password.
Dictionary<String, String> myDictionary = new Dictionary<String, String>
// Example of adding ONE username/password to the dictionary
myDictionary.Add("username", "password");
Then, checking for the username would be as simple as
bool containsUsername = myDictionary.ContainsKey(username);
And checking the password would be:
bool doesPasswordMatch = myDictionary[username] == givenPassword;
Give it a shot! C# is a great language to learn.

Categories

Resources