Related
I've the following string that I get from a method. I would like to parse it and make pairs. The order of input string will not change.
INPUT:
ku=value1,ku=value2,ku=value3,ku=value4,ku=value5,lu=value6,lu=value7,lu=value8,lu=value9
OUTPUT
Name value1
Title value2
School value3
.
.
.
Age value9
I think I can read through the string and assign value to the left hand side as I go and so on. However, I am very new to C#.
Use string.Split and split imput string to list key-value pair then split each pair to key and value. Tahts all.
You can do something like this:
void Main()
{
string str = "ku=value1,ku=value2,ku=value3,ku=value4,ku=value5,lu=value6,lu=value7,lu=value8,lu=value9";
var tempStr = str.Split(',');
var result = new List<KeyValue>();
foreach(var val in tempStr)
{
var temp = val.Split('=');
var kv = new KeyValue();
kv.Name = temp[0];
kv.Value = temp[1];
result.Add(kv);
}
}
public class KeyValue
{
public string Name {get;set;}
public string Value{get;set;}
}
If you don't need the first part you can do this using String split as follow
Split String on , using String.Split method creating sequence of string {"ku=value1","ku=value2",...}
Use Linq's Select method to apply an additional transformation
Use Split again on each item on the '=' character
Select the item to the right of the '=', at index 1 of the newly split item
Loop through everything and print your results
Here's the code
var target = "ku=value1,ku=value2,ku=value3,ku=value4,ku=value5,lu=value6,lu=value7,lu=value8,lu=value9";
var split = target.Split(',').Select(a=>a.Split('=')[1]).ToArray();
var names = new[]{"Name","Title","School",...,"Age"};
for(int i=0;i<split.Length;i++)
{
Console.WriteLine(names[i]+"\t"+split[i]);
}
If you want to find out more about how to use these methods you can look at the MSDN documentation for them :
String.Split(char[]) Method
Enumerable.Select Method
I suggest to try this way. Split() plus regular expression
string inputString = "ku=value1,ku=value2,ku=value3,ku=value4,ku=value5,lu=value6,lu=value7,lu=value8,lu=value9";
string pattern = "(.*)=(.*)";
foreach(var pair in inputString.Split(','))
{
var match = Regex.Match(pair,pattern);
Console.WriteLine(string.Format("{0} {1}",match.Groups[1].Value, match.Groups[2].Value));
}
I'm working on a project that uses a plain ASCII .txt file as a key/value configuration file. The current format for ConfigFile.txt is something like
FirstName=Elmer|LastName=Fudd|UserId=EFudd|Password=fubar|Date=7/29/2016
This is easy to read into the program and create a dictionary with KeyValuePairs with code something like:
using (FileStream fs = new FileStream("ConfigFile.txt", FileMode.Open))
{
using (StreamReader sr = new StreamReader(fs))
{
string fileText = sr.ReadToEnd();
// Tokenize the entire file string into separate key=value strings.
string[] tokens = fileText.Split('|');
// Iterate through all of the key=value strings, tokenize each one into a key=Value
// pair and add the key and value as separate strings into the dictionary.
foreach (string token in tokens)
{
string[] keyValuePair = token.Split('=');
configDict.Add(keyValuePair[0], keyValuePair[1]);
}
}
}
It first splits out each key/value as a separate string using the '|' as the delimiter.
FirstName=Elmer
LastName=Fudd
UserId=EFudd
Password=foobar
Date=7/29/2016
Then, for each key/value string, it separates the key and value on the '=' delimiter, creates a KeyValuePair, and inserts it into a dictionary for later lookups in the program.
So far so good. Users are instructed not to create passwords with either delimiter. However, I now have to encrypt the password before including it in the file and the encryption routine can produce any printable character from 0x20 through 0x7F. So, an encrypted password can end up with either or both of the delimiters. I can end up with 'foobar' (or whatever) being encrypted by the encryption engine into P#|=g%. This messes up the ability of the split function to work properly.
So, I want to change the delimiters typed into the Notepad .txt file to control characters so that, instead of the '|' delimiter, I am using 0x1E (Record Separator) and replace the '=' sign with 0x1F (Unit Separator).
I can escape and code this directly in C# with no problems, but how would I modify the original .txt disk file so that it will read in the delimiters as single (non-printable) characters correctly?
So, Instead of having plain text like that, What I would do is use a proper serialization format, such as JSON.
There are tools out there that do the hard work for you.
The built-in System.Web.Script.Serialization namespace has some tools that you can use, but I prefer to use Json.Net. If you have Visual Studio, you can install it with nuGet(let me know in the comments if you need more help than that).
But once you add it to your project, you can do something like this
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
var dict = new Dictionary<string, string>();
dict.Add("FirstName", "Elmer");
dict.Add("LastName", "Fudd");
dict.Add("Password", #"\a\ansld\sb\b8d95nj");
var json = JsonConvert.SerializeObject(dict);
File.WriteAllText("ConfigFile.txt, json);
var txt = File.ReadAllText("ConfigFile.txt");
var newDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(txt);
}
}
}
and ConfigFile.txt will look like this
{"FirstName":"Elmer","LastName":"Fudd","Password":"\\a\\ansld\\sb\\b8d95nj"}
If you want it more human-readable, use
var json = JsonConvert.SerializeObject(dict, Formatting.Indented);
and you'll get
{
"FirstName": "Elmer",
"LastName": "Fudd",
"Password": "\\a\\ansld\\sb\\b8d95nj"
}
You can convert integers to chars so just do this...
string[] tokens = fileText.Split((char)0x1e);
// ...
string[] keyValuePair = token.Split((char)0x1f);
... but encoding your passwords as base64 would be easier and cleaner...
string base64 = Convert.ToBase64String(passwordHash);
byte[] passwordHash = Convert.FromBase64String(base64);
... NOTE:
it is possible that the hashes/encrypted data will contain these characters so I wouldn't just dump the hases into the text file.
The following class extract the string segments using Regular Expressions and support password with non-printable characters : 0x00 .. 0xFF
The class include properties to the segments of the configuration
you can run Demo Example at .NEt Fiddle
using System;
using System.Text.RegularExpressions;
class ConfigParser
{
public string Text { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserId { get; set; }
public string Password { get; set; }
public string Date { get; set; }
public ConfigParser(string text)
{
Text =text;
Parse(text);
}
private static string pattern = #"
^FirstName=(?<firstname>\w+) \|
LastName=(?<lastname>\w+) \|
UserId=(?<userid>\w+) \|
Password=(?<pasword>.+)
Date=(?<date>.+)
$
";
private Regex regex = new Regex(pattern,
RegexOptions.Singleline
| RegexOptions.ExplicitCapture
| RegexOptions.CultureInvariant
| RegexOptions.IgnorePatternWhitespace
| RegexOptions.Compiled
);
private void Parse(string text)
{
Console.WriteLine("text: {0}",text);
Match m = regex.Match(text);
FirstName = m.Groups["firstname"].ToString();
LastName = m.Groups["lastname"].ToString();
UserId = m.Groups["userid"].ToString();
Password = m.Groups["pasword"].ToString();
Date = m.Groups["date"].ToString();
}
}
How to use:
var text ="your text here";
var c = new ConfigParser(text );
you can access the properties of the class: FirstName, LastName,....
Console.WriteLine("firstname: {0}", c.FirstName);
Console.WriteLine("lastname: {0}", c.LastName);
Console.WriteLine("UserId: {0}", c.UserId);
Console.WriteLine("Password: {0}", c.Password);
Console.WriteLine("date {0}", c.Date);
Sample output:
The password include non-printable characters | separator and symbols
text: FirstName=Elmer|LastName=Fudd|UserId=EFudd|Password=fg%|uy|◄¶|hj↑khg|Date=7/29/2016
firstname: Elmer
lastname: Fudd
UserId: EFudd
Password: fg%|uy|◄¶|hj↑khg
date: 7/29/2016
Easiest Answer:
Insert the special characters into the string using the ALT-numberpad value trick. Record Group ALT-31 (▼) to delimit the end of a Key/Value pair and Item Group ALT-30 (▲) to delimit the key from the value. Save the string as UTF-8.
Code for delimiters is
private static char tokenDelimiter = ('▲');
private static char keyValuePairDelimiter = ('▼');
using the same ALT-numberpad trick to put in the up and down triangles. Include instructions that the black triangles are NEVER to be edited or removed and explain their meaning.
It takes me back to my old DOS days. Simple, and took 5 minutes to implement - and it doesn't require that the existing code base be materially changed - just the two delimiter characters changed.
I have a string in my C# model populated with this string:
"[{\"ta_id\":97497,\"partner_id\":\"229547\",\"partner_url\":\"http://partner.com/deeplink/to/229547\"},{\"ta_id\":97832,\"partner_id\":\"id34234\",\"partner_url\":\"http://partner.com/deeplink/to/id34234\"}]"
Is there a way, using LINQ or RegEx, that I could parse out the partner_id's - so I ended up with a list object with:
229547
id34234
Thanks for your help, Mark
I have never used any JSON parser but if it comes to Regex you could try something like this:
private static void regexString()
{
string myString = "[{\"ta_id\":97497,\"partner_id\":\"229547\",\"partner_url\":\"http://partner.com/deeplink/to/229547\"},{\"ta_id\":97832,\"partner_id\":\"id34234\",\"partner_url\":\"http://partner.com/deeplink/to/id34234\"}]";
string[] stringList = Regex.Split(myString, "},{");
for (int i=0; i<stringList.Length ;i++)
{
stringList[i] = Regex.Split(Regex.Split(stringList[i], "partner_id\\\":\\\"")[1], "\\\",\\\"partner_url\\\"")[0];
}
}
Also there is a nice website to help you with creating your own regex patterns in the future, check it out:
gskinner.com
And a nice and short tutorial:
www.codeproject.com
Assuming your link having partner id always-
string Name = "[{\"ta_id\":97497,\"partner_id\":\"229547\",\"partner_url\":\"http://partner.com/deeplink/to/229547\"},{\"ta_id\":97832,\"partner_id\":\"id34234\",\"partner_url\":\"http://partner.com/deeplink/to/id34234\"}]";
string[] splittedString = Regex.Split(Name, "}");
List<string> allIds = new List<string>();
foreach (var i in splittedString)
{
var ids =Regex.Split(i, "/");
string id = ids[ids.Length - 1];
allIds.Add(id);
}
If that is the general format of the string then this regex should work
(?i)(?<=(partner_id).{5})\w+
Test here
This from your string will get
229547 and id34234
(?i) = Case insesitivity
(?<=(partner_id).{5}) = postive lookbehind for parter_id then any 5 characters which in this case will be \":\"
\w+ = Any alphanumeric characters one or more times
Hope this helped
Since this is JSON, you probably shouldn't bother trying to get a regex working. Instead, you can parse the JSON and then use LINQ.
using System.Web.Script.Serialization; // (in System.Web.Extensions.dll)
...
string s = "[{\"ta_id\":97497,\"partner_id\":\"229547\",\"partner_url\":\"http://partner.com/deeplink/to/229547\"},{\"ta_id\":97832,\"partner_id\":\"id34234\",\"partner_url\":\"http://partner.com/deeplink/to/id34234\"}]";
JavaScriptSerializer j = new JavaScriptSerializer();
object[] objects = (object[])j.DeserializeObject(s);
string[] ids = objects.Cast<Dictionary<string, object>>()
.Select(dict => (string)dict["partner_id"])
.ToArray();
It's a little messy to deserialize it to an object, because you don't have any type information. If you're not afraid of making a small class to deserialize into, you can do something like this:
class Foo
{
public string partner_id
{
get;
set;
}
}
...
JavaScriptSerializer j = new JavaScriptSerializer();
string[] ids = j.Deserialize<Foo[]>(s).Select(x => x.partner_id).ToArray();
Note that there are other options for deserializing JSON. I simply chose the most general-purpose one that's built in.
I think I've come across this requirement for a dozen times. But I could never find a satisfying solution. For instance, there are a collection of string which I want to serialize (to disk or through network) through a channel where only plain string is allowed.
I almost always end up using "split" and "join" with ridiculous separator like
":::==--==:::".
like this:
public static string encode(System.Collections.Generic.List<string> data)
{
return string.Join(" :::==--==::: ", data.ToArray());
}
public static string[] decode(string encoded)
{
return encoded.Split(new string[] { " :::==--==::: " }, StringSplitOptions.None);
}
But this simple solution apparently has some flaws. The string cannot contains the separator string. And consequently, the encoded string can no longer re-encoded again.
AFAIK, the comprehensive solution should involve escaping the separator on encoding and unescaping on decoding. While the problem sound simple, I believe the complete solution can take significant amount of code. I wonder if there is any trick allowed me to build encoder & decoder in very few lines of code ?
Add a reference and using to System.Web, and then:
public static string Encode(IEnumerable<string> strings)
{
return string.Join("&", strings.Select(s => HttpUtility.UrlEncode(s)).ToArray());
}
public static IEnumerable<string> Decode(string list)
{
return list.Split('&').Select(s => HttpUtility.UrlDecode(s));
}
Most languages have a pair of utility functions that do Url "percent" encoding, and this is ideal for reuse in this kind of situation.
You could use the .ToArray property on the List<> and then serialize the Array - that could then be dumped to disk or network, and reconstituted with a deserialization on the other end.
Not too much code, and you get to use the serialization techniques already tested and coded in the .net framework.
You might like to look at the way CSV files are formatted.
escape all instances of a deliminater, e.g. " in the string
wrap each item in the list in "item"
join using a simple seperator like ,
I don't believe there is a silver bullet solution to this problem.
Here's an old-school technique that might be suitable -
Serialise by storing the width of each string[] as a fixed-width prefix in each line.
So
string[0]="abc"
string[1]="defg"
string[2]=" :::==--==::: "
becomes
0003abc0004defg0014 :::==--==:::
...where the size of the prefix is large enough to cater for the string maximum length
You could use an XmlDocument to handle the serialization. That will handle the encoding for you.
public static string encode(System.Collections.Generic.List<string> data)
{
var xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("data"));
foreach (var item in data)
{
var xmlItem = (XmlElement)xml.DocumentElement.AppendChild(xml.CreateElement("item"));
xmlItem.InnerText = item;
}
return xml.OuterXml;
}
public static string[] decode(string encoded)
{
var items = new System.Collections.Generic.List<string>();
var xml = new XmlDocument();
xml.LoadXml(encoded);
foreach (XmlElement xmlItem in xml.SelectNodes("/data/item"))
items.Add(xmlItem.InnerText);
return items.ToArray();
}
I would just prefix every string with its length and an terminator indicating the end of the length.
abc
defg
hijk
xyz
546
4.X
becomes
3: abc 4: defg 4: hijk 3: xyz 3: 546 3: 4.X
No restriction or limitations at all and quite simple.
Json.NET is a very easy way to serialize about any object you can imagine. JSON keeps things compact and can be faster than XML.
List<string> foo = new List<string>() { "1", "2" };
string output = JsonConvert.SerializeObject(foo);
List<string> fooToo = (List<string>)JsonConvert.DeserializeObject(output, typeof(List<string>));
It can be done much simpler if you are willing to use a separator of 2 characters long:
In java code:
StringBuilder builder = new StringBuilder();
for(String s : list) {
if(builder.length() != 0) {
builder.append("||");
}
builder.append(s.replace("|", "|p"));
}
And back:
for(String item : encodedList.split("||")) {
list.add(item.replace("|p", "|"));
}
You shouldn't need to do this manually. As the other answers have pointed out, there are plenty of ways, built-in or otherwise, to serialize/deserialize.
However, if you did decide to do the work yourself, it doesn't require that much code:
public static string CreateDelimitedString(IEnumerable<string> items)
{
StringBuilder sb = new StringBuilder();
foreach (string item in items)
{
sb.Append(item.Replace("\\", "\\\\").Replace(",", "\\,"));
sb.Append(",");
}
return (sb.Length > 0) ? sb.ToString(0, sb.Length - 1) : string.Empty;
}
This will delimit the items with a comma (,). Any existing commas will be escaped with a backslash (\) and any existing backslashes will also be escaped.
public static IEnumerable<string> GetItemsFromDelimitedString(string s)
{
bool escaped = false;
StringBuilder sb = new StringBuilder();
foreach (char c in s)
{
if ((c == '\\') && !escaped)
{
escaped = true;
}
else if ((c == ',') && !escaped)
{
yield return sb.ToString();
sb.Length = 0;
}
else
{
sb.Append(c);
escaped = false;
}
}
yield return sb.ToString();
}
Why not use Xstream to serialise it, rather than reinventing your own serialisation format?
Its pretty simple:
new XStream().toXML(yourobject)
Include the System.Linq library in your file and change your functions to this:
public static string encode(System.Collections.Generic.List<string> data, out string delimiter)
{
delimiter = ":";
while(data.Contains(delimiter)) delimiter += ":";
return string.Join(delimiter, data.ToArray());
}
public static string[] decode(string encoded, string delimiter)
{
return encoded.Split(new string[] { delimiter }, StringSplitOptions.None);
}
There are loads of textual markup languages out there, any would function
Many would function trivially given the simplicity of your input it all depends on how:
human readable you want the encoding
resilient to api changes it should be
how easy to parse it is
how easy it is to write or get a parser for it.
If the last one is the most important then just use the existing xml libraries MS supply for you:
class TrivialStringEncoder
{
private readonly XmlSerializer ser = new XmlSerializer(typeof(string[]));
public string Encode(IEnumerable<string> input)
{
using (var s = new StringWriter())
{
ser.Serialize(s, input.ToArray());
return s.ToString();
}
}
public IEnumerable<string> Decode(string input)
{
using (var s = new StringReader(input))
{
return (string[])ser.Deserialize(s);
}
}
public static void Main(string[] args)
{
var encoded = Encode(args);
Console.WriteLine(encoded);
var decoded = Decode(encoded);
foreach(var x in decoded)
Console.WriteLine(x);
}
}
running on the inputs "A", "<", ">" you get (edited for formatting):
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfString
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>A</string>
<string><</string>
<string>></string>
</ArrayOfString>
A
<
>
Verbose, slow but extremely simple and requires no additional libraries
Is there an easy way in C# to read a properties file that has each property on a separate line followed by an equals sign and the value, such as the following:
ServerName=prod-srv1
Port=8888
CustomProperty=Any value
In Java, the Properties class handles this parsing easily:
Properties myProperties=new Properties();
FileInputStream fis = new FileInputStream (new File("CustomProps.properties"));
myProperties.load(fis);
System.out.println(myProperties.getProperty("ServerName"));
System.out.println(myProperties.getProperty("CustomProperty"));
I can easily load the file in C# and parse each line, but is there a built in way to easily get a property without having to parse out the key name and equals sign myself? The C# information I have found seems to always favor XML, but this is an existing file that I don't control and I would prefer to keep it in the existing format as it will require more time to get another team to change it to XML than parsing the existing file.
No there is no built-in support for this.
You have to make your own "INIFileReader".
Maybe something like this?
var data = new Dictionary<string, string>();
foreach (var row in File.ReadAllLines(PATH_TO_FILE))
data.Add(row.Split('=')[0], string.Join("=",row.Split('=').Skip(1).ToArray()));
Console.WriteLine(data["ServerName"]);
Edit: Updated to reflect Paul's comment.
Final class. Thanks #eXXL.
public class Properties
{
private Dictionary<String, String> list;
private String filename;
public Properties(String file)
{
reload(file);
}
public String get(String field, String defValue)
{
return (get(field) == null) ? (defValue) : (get(field));
}
public String get(String field)
{
return (list.ContainsKey(field))?(list[field]):(null);
}
public void set(String field, Object value)
{
if (!list.ContainsKey(field))
list.Add(field, value.ToString());
else
list[field] = value.ToString();
}
public void Save()
{
Save(this.filename);
}
public void Save(String filename)
{
this.filename = filename;
if (!System.IO.File.Exists(filename))
System.IO.File.Create(filename);
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
foreach(String prop in list.Keys.ToArray())
if (!String.IsNullOrWhiteSpace(list[prop]))
file.WriteLine(prop + "=" + list[prop]);
file.Close();
}
public void reload()
{
reload(this.filename);
}
public void reload(String filename)
{
this.filename = filename;
list = new Dictionary<String, String>();
if (System.IO.File.Exists(filename))
loadFromFile(filename);
else
System.IO.File.Create(filename);
}
private void loadFromFile(String file)
{
foreach (String line in System.IO.File.ReadAllLines(file))
{
if ((!String.IsNullOrEmpty(line)) &&
(!line.StartsWith(";")) &&
(!line.StartsWith("#")) &&
(!line.StartsWith("'")) &&
(line.Contains('=')))
{
int index = line.IndexOf('=');
String key = line.Substring(0, index).Trim();
String value = line.Substring(index + 1).Trim();
if ((value.StartsWith("\"") && value.EndsWith("\"")) ||
(value.StartsWith("'") && value.EndsWith("'")))
{
value = value.Substring(1, value.Length - 2);
}
try
{
//ignore dublicates
list.Add(key, value);
}
catch { }
}
}
}
}
Sample use:
//load
Properties config = new Properties(fileConfig);
//get value whith default value
com_port.Text = config.get("com_port", "1");
//set value
config.set("com_port", com_port.Text);
//save
config.Save()
Most Java ".properties" files can be split by assuming the "=" is the separator - but the format is significantly more complicated than that and allows for embedding spaces, equals, newlines and any Unicode characters in either the property name or value.
I needed to load some Java properties for a C# application so I have implemented JavaProperties.cs to correctly read and write ".properties" formatted files using the same approach as the Java version - you can find it at http://www.kajabity.com/index.php/2009/06/loading-java-properties-files-in-csharp/.
There, you will find a zip file containing the C# source for the class and some sample properties files I tested it with.
Enjoy!
Yet another answer (in January 2018) to the old question (in January 2009).
The specification of Java properties file is described in the JavaDoc of java.util.Properties.load(java.io.Reader). One problem is that the specification is a bit complicated than the first impression we may have. Another problem is that some answers here arbitrarily added extra specifications - for example, ; and ' are regarded as starters of comment lines but they should not be. Double/single quotations around property values are removed but they should not be.
The following are points to be considered.
There are two kinds of line, natural lines and logical lines.
A natural line is terminated by \n, \r, \r\n or the end of the stream.
A logical line may be spread out across several adjacent natural lines by escaping the line terminator sequence with a backslash character \.
Any white space at the start of the second and following natural lines in a logical line are discarded.
White spaces are space (, \u0020), tab (\t, \u0009) and form feed (\f, \u000C).
As stated explicitly in the specification, "it is not sufficient to only examine the character preceding a line terminator sequence to decide if the line terminator is escaped; there must be an odd number of contiguous backslashes for the line terminator to be escaped. Since the input is processed from left to right, a non-zero even number of 2n contiguous backslashes before a line terminator (or elsewhere) encodes n backslashes after escape processing."
= is used as the separator between a key and a value.
: is used as the separator between a key and a value, too.
The separator between a key and a value can be omitted.
A comment line has # or ! as its first non-white space characters, meaning leading white spaces before # or ! are allowed.
A comment line cannot be extended to next natural lines even its line terminator is preceded by \.
As stated explicitly in the specification, =, : and white spaces can be embedded in a key if they are escaped by backslashes.
Even line terminator characters can be included using \r and \n escape sequences.
If a value is omitted, an empty string is used as a value.
\uxxxx is used to represent a Unicode character.
A backslash character before a non-valid escape character is not treated as an error; it is silently dropped.
So, for example, if test.properties has the following content:
# A comment line that starts with '#'.
# This is a comment line having leading white spaces.
! A comment line that starts with '!'.
key1=value1
key2 : value2
key3 value3
key\
4=value\
4
\u006B\u0065\u00795=\u0076\u0061\u006c\u0075\u00655
\k\e\y\6=\v\a\lu\e\6
\:\ \= = \\colon\\space\\equal
it should be interpreted as the following key-value pairs.
+------+--------------------+
| KEY | VALUE |
+------+--------------------+
| key1 | value1 |
| key2 | value2 |
| key3 | value3 |
| key4 | value4 |
| key5 | value5 |
| key6 | value6 |
| : = | \colon\space\equal |
+------+--------------------+
PropertiesLoader class in Authlete.Authlete NuGet package can interpret the format of the specification. The example code below:
using System;
using System.IO;
using System.Collections.Generic;
using Authlete.Util;
namespace MyApp
{
class Program
{
public static void Main(string[] args)
{
string file = "test.properties";
IDictionary<string, string> properties;
using (TextReader reader = new StreamReader(file))
{
properties = PropertiesLoader.Load(reader);
}
foreach (var entry in properties)
{
Console.WriteLine($"{entry.Key} = {entry.Value}");
}
}
}
}
will generate this output:
key1 = value1
key2 = value2
key3 = value3
key4 = value4
key5 = value5
key6 = value6
: = = \colon\space\equal
An equivalent example in Java is as follows:
import java.util.*;
import java.io.*;
public class Program
{
public static void main(String[] args) throws IOException
{
String file = "test.properties";
Properties properties = new Properties();
try (Reader reader = new FileReader(file))
{
properties.load(reader);
}
for (Map.Entry<Object, Object> entry : properties.entrySet())
{
System.out.format("%s = %s\n", entry.getKey(), entry.getValue());
}
}
}
The source code, PropertiesLoader.cs, can be found in authlete-csharp. xUnit tests for PropertiesLoader are written in PropertiesLoaderTest.cs.
I've written a method that allows emty lines, outcommenting and quoting within the file.
Examples:
var1="value1"
var2='value2'
'var3=outcommented
;var4=outcommented, too
Here's the method:
public static IDictionary ReadDictionaryFile(string fileName)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (string line in File.ReadAllLines(fileName))
{
if ((!string.IsNullOrEmpty(line)) &&
(!line.StartsWith(";")) &&
(!line.StartsWith("#")) &&
(!line.StartsWith("'")) &&
(line.Contains('=')))
{
int index = line.IndexOf('=');
string key = line.Substring(0, index).Trim();
string value = line.Substring(index + 1).Trim();
if ((value.StartsWith("\"") && value.EndsWith("\"")) ||
(value.StartsWith("'") && value.EndsWith("'")))
{
value = value.Substring(1, value.Length - 2);
}
dictionary.Add(key, value);
}
}
return dictionary;
}
Yeah there's no built in classes to do this that I'm aware of.
But that shouldn't really be an issue should it? It looks easy enough to parse just by storing the result of Stream.ReadToEnd() in a string, splitting based on new lines and then splitting each record on the = character. What you'd be left with is a bunch of key value pairs which you can easily toss into a dictionary.
Here's an example that might work for you:
public static Dictionary<string, string> GetProperties(string path)
{
string fileData = "";
using (StreamReader sr = new StreamReader(path))
{
fileData = sr.ReadToEnd().Replace("\r", "");
}
Dictionary<string, string> Properties = new Dictionary<string, string>();
string[] kvp;
string[] records = fileData.Split("\n".ToCharArray());
foreach (string record in records)
{
kvp = record.Split("=".ToCharArray());
Properties.Add(kvp[0], kvp[1]);
}
return Properties;
}
Here's an example of how to use it:
Dictionary<string,string> Properties = GetProperties("data.txt");
Console.WriteLine("Hello: " + Properties["Hello"]);
Console.ReadKey();
The real answer is no (at least not by itself). You can still write your own code to do it.
C# generally uses xml-based config files rather than the *.ini-style file like you said, so there's nothing built-in to handle this. However, google returns a number of promising results.
I don't know of any built-in way to do this. However, it would seem easy enough to do, since the only delimiters you have to worry about are the newline character and the equals sign.
It would be very easy to write a routine that will return a NameValueCollection, or an IDictionary given the contents of the file.
You can also use C# automatic property syntax with default values and a restrictive set. The advantage here is that you can then have any kind of data type in your properties "file" (now actually a class). The other advantage is that you can use C# property syntax to invoke the properties. However, you just need a couple of lines for each property (one in the property declaration and one in the constructor) to make this work.
using System;
namespace ReportTester {
class TestProperties
{
internal String ReportServerUrl { get; private set; }
internal TestProperties()
{
ReportServerUrl = "http://myhost/ReportServer/ReportExecution2005.asmx?wsdl";
}
}
}
There are several NuGet packages for this, but all are currently in pre-release version.
Capgemini.Cauldron.Core.JavaProperties 2.0.39-beta
Kajabity.Tools.Java 0.2.6638.28124
[Update]
As of June 2018, Capgemini.Cauldron.Core.JavaProperties is now in a stable version (version 2.1.0 and 3.0.20).
I realize that this isn't exactly what you're asking, but just in case:
When you want to load an actual Java properties file, you'll need to accomodate its encoding. The Java docs indicate that the encoding is ISO 8859-1, which contains some escape sequences that you might not correctly interpret. For instance look at this SO answer to see what's necessary to turn UTF-8 into ISO 8859-1 (and vice versa)
When we needed to do this, we found an open-source PropertyFile.cs and made a few changes to support the escape sequences. This class is a good one for read/write scenarios. You'll need the supporting PropertyFileIterator.cs class as well.
Even if you're not loading true Java properties, make sure that your prop file can express all the characters you need to save (UTF-8 at least)
No there is not : But I have created one easy class to help :
public class PropertiesUtility
{
private static Hashtable ht = new Hashtable();
public void loadProperties(string path)
{
string[] lines = System.IO.File.ReadAllLines(path);
bool readFlag = false;
foreach (string line in lines)
{
string text = Regex.Replace(line, #"\s+", "");
readFlag = checkSyntax(text);
if (readFlag)
{
string[] splitText = text.Split('=');
ht.Add(splitText[0].ToLower(), splitText[1]);
}
}
}
private bool checkSyntax(string line)
{
if (String.IsNullOrEmpty(line) || line[0].Equals('['))
{
return false;
}
if (line.Contains("=") && !String.IsNullOrEmpty(line.Split('=')[0]) && !String.IsNullOrEmpty(line.Split('=')[1]))
{
return true;
}
else
{
throw new Exception("Can not Parse Properties file please verify the syntax");
}
}
public string getProperty(string key)
{
if (ht.Contains(key))
{
return ht[key].ToString();
}
else
{
throw new Exception("Property:" + key + "Does not exist");
}
}
}
Hope this helps.