That's a part of it, at least; just property values inside parentheses and curly brackets, as strings, doubles, and ints. The trouble is, StreamReader can't recognize what's inside the parentheses as values. Google gave me some tips on how to read ini files, which just use = to sepearte variables from values, but these property files are different in that they have values inside parentheses and a few properties [Ambient(r,g,b) for example] have three values associated with them.
What makes it even more complicated is that I can't tell for sure what line any particular property will be at in the file.
In other words, I can have float Angle in my program, and I can read Angle(-90.0); from the file, but how do I then assign a value of -90.0 to the Angle float?
If by anything you mean code for reading it, I was trying to think of how to go about it when a friend suggested I use ReadLine to put each line into a string array, then for every property I could loop through the array until the first few characters of a line match that property's name. Once I got the property's position in the array I could use array[index].Substring(#, #) to get the value, and Convert.ToInt or something to convert it to a numeric value. Could that work?
To do that for every single property (I'm expecting 10 - 60 properties per file) seems rather complex and expensive, but as you noted, it's a really odd file, so I'll have to do whatever works. To that end I wrote up some code to test it:
Code:
string fileName;
string[] fileDataArray;
int fDAIndex = 0;
private void loadToolStripMenuItem_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
fileName = openFileDialog1.FileName;
System.IO.StreamReader tr = new System.IO.StreamReader(fileName);
// Read the lines of the file into an array
while (tr.Peek() >= 0)
{
fileDataArray[fDAIndex] = tr.ReadLine();
fDAIndex = fDAIndex + 1;
}
tr.Close();
fDAIndex = 0;
// Get the index where the string containing "Angle(-90.0);" is stored
while (fileDataArray[fDAIndex].Substring(0, 5) != "Angle")
{
fDAIndex = fDAIndex + 1;
}
// Get the "-90.0" part of the string
string angleValueStr = fileDataArray[fDAIndex].Substring(5, 5);
// To check if it worked, display angleValueStr in the angleValueText textbox (already on the form)
angleValueText.Text = angleValueStr;
}
}
When I load the file, I get an error at fileDataArray[fDAIndex] = tr.ReadLine(); saying that "NullReferenceException was unhandled" and "Object reference not set to an instance of an object." What's going on? Moreover, is there a better way to get this data than this current playing around with ReadLine and string array and substrings?
EDIT - I changed the code slightly. Instead of that bit with the tr StreamReader and the while loop that loads the lines into an array, I now just have the one line:
I still get an error when loading the file, this time at the line that says
Code:
while (fileDataArray[fDAIndex].Substring(0, 5) != "Angle")
The error is "Index and length must refer to a location within the string." The problem is with Substring(0, 5); some lines only have one character, so I get an error when Substring tries to read the first 5 characters. How to I get the while loop to continue on past that error? I can't really do Substring(0, 1) since 1 character isn't really much use in identifying the property.
Further, even when I used Substring(0, 1) != "A", I got the same error. I guess C# reads the lines with just newline characters as characterless, so it will only accept Substring(0, 0)?
Last edited by fiodis; June 27th, 2012 at 06:19 PM.
I personally do not like ReadAllLines as it can get very memory intensive but if the files are small I guess it doesn't really matter.
To fix your substring issue you can use:
Code:
while (fileDataArray[fDAIndex].Substring(0, fileDataArray[fDAIndex].IndexOf("(")) != "Angle")
//for the properties value
fileDataArray[fDAIndex].Substring(fileDataArray[fDAIndex].IndexOf("(") + 1)
The problem is when you were doing Sustring(0, 5) and you only have a string that may be 4 characters long, you will hit the exception you are currently having problems with.
So what I do is get the index of the "(" parentheses symbol + 1, then I do not need a length, it will just take that index to the end of the string. If you need to cut white space add .Trim()
**Also Note**
I am not on my work computer lmao, so I may have spelled something wrong or missed a character/symbol cause I am lazy and always check with VS haha
Last edited by Deranged; June 27th, 2012 at 07:49 PM.
Oh yea, duh, forgot there is a ")" at the end lol
In that case, couple ways to deal with this.
Code:
//for the properties value
int num = 0;
fileDataArray[fDAIndex].Substring(num = fileDataArray[fDAIndex].IndexOf("(") + 1, fileDataArray[fDAIndex].IndexOf(")") - num
or you can remove it by getting the length of line..
Code:
//for properties value
int length = 0;
length = yourstring.Length()
fileDataArray[fDAIndex].Substring(fileDataArray[fDAIndex].IndexOf("(") + 1).Remove(length - 1)
It's something along those lines, ugh... I feel terrible without IDE/Intellisense.... Feel bad for old programmers
*Edit*
Come to think of it, you probably can't use length on an array. Length will return the array index basically. Sigh
Last edited by Deranged; June 27th, 2012 at 07:51 PM.
Wow, I didn't know about IndexOf; that'll be useful.
Unfortunately I get an error inside the while loop, again with the length parameter of Substring, this time saying that "Length cannot be less than zero." I'm guessing that for lines without a "(" char the IndexOf("(") evaluates to -1 or something. The trouble is I can't do IndexOf("(") + 1 inside the while loop since that will include the first character of the actual property value, and I won't be able to check that against the property name inside the while condition.
Is there some way I can tell the program, "If you encounter this error, no biggie, just ignore it and execute the while loop anyway"? Or maybe a better way would not use a while statement at all? It's getting late now, but in the morning I think I'll try something with a try-catch statement. I've never used those before but they seem like they handle errors better than what the code's currently using.
Ok so I wrote this up extremely fast, but this is an idea of how I would read the file. If I had your source or knew exactly how you specify the property or what you want to do with it I'd be able to help further but here you go:
Code:
namespace ReadPropertyFile
{
class Program
{
static void Main(string[] args)
{
string fn = @"C:\Users\Kelby\Documents\Visual Studio 2010\Projects\ReadPropertyFile\Properties.txt";
foreach (List<string> propData in RetrievePropertyData(fn))
{
string formattedPropData = String.Format("\nProperty Name: {0}\nProperty Value: {1}", propData[0], propData[1]);
Console.Write(formattedPropData);
Console.ReadKey();
}
}
private static IEnumerable<List<string>> RetrievePropertyData(string fileName)
{
List<string> ret = new List<string>();
string line = string.Empty;
int num = 0;
using (StreamReader sr = new StreamReader(fileName))
{
while (!sr.EndOfStream)
{
line = sr.ReadLine().Trim();
if (line.Trim().Length <= 1) continue; //Those stupid "}{" are evil
if (ret.Count > 1) ret = new List<string>();
ret.Add(line.Substring(0, line.IndexOf("(")));
if (IsQualifiedOrMultiValued(line))
line = line.Replace("\"", "");
ret.Add(line.Substring(num = line.IndexOf("(") + 1, line.IndexOf(")") - num));
yield return ret;
}
}
}
private static bool IsQualifiedOrMultiValued(string inputLine)
{
for (int i = 0; i < inputLine.Length; i++)
{
if (inputLine[i] == '"')
return true;
}
return false;
}
}
}
I was only using \n for console testing, doesn't really matter. I would personally use Environment.Newline for cross platform.
I was renewing the list because I didn't want to read all the properties straight into the list. Because in my console testing I was using propData[0] and propData[1] so I was only expecting the property name and property value. Of course if I want to just read all the properties into the list, just remove the renewing of the list and use a for loop or something to indicate the index within the list.
Like I said, I didn't really know what end result you were looking for? I could have added it to a form so you could be like "I want to know this property" so then you pass the argument into the parser, looks for that property and then displays result? I just simply read them all and displayed their value.
Bookmarks