Differentiating between different values with a Line.Contains() - c#

Something I was curious about, I'm coding a utility for a old game that I play and this allows for custom NPC's. Long story short, I'm coding a reader for these custom NPC files. I've gotten most of the reading down with a line.contains() method (all code will be shown later) but there's a problem. The file can contain either just "height" or "gfxheight" which both do different things. Using line.contains("width") will make it output both width and gfxwidth twice. I don't really know any good way to explain it so here's the file:
width=32
height=32
gfxwidth=64
gfxheight=32
nofireball=1
noiceball=1
noyoshi=1
grabside=0
The Console output when I read it in and do what I need to split the lines and such:
And here's the code I use for height and gfxheight (of course there are others but these are the only problems I have when reading):
if (line.Contains("height"))
{
var split = line.Split(new char[] { '=' }, 2);
decimal dc;
//var val = int.Parse(split.ToString());
Console.WriteLine(split[0].ToString() + " is equal to " + split[1].ToString());
npcHeight.Value = Decimal.Parse(split[1].ToString());
npcHeight.Enabled = true;
npcHCb.Checked = true;
}
if (line.Contains("gfxheight"))
{
var split = line.Split(new char[] { '=' }, 2);
//var val = int.Parse(split.ToString());
Console.WriteLine(split[0].ToString() + " is equal to " + split[1].ToString());
}
Of course there's also the code for width and gfxwidth and the other various codes but I'm not going to bother posting those because I can apply what I get for the height to those.
So what would I have to do to differentiate between them? Suggestions?
Thanks in advanced,
Mike

Read the file into a string array, then parse it into a dictionary.
var file = File.ReadAllLines(yourFile);
var config = (from line in file
let s = line.Split('=')
select new { Key = s[0], Value = s[1] })
.ToDictionary(x => x.Key, x => x.Value);
Now you can access anything you want by referencing the key:
var gfxHeight = config["gfxheight"]; // gfxHeight is a string containing "32"
If you know the value after the = is always a number, you could parse it:
var config = (from line in file
let s = line.Split('=')
select new { Key = s[0], Value = int.Parse(s[1]) })
.ToDictionary(x => x.Key, x => x.Value);
var gfxHeight = config["gfxheight"]; // gfxHeight is an int containing 32

Instead of trying to figure out what each line is before splitting it, try splitting it first. This parsing approach leverages the format of the file and has a much-reduced dependency on its data:
foreach (var line in lines) {
var data = line.Split('=', 2);
if (data.Length != 2) {
continue;
}
var attrib = data[0];
var value = data[1];
Console.WriteLine(attrib + " is equal to " + value);
switch (attrib) {
case "height":
// ...
break;
case "gfxheight":
// ...
break;
}
}

I figured out a solution actually! Be warned: I haven't refreshed the page yet to see any proposed answers.
if (line.Contains("width"))
{
if (line.Contains("gfx"))
{
var split = line.Split(new char[] { '=' }, 2);
//var val = int.Parse(split.ToString());
Console.WriteLine(split[0].ToString() + " is equal to " + split[1].ToString());
npcWidth.Value = Decimal.Parse(split[1].ToString());
npcWidth.Enabled = true;
npcWCb.Checked = true;
}
else
{
var split = line.Split(new char[] { '=' }, 2);
Console.WriteLine(split[0].ToString() + " is equal to " + split[1].ToString());
pNpcWidth.Value = Decimal.Parse(split[1].ToString());
pNpcWidth.Enabled = true;
pNpcWidthCb.Checked = true;
}
}
Basically that ^
What it does is checks if the line is width with that line.Contains method. And if it does, it then checks to see if it contains gfx in it (as in gfxheight, gfxwidth, etc) and if it does, then that's the gfxheight or gfxwidth value. If not, it's the regular height/width.

Related

How to blacklist(filter) a list c#?

I am trying to filter a existing list, currently my code will output me the following:
MY OUTPUT:
CHEVRON -- 90,02 € -- 200 PCS -- 18004 €
DEUTSCHE_BOERSE_NA_O.N. -- 139,00 € -- 100 PCS -- 13900 €
VONOVIA_SE -- 50,94 € -- 170 PCS -- 8659,8 €
SAP_SE -- 112,86 € -- 50 PCS -- 5643 €
I want to further filter the output by removing specific names by giving the first word into a richtextbox
MY RICHTEXTBOX:
CHEVRON SAP
DESIRED OUTPUT:
DEUTSCHE BOERSE NA O.N. -- 139,00 € -- 100 PCS -- 13900 €
VONOVIA SE -- 50,94 € -- 170 PCS -- 8659,8 €
MY CURRENT WORKING CODE:
private void timer4_Tick(object sender, EventArgs e)
{
try
{
string[] clist = listBox1.Items.OfType<string>().ToArray();
string toDisplay = string.Join(Environment.NewLine, clist);
listBox3.Items.Clear();
var germanCulture = new CultureInfo("de-DE");
var map = new Dictionary<string, double>();
foreach (string f in clist)
{
var inputs = f.Split(new[] { " -- " }, StringSplitOptions.None); //Creates a list of strings
var unitPrice = Convert.ToDouble(inputs[1].Split(' ')[0], CultureInfo.GetCultureInfo("de-DE").NumberFormat);
var numUnits = Convert.ToDouble(inputs[2].Split(' ')[0]);
var key = inputs[0];
var pieces = inputs[2];
var price = inputs[1];
if (map.ContainsKey(key)) map[key] = numUnits * unitPrice;
else
{
try
{
map.Add(key + " --" + price + " -- " + pieces, numUnits * unitPrice);
}
catch
{
}
}
}
var sortedMap = map.OrderByDescending(x => x.Value);
foreach (var item in sortedMap)
{
listBox3.Items.Add(item.Key + " -- " + item.Value + " €");
}
} catch (Exception E)
{
}
}
WHAT I TRIED:
listboxfilteritems1 = listBox3.Items.Cast<String>().ToList();
string[] listboxfilteritems2 = listboxfilteritems1
.Select(s => s.Replace("_", " "))
.ToArray()
;
listboxfilteritems3 = new List<string>(listboxfilteritems2.Where(l => !richTextBox3.Text.Split().Any(s => l.IndexOf(s, StringComparison.CurrentCultureIgnoreCase) >= 0)));
listBox3.DataSource = listboxfilteritems3;
THE PROBLEM
Simply nothing happens and the programm crashes if I dont catch the general exception
In my programs to filter a list i create a copy of it that is the one that i display, then when i activate the filters i just go though the list checking for conditions:
List<Object> yourOriginalList = ...;
foreach (var o in yourOriginalList)
{
if (condition)
displayList.Add(o);
}
displayList.Sort();
....ItemsSource = displayList;
If you have the posibility to use LinQ it will work really nice:
public Object[] displayList (Object[] item)
{
return item.Where(i => condition...).ToArray();
}
I would encourage you to use specific types instead of "var" to make the code a bit more readble, specifically for this bit of your code.
Dictionary<string, double> map = new Dictionary<string, double>();
foreach (string f in clist)
{
string[] inputs = f.Split(new[] { " -- " }, StringSplitOptions.None); //Creates a list of strings
double unitPrice = Convert.ToDouble(inputs[1].Split(' ')[0], CultureInfo.GetCultureInfo("de-DE").NumberFormat);
double numUnits = Convert.ToDouble(inputs[2].Split(' ')[0]);
string key = inputs[0];
string pieces = inputs[2];
string price = inputs[1];
if (map.ContainsKey(key)) map[key] = numUnits * unitPrice;
Because this may lead to some confusion, for example, "inputs" is not a list, but an array of strings.
Continuing, I've done some assumptions over the code that's missing that may or may not be true.
I guess this is the line of code where you are getting an exception:
listboxfilteritems3 = new List<string>(listboxfilteritems2.Where(l => !richTextBox3.Text.Split().Any(s => l.IndexOf(s, StringComparison.CurrentCultureIgnoreCase) >= 0)));
Assuming that the StringComparison contains the correct key, I don't see anything inherently wrong with it, but maybe if you try write and debug the code without the lamda expression you may be able to see some data inside the attributes that can be wrong. I tried writing a bit of the schema of how it could be, but beware I am not sure of some of the types here:
listboxfilteritems3 = new List<string>();
bool addText = true;
foreach(string item1 in listboxfilteritems3){
foreach(string textBox in richTextBox3.Text.Split('?')){
if(!item1.Equals(textBox)){
addText=false;
}
}
if(addText){
listboxfilteritems3.Add(item1);
}
addText=true;
}
And you coul'd try either to debug it by hand or ad some try catch blocks that specify the error.
I haven't specified the correct character that should be used by the separator I left it as a '?', and I am assuming that you are managing the fact tahat, like in the example you provided, you may be entering substrings of a key instead of the exact key, or is beeing managed by the CultureInfo.
Anyway, I can't think of any better way to find the error, hope it helps.

Create Bing maps v8 polygon from lookup files

I have two files (fileA.txt and fileB.txt) that I need to use to create polygons on a map. FileA contains the list of reference numbers that refer to fileB. I am able to get that list from A and find the corresponding file in B using streamreader. What I can not seem to grab is the data that I need from that file.
FileA looks like:
|ADV|170613/0448|170613/0600|KRIW||0|1
WYZ023 500230 Star_Valley
WYZ013 500130 Jackson_Hole
I take the number in bold, 500230, and look for it in fileB which looks like this:
|FIPS|500230|
8 59.094 59.091 -138.413 -138.425 59.091 -138.413 59.092 -138.425 59.094 -138.415 59.091 -138.413
8 59.101 59.099 -138.397 -138.413 59.099 -138.405
59.101 -138.413 59.100 -138.397 59.099 -138.405
|FIPS|500231|
Each line that starts with 8 after the |FIPS| line is one polygon. The number 8 represents the number of lat long pairs AFTER the numbers in bold. This 8 can be anywhere from 4 to 20. I can also have anywhere from 1 to 20 of these "groups" of lat long pairs within each |FIPS|.
In the end, I'm trying to get a list of lat long pairs for each |FIPS| that equals the |FIPS| that is looked up.
or an array of arrays. Any thoughts?
UPDATE: this is what I came up with but Im getting stuck on mainListLoop. The streamReader only reads to the end of the line but I need it to read to the next FIPS. Any suggestions?
[HttpGet]
public ActionResult GetWinterData()
{
var winterFilePaths = HostingEnvironment.MapPath(ConfigurationManager.AppSettings["WatchWarnFilePath"] + "/wstm.txt");
var stringData = new List<string>();
var mainList = new List<WinterModel>();
using (var reader = new StreamReader(winterFilePaths))
{
while (!reader.EndOfStream)
{
var data = reader.ReadLine().Trim();
if (!string.IsNullOrEmpty(data))
stringData.Add(data);
}
reader.Close();
}
WinterModel temp = null;
stringData.ForEach(line =>
{
if (line.StartsWith("|"))
{
if (temp != null)
{
mainList.Add(temp);
}
string[] rawData = line.Split('|');
temp = new WinterModel
{
Type = rawData[0],
PolyBorderColor = GetBorderColor(rawData[0], types.WINTER),
PolyBorderThickness = GetPolyBorderThickness(rawData[0], types.WINTER),
StartDateTime = rawData[1],
EndDateTime = rawData[2],
innerData = new List<tempInnerWinterModel>(),
InfoboxTitle = GetInfoboxTitle(rawData[0], types.WINTER)
};
}
else
{
string[] tempLine = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
temp.innerData.Add(new tempInnerWinterModel
{
param1 = tempLine[0],
FIPS = tempLine[1],
Location = tempLine[2],
latLongs = new List<lat_longPairs>()
});
}
});
mainList.Add(temp);
getWinterLatLongPairs2(mainList);
var tempJson = (from item in stringData
select item.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
into rawData
select new WatchPolygons
{
Type = rawData[0],
PolyBorderColor = GetBorderColor(rawData[0], types.WINTER),
StartDateTime = rawData[1],
EndDateTime = rawData[2],
//Lat_Long_Pairs = getWinterLatLongPairs2(rawData[5])
//Metadata = "Watch Type: " + rawData[0] + "< /br>" + "Watch Start: " + rawData[1] + ' ' + rawData[2]
// + "< /br>" + "Watch End: " + rawData[3] + ' ' + rawData[4]
});
return Json((from item in stringData
select item.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
into rawData
select new WatchPolygons
{
Type = rawData[0],
PolyBorderColor = GetBorderColor(rawData[0], types.WINTER),
StartDateTime = rawData[1],
EndDateTime = rawData[2],
//Lat_Long_Pairs = getWinterLatLongPairs2(rawData[5])
//Metadata = "Watch Type: " + rawData[0] + "< /br>" + "Watch Start: " + rawData[1] + ' ' + rawData[2]
// + "< /br>" + "Watch End: " + rawData[3] + ' ' + rawData[4]
}).ToList(), JsonRequestBehavior.AllowGet);
}
private static void getWinterLatLongPairs2(List<WinterModel> inputFips)
{
var searchFips = inputFips;
var fipFilePath = HostingEnvironment.MapPath(ConfigurationManager.AppSettings["WatchWarnFilePath"] + "/pfzbnds.tbl");
var stringInnerData = new List<string>();
using (var reader = new StreamReader(fipFilePath))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine().Trim();
if (!string.IsNullOrEmpty(line) && line.Contains("<FIPS>"))
{
MainListLoop(inputFips, line, reader);
if (inputFips.Last().innerData.Last().latLongs.Count > 0)
{
return;
}
}
}
reader.Close();
}
return;
}
private static void MainListLoop(List<WinterModel> inputFips, string line, StreamReader reader)
{
inputFips.ForEach(main =>
{
main.innerData.ForEach(fips =>
{
if (line.Contains(fips.FIPS))
{
var line2 = reader.ReadLine().Trim();
fips.param1 = "CHANGE";
string[] tempLine = line2.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
string numLatLongPairs = tempLine[0];
var latLonPairsWint = new List<lat_longPairs>();
int endIndex = ((Int16.Parse(numLatLongPairs) * 2) + 3 - 1);
//grab each pair of lat/longs starting at the 5th and add them to the array
for (int i = 5; i < endIndex; i += 2)
{
fips.latLongs.Add(new lat_longPairs { latitude = decimal.Parse(tempLine[i]), longitude = decimal.Parse(tempLine[i + 1]) });
}
return;
}
});
});
}
Not 100% clear on the file format of A, but if fileB isn't massive, I would recommend parsing fileB first into a dictionary where the key is the fips code and the value is a array of array of lat,long pairs, or whatever coordinate object you have in your application (numbers work fine too). From there when parsing fileA, when ever you come across a fips value, check the dictionary. This would be the easiest implementation.
If however fileB is massive, then memory on your computer or server (where you are parsing this), may be an issue. Or if fileA only references a small subset of data in fileB, then parsing all the data in fileB likely isn't the greatest. In these case, parse fileA and get a list of fips code. Next, scan through fileB until you find a fips code and see if it is in your list from fileA. If it is, parse that data from fileB and remove that value from your fileA list. Continue this until all fips in the filaA list are removed or you reach the end of fileB. For scanning fileB, you can write a custom stream reader, or if the file isn't massive, read the whole thing as a string, then use the index and substring to skip through fileB looking for fips.

C# implement function about way synchronization

I did this:
businessCRM.Description =
string.Format("{0}\n" +
"Name: {1}\n" +
"Street: {2}\n " +
"Number: {3}\n" +
"floor: {4}\n" +
business.Name,
business.Address.Street,
business.Address.Number,
business.Address.floor,
);
And I would like to do the same but inversely in other method something like this to do way synchronization (round trip):
business.Address.Street = businessCRM.Description;
business.Address.Number = businessCRM.Description;
business.Address.floor = businessCRM.Description;
But I don't know if this is completely correct, any idea?
The real solution would be to put an additional Address member in your businessCRM class, so you can store the data and the description and don't need to extract the data from the description later. Parsing it will only lead to problems.
You can pull it apart into a dictionary using something like the below; note I had to special-case the first line (has no prefix), and it won't work well if your data includes newlines internally in the values:
var parts = new Dictionary<string, string>(
StringComparer.InvariantCultureIgnoreCase);
string firstLine;
using(var reader = new StringReader(description))
{
string line;
firstLine = reader.ReadLine();
var splitBy = new[] { ':' };
while ((line = reader.ReadLine()) != null)
{
var pair = line.Split(splitBy, 2, StringSplitOptions.None);
if (pair.Length == 2) parts[pair[0].Trim()] = pair[1].Trim();
}
}
string tmp;
string name, street, number, floor; // in your case, you could assign to
// the properties directly
name = parts.TryGetValue("Name", out tmp) ? tmp : "";
street = parts.TryGetValue("Street", out tmp) ? tmp : "";
number = parts.TryGetValue("Number", out tmp) ? tmp : "";
floor = parts.TryGetValue("floor", out tmp) ? tmp : "";
First of all see #Marc Gravell comment under question - it would be much easer to handle "deserialization".
If not, here is solution.
my code may be slow - you need to cache reflection info (like property info collection)
there is no error handling and checking (ex. for nullity - do it manually ;))
Code assumes that input is always proper input (f.e. new line char inside value), format etc.
Code:
var addressType = business.Address.GetType();
foreach (var line in businessCRM.Description
.Split(new[] { "\n", Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries))
{
var propSelectorIndex = line.IndexOf(":");
if (propSelectorIndex == -1) continue;
var propName = line.Subtring(0, propSelectorIndex);
var propInfo = addressType.GetProperties(BindigsFlag.Public
| BindigsFlag.Instance)
.FirstOrDefault(prop => prop.Name == propName);
if (propInfo == null) throw new InvalidOperationException();
var newPropValue = line.Substring(propSelectorIndex + 2);
// + 2 to omit : char and additional space
propInfo.SetValue(business.Address, newPropValue, null);
}

C#: Loop over Textfile, split it and Print a new Textfile

I get many lines of String as an Input that look like this. The Input is a String that comes from
theObjects.Runstate;
each #VAR;****;#ENDVAR; represents one Line and one step in the loop.
#VAR;Variable=Speed;Value=Fast;Op==;#ENDVAR;#VAR;Variable=Fabricator;Value=Freescale;Op==;#ENDVAR;
I split it, to remove the unwanted fields, like #VAR,#ENDVAR and Op==.
The optimal Output would be:
Speed = Fast;
Fabricator = Freescale; and so on.
I am able to cut out the #VAR and the#ENDVAR. Cutting out the "Op==" wont be that hard, so thats now not the main focus of the question. My biggest concern right now is,thatI want to print the Output as a Text-File. To print an Array I would have to loop over it. But in every iteration, when I get a new line, I overwrite the Array with the current splitted string. I think the last line of the Inputfile is an empty String, so the Output I get is just an empty Text-File. It would be nice if someone could help me.
string[] w;
Textwriter tw2;
foreach (EA.Element theObjects in myPackageObject.Elements)
{
theObjects.Type = "Object";
foreach (EA.Element theElements in PackageHW.Elements)
{
if (theObjects.ClassfierID == theElements.ElementID)
{
t = theObjects.RunState;
w = t.Replace("#ENDVAR;", "#VAR;").Replace("#VAR;", ";").Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string s in w)
{
tw2.WriteLine(s);
}
}
}
}
This linq-query gives the exptected result:
var keyValuePairLines = File.ReadLines(pathInputFile)
.Select(l =>
{
l = l.Replace("#VAR;", "").Replace("#ENDVAR;", "").Replace("Op==;", "");
IEnumerable<string[]> tokens = l.Split(new[]{';'}, StringSplitOptions.RemoveEmptyEntries)
.Select(t => t.Split('='));
return tokens.Select(t => {
return new KeyValuePair<string, string>(t.First(), t.Last());
});
});
foreach(var keyValLine in keyValuePairLines)
foreach(var keyVal in keyValLine)
Console.WriteLine("Key:{0} Value:{1}", keyVal.Key, keyVal.Value);
Output:
Key:Variable Value:Speed
Key:Value Value:Fast
Key:Variable Value:Fabricator
Key:Value Value:Freescale
If you want to output it to another text-file with one key-value pair on each line:
File.WriteAllLines(pathOutputFile, keyValuePairLines.SelectMany(l =>
l.Select(kv => string.Format("{0}:{1}", kv.Key, kv.Value))));
Edit according to your question in the comment:
"What would I have to change/add so that the Output is like this. I
need AttributeValuePairs, for example: Speed = Fast; or Fabricator =
Freescale ?"
Now i understand the logic, you have key-value pairs but you are interested only in the values. So every two key-values belong together, the first value of a pair specifies the attibute and the second value the value of that attribute(f.e. Speed=Fast).
Then it's a little bit more complicated:
var keyValuePairLines = File.ReadLines(pathInputFile)
.Select(l =>
{
l = l.Replace("#VAR;", "").Replace("#ENDVAR;", "").Replace("Op==;", "");
string[] tokens = l.Split(new[]{';'}, StringSplitOptions.RemoveEmptyEntries);
var lineValues = new List<KeyValuePair<string, string>>();
for(int i = 0; i < tokens.Length; i += 2)
{
// Value to a variable can be found on the next index, therefore i += 2
string[] pair = tokens[i].Split('=');
string key = pair.Last();
string value = null;
string nextToken = tokens.ElementAtOrDefault(i + 1);
if (nextToken != null)
{
pair = nextToken.Split('=');
value = pair.Last();
}
var keyVal = new KeyValuePair<string, string>(key, value);
lineValues.Add(keyVal);
}
return lineValues;
});
File.WriteAllLines(pathOutputFile, keyValuePairLines.SelectMany(l =>
l.Select(kv=>string.Format("{0} = {1}", kv.Key, kv.Value))));
Output in the file with your single sample-line:
Speed = Fast
Fabricator = Freescale

comparing string and variable but failing based on contains

What I have going on is I have two files. Both files are delimited by '|'. If file 1 matches a line in file 2 I need to combine the lines. Here is the code:
string[] mathlines = File.ReadAllLines(#"C:\math.txt");
var addlines = File.ReadAllLines(#"K:\add.txt");
foreach (string ml in mathlines)
{
string[] parse = ml.Split('|');
if (addlines.Contains(parse[0]))
{
File.AppendAllText(#"C:\final.txt", parse[0]+"|"+parse[1]+"\n");
}
else
{
File.AppendAllText(#"C:\final.txt", ml + "\n");
}
}
I realize that the math part isn't setup yet, but I need to get the match part working.
Here is an example:
mathlines:
dart|504.91
GI|1782.06
Gcel|194.52
clay|437.35
grado|217.77
greGCR|14.82
rp|372.54
rp2|11.92
gsg|349.92
GSxil|4520.55
addlines:
Gimet|13768994304
GSxil|394735896576
Ho|4994967296
gen|485331304448
GSctal|23482733690
Obr|88899345920
As you can see mathlines contains GSxil and so does addlines but my if (addlines.Contains) never fines the variable in addlines. Any help is always loved! Thanks.
Sorry forgot to mention that I need it to match exactly on the comparison. Also i need to split out the variable on the correct line that matches. So I would need to split out the 394735896576 this example and then append the 394735896576.
addLines.Contains(parse[0]) is going to match on the entire string; you need to match based on part. There are more efficient solutions, but a O(n^2) option is to use LINQ Any():
if (addLines.Any(l => l.StartsWith(parse[0])))
{
...
You could load all lines from addlines.txt into a dictionary and then use that to find a match for each line in mathlines.txt. This method would be much faster than what you have currently.
string[] mathlines = File.ReadAllLines(#"C:\math.txt");
string[] addlines = File.ReadAllLines(#"K:\addlines.txt");
string[] finallines = new string[mathlines.Length];
var addlinesLookup = new Dictionary<string, string>();
for (int i = 0; i < addlines.Length; i++)
{
string[] parts = addlines[i].Split('|');
if (parts.Length == 2) // Will there ever be more than 2 parts?
{
addlinesLookup.Add(parts[0], parts[1]);
}
}
for (int i = 0; i < mathlines.Length; i++)
{
string[] parts = mathlines[i].Split('|');
if (parts.Length >= 1)
{
if (addlinesLookup.ContainsKey(parts[0]))
{
finallines[i] = mathlines[i] + "|" + addlinesLookup[parts[0]] + "\n";
}
{
finallines[i] = mathlines[i] + "\n";
}
}
}
File.AppendAllLines(#"C:\final.txt", finallines, Encoding.ASCII);

Categories