Need some ideas how to solve this problem.
I have a template file what describes the line in the text file. For example:
Template
[%f1%]|[%f2%]|[%f3%]"[%f4%]"[%f5%]"[%f6%]
Text file
1234|1234567|123"12345"12"123456
Now i need to read in the fields from the text file. In the template file fields are described with [%some name%]. Allso in the template file there is set what the field separators are, in this example here there are | and ". The lenght of the fields can change through different files but the separators will stay the same. What would be the best way to read in the template and by template read in the text file?
EDIT: Text file has multiple rows, like this:
1234|1234567|123"12345"12"123456"\r\n
1234|field|123"12345"12"asdasd"\r\n
123sd|1234567|123"asdsadf"12"123456"\r\n
45gg|somedata|123"12345"12"somefield"\r\n
EDIT2: Ok, lets make it even harder. Some fields can contain binary data and i know the starting and end position of the binary data field. I should be able to mark those fields in the template and then the parser will know that this field is binary. How to solve this problem?
I would create a regex based on the template and then parse the text file using that:
class Parser
{
private static readonly Regex TemplateRegex =
new Regex(#"\[%(?<field>[^]]+)%\](?<delim>[^[]+)?");
readonly List<string> m_fields = new List<string>();
private readonly Regex m_textRegex;
public Parser(string template)
{
var textRegexString = '^' + TemplateRegex.Replace(template, Evaluator) + '$';
m_textRegex = new Regex(textRegexString);
}
string Evaluator(Match match)
{
// add field name to collection and create regex for the field
var fieldName = match.Groups["field"].Value;
m_fields.Add(fieldName);
string result = "(.*?)";
// add delimiter to the regex, if it exists
// TODO: check, that only last field doesn't have delimiter
var delimGroup = match.Groups["delim"];
if (delimGroup.Success)
{
string delim = delimGroup.Value;
result += Regex.Escape(delim);
}
return result;
}
public IDictionary<string, string> Parse(string text)
{
var match = m_textRegex.Match(text);
var groups = match.Groups;
var result = new Dictionary<string, string>(m_fields.Count);
for (int i = 0; i < m_fields.Count; i++)
result.Add(m_fields[i], groups[i + 1].Value);
return result;
}
}
You can parse the template using regular expressions. An expression like this will match each field definition and separator:
Match m = Regex.Match(template, #"^(\[%(?<name>.+?)%\](?<separator>.)?)+$")
The match will contain two named groups for (name and separator), each of which will contain a number of captures for each time they matched in the input string. In your example, the separator group would have one less capture than the name group.
You can then iterate over the captures, and use the results to extract the fields from the input string and store the values, like this:
if( m.Success )
{
Group name = m.Groups["name"];
Group separator = m.Groups["separator"];
int index = 0;
Dictionary<string, string> fields = new Dictionary<string, string>();
for( int x = 0; x < name.Captures.Count; ++x )
{
int separatorIndex = input.Length;
if( x < separator.Captures.Count )
separatorIndex = input.IndexOf(separator.Captures[x].Value, index);
fields.Add(name.Captures[x].Value, input.Substring(index, separatorIndex - index));
index = separatorIndex + 1;
}
// Do something with results.
}
Obviously in a real program you'd have to account for invalid input and such, which I didn't do here.
I would do this with a few lines of code. Loop through your template row, grabbing all text between "[" as the variable name and everything else as a terminator. Read all the text to the terminal, assign it to the variable name, repeat.
1- Use API for that sscanf(line, format, __arglist) check here
2- Use string split Like:
public IEnumerable<int> GetDataFromLines(string[] lines)
{
//handle the output data
List<int> data = new List<int>();
foreach (string line in lines)
{
string[] seperators = new string[] { "|", "\"" };
string[] results = line.Split(seperators, StringSplitOptions.RemoveEmptyEntries);
foreach (string result in results)
{
data.Add(int.Parse(result));
}
}
return data;
}
Test it with line:
line = "1234|1234567|123\"12345\"12\"123456";
string[] lines = new string[] { line };
GetDataFromLines(lines);
//output list items are:
1234
1234567
123
12345
12
123456
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 have a text-file with many lines, each line looks like this:
"string string double double" between each value is a space. I'd like to read out the first string and last double of every line and put these two values in a existing list. That is my code so far, but it doesnt really work.
private void bOpen_Click(object sender, RoutedEventArgs e)
{
bool exists = File.Exists(#"C:\Users\p2\Desktop\Liste.txt");
if (exists == true)
{
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(#"C:\Users\p2\Desktop\Liste.txt"))
{
Vgl comp = new Vgl();
comp.name = Abzahlungsdarlehenrechner.zgName;
comp.gErg = Abzahlungsdarlehenrechner.zgErg;
GlobaleDaten.VglDaten.Add(comp);
int i = 0;
string line = File.ReadLines(#"Liste.txt").Skip(0).Take(1).First();
while ((line = sr.ReadLine()) != null)
{
sb.Append((line));
listBox.Items.Add(line);
GlobaleDaten.VglDaten.Add(comp);
i++;
}
}
}
I have already read this, but it didnt help How do I read specific value[...]
You can try Linq:
var source = File
.ReadLines(#"C:\Users\p2\Desktop\Liste.txt")
.Select(line => line.Split(' '))
.Select(items => new Vgl() {
name = items[0],
gErg = double.Parse(items[3])
});
// If you want to add into existing list
GlobaleDaten.VglDaten.AddRange(source);
// If you want to create a new list
//List<Vgl> list = source.ToList();
how about
List<Vgl> Result = File.ReadLines(#"C:\Users\p2\Desktop\Liste.txt")
.Select(x => new Vgl()
{
name = x.Split(' ').First(),
gErg = decimal.Parse(x.Split(' ').Last(), NumberStyles.AllowCurrencySymbol)
})
.ToList();
I would avoid storing money within doulbe values because this could lead to rounding issues. Use decimal instead. Examples here: Is a double really unsuitable for money?
You can use:
string[] splitBySpace = line.Split(' ');
string first = splitBySpace.ElementAt(0);
decimal last = Convert.ToDecimal(splitBySpace.ElementAt(splitBySpace.Length - 1));
Edit : To Handle Currency symbol:
string[] splitBySpace = line.Split(' ');
string pattern = #"[^0-9\.\,]+";
string first = splitBySpace.ElementAt(0);
string last = (new Regex(pattern)).Split(splitBySpace.ElementAt(splitBySpace.Length - 1))
.FirstOrDefault();
decimal lastDecimal;
bool success = decimal.TryParse(last, out lastDecimal);
I agree with #Dmitry and fubo, if you are looking for alternatives, you could try this.
var source = File
.ReadLines(#"C:\Users\p2\Desktop\Liste.txt")
.Select(line =>
{
var splits = line.Split(' '));
return new Vgl()
{
name = splits[0],
gErg = double.Parse(splits[3])
};
}
use string.split using space as the delimiter on line to the string into an array with each value. Then just access the first and last array element. Of course, if you aren't absolutely certain that each line contains exactly 4 values, you may want to inspect the length of the array to ensure there are at least 4 values.
reference on using split:
https://msdn.microsoft.com/en-us/library/ms228388.aspx
Read the whole file as a string.
Split the string in a foreach loop using \r\n as a row separator. Add each row to a list of strings.
Iterate through that list and split again each record in another loop using space as field separator and put them into another list of strings.
Now you have all the four fields containig one row. Now just use First and Last methods to get the first word and the last number.
I'm trying to get the contents of a Text File, delete a line of string, and re-write back to the Text File, deleting the line of string. I'm using StreamReader to get the text, importing into a List, removing the string, then rewriting using StreamWriter. My problems arises somewhere around the removing or writing of the string. Instead of writing back the existing, non deleted contents to the text file, all the text is replaced with :
System.Collections.Generic.List`1[System.String]
My code for this function is as follows:
{
for (int i = deleteDevice.Count - 1; i >= 0; i--)
{
string split = "";
//deleteDevice[i].Split(',').ToString();
List<string> parts = split.Split(',').ToList();
if (parts.Contains(deviceList.SelectedItem.ToString()))
{
deleteDevice.Remove(i.ToString());
}
}
if (deleteDevice.Count != 0) //Error Handling
{
writer.WriteLine(deleteDevice);
}
}
deviceList.Items.Remove(deviceList.SelectedItem);
}
I would just like the script to write back any string that isn't deleted (If there is any), without replacing it. Any help is appreciated, Cheers
You can read all the info from the text file into a list and then remove from the list and rewrite that to the text file.
I would change the list 'deleteDevice' to store a string array instead and use the code below to determine which item to remove.
List<int> toRemove = new List<int>();
int i = 0;
/*build a list of indexes to remove*/
foreach (string[] x in deleteDevice)
{
if (x[0].Contains(deviceList.SelectedItem.ToString()))
{
toRemove.Add(i);
}
i++;
}
/*Remove items from list*/
foreach (int fd in toRemove)
deleteDevice.RemoveAt(fd);
/*write to text file*/
using (StreamWriter writer = new StreamWriter("Devices.txt"))
{
if (deleteDevice.Count != 0) //Error Handling
{
foreach (string[] s in deleteDevice)
{
StringBuilder sb = new StringBuilder();
for (int fds = 0; fds < s.Length; fds++ )
{
sb.Append(s[fds] + ",");
}
string line = sb.ToString();
writer.WriteLine(line.Substring(0, line.Length - 1));
}
}
}
This isn't the best solution but should work for your needs. There's probably a much easier way of doing this.
The problem is in the following line:
writer.WriteLine(deleteDevice);
You're writing deleteDevice (I assume this is of type List). List.ToString() returns the type name of the list, because this has no specific implementation. What you want is
foreach(String s in deleteDevice)
{
writer.WriteLine(s);
}
Problems
deleteDevice is of type List<string>, and because it also doesn't overload ToString(), the default behaviour of List<string>.ToString() is to return the name of the type.
Hence your line writer.WriteLine(deleteDevice); writes the string System.Collections.Generic.List1[System.String]`.
Other than that, there are many things wrong with your code...
For example, you do this:
string split = "";
and then on the line afterwards you do this:
List<string> parts = split.Split(',').ToList();
But because split is "", this will always return an empty list.
Solution
To simplify the code, you could first write a helper method that will remove from a file all the lines that match a specified predicate:
public void RemoveUnwantedLines(string filename, Predicate<string> unwanted)
{
var lines = File.ReadAllLines(filename);
File.WriteAllLines(filename, lines.Where(line => !unwanted(line)));
}
Then you can write the predicate something like this (this might not be quite right; I don't really know exactly what your code is doing because it's not compilable and omits some of the types):
string filename = "My Filename";
string deviceToRemove= deviceList.SelectedItem.ToString();
Predicate<string> unwanted = line =>
line.Split(new [] {','})
.Contains(deviceToRemove);
RemoveUnwantedLines(filename, unwanted);
If I have a RichTextBox that is loaded from a file containg:
TEXT MORETEXT 10.505 100.994 0
TEXT MORETEXT -5.132 -12.994 90
TEXT MORETEXT 100.001 -8.994 270
and a TextBox that contains whatever the user enters in the textbox. Let's say the user enters "10.005".
My question is, how do I take this value and add it to the 3rd column containing the values 10.505, -5.132, 100.001. Once it is added, I would like to take the value and Replace the old value in the string. SO the updated RichTextBox would look like this.
TEXT MORETEXT 20.510 100.994 0
TEXT MORETEXT 4.873 -12.994 90
TEXT MORETEXT 110.006 -8.994 270
RIGHT NOW I am able to strip the strings from the RichTextBox by using this code:
private void calculateXAndYPlacementTwo()
{
// Reads the lines in the file to format.
var fileReader = File.OpenText(filePath);
// Creates a list for the lines to be stored in.
var fileList = new List<string>();
// Adds each line in the file to the list.
while (true)
{
var line = fileReader.ReadLine();
if (line == null)
break;
fileList.Add(line);
}
// Creates new lists to hold certain matches for each list.
var xyResult = new List<string>();
var xResult = new List<string>();
var yResult = new List<string>();
// Iterate over each line in the file and extract the x and y values
fileList.ForEach(line =>
{
Match xyMatch = Regex.Match(line, #"(?<x>-?\d+\.\d+)\s+(?<y>-?\d+\.\d+)");
if (xyMatch.Success)
{
// grab the x and y values from the regular expression match
String xValue = xyMatch.Groups["x"].Value;
String yValue = xyMatch.Groups["y"].Value;
// add these two values, separated by a space, to the "xyResult" list.
xyResult.Add(String.Join(" ", new[]{ xValue, yValue }));
// Adds the values into the xResult and yResult lists.
xResult.Add(xValue);
yResult.Add(yValue);
// Place the 'X' and 'Y' values into the proper RTB.
xRichTextBox.AppendText(xValue + "\n");
yRichTextBox.AppendText(yValue + "\n");
}
});
}
To get the values in the xRichTextBox looking like:
10.505
-5.132
100.001
and the yRichTextBox looking like:
100.994
-12.994
-8.994
But I do not know how to turn those into values that can have addition used on them...
EDIT:
I have messed around with this some more... I am now using this code (below) to try to accomplish what I need it to do. This is only for the "X" (3rd column).
HOWEVER THIS CODE IS NOT WORKING (it concats the user input to the end of the xRichTextBox instead of mathematically adding it to each line..)
The xDisplacementTextBox is the user input and the xRichTextBox is the stripped values from the main string.
StringBuilder stringBuilder = new StringBuilder();
string[] Lines = xRichTextBox.Text.Split('\n');
double d = double.Parse(xDisplacementTextBox.Text);
for(int i = 0; i < Lines.Length; ++i)
{
string newThing = double.Parse((Lines[i]) + d).ToString();
stringBuilder.AppendLine(newThing);
}
xRichTextBox.Text = stringBuilder.ToString();
This is also not letting me enter in values that have decimals (ie. 50.005)..
Look at double.Parse - as in
double x = double.Parse(xValue);
To expand, and do your work for you...
double d = double.Parse(xDisplacementTextBox.Text);
string[] Lines = xRichTextBox.Text.Split('\n');
for(int i = 0; i < Lines.Length; ++i)
{
Match lineMatch = Regex.Match(lines[i], #"^(?<p>.*)(?<x>-?\d+\.\d+)(?<y>\s+-?\d+\.\d+\s+-?\d+\.\d+)$");
if (lineMatch.Success)
{
double xValue = double.Parse(lineMatch.Groups["x"].Value) + d;
lines[i] = lineMatch.Groups["p"] + xValue + lineMatch.Groups["p"];
}
}
xRichTextBox.Text = string.Join(lines, '\n');
Too many strings and not enough data structures.
This looks like a data structure:
TEXT MORETEXT 10.505 100.994 0
TEXT MORETEXT -5.132 -12.994 90
TEXT MORETEXT 100.001 -8.994 270
So, create a class that holds
"Text" string
"MoreText" string
10.505 - double (let's call this prop1)
100.994 - double
0 - int
I'm speculating on the data values here.
Load the List<> of your class into memory.
Then, apply the text box value to your list of object every time the value changes.
PsuedoCode:
foreach(class c in List<>)
{
c.prop1 = c.prop1 + (double)Textbox.value;
}
Override ToString() in your class and display the object as needed in the rich text box.
Personally I would use a list box to display the objects.
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];