I want to split an already split string - c#

Hey everyone I was hoping you could help me with my little problem. I want to split an already split string but I get the error: "The index was outside the bounds of the array".
I know that my array is too small but I don't know how to increase that in this instance. Here is the code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace Reserveringssysteem
{
public partial class Form2 : Form
{
public string naam;
public string adres;
public string datum;
public int kamernr;
public int id;
public int indexnr;
public int indexnrb;
public int indexnrc;
Kamer[] reserveringArray = new Kamer[6];
public Form2()
{
InitializeComponent();
}
private void backbtn_Click(object sender, EventArgs e)
{
this.Close();
}
private void Form2_Load(object sender, EventArgs e)
{
using (StreamReader sr = new StreamReader(#"C:\Users\Gebruiker\Desktop\OIS\Hotelsysteem\Reserveringen.txt", Encoding.Default))
{
string text = sr.ReadToEnd();
string[] lines = text.Split(';');
foreach (string s in lines)
{
id = Convert.ToInt32(s.Split(':')[0]);
indexnr = s.IndexOf(':');
naam = s.Split('/')[indexnr];
indexnra = s.IndexOf("/");
adres = s.Split('<')[indexnra];
indexnrb = s.IndexOf('<');
kamernr = Convert.ToInt32(s.Split('>')[indexnrb]);
indexnrc = s.IndexOf('>');
datum = s.Split(';')[indexnrc];
ListViewItem opbouwera = new ListViewItem(Convert.ToString(id));
opbouwera.SubItems.Add(naam);
opbouwera.SubItems.Add(adres);
opbouwera.SubItems.Add(Convert.ToString(kamernr));
opbouwera.SubItems.Add(datum);
reserveringlistview.Items.Add(opbouwera);
}
}
}
}
}
The problem occurs from the moment I start using indexnra. I hope one of you guys can help me out here.

These lines are an example of the problem:
indexnr = s.IndexOf(':');
naam = s.Split('/')[indexnr];
IndexOf will return the position within the string of the : character (e.g. 9 if the colon is the 10th character since the index is zero-based).
s.Split('/') will return an array of strings, so in that example you would be asking for the 10th string if you used a value of 9 for indexnr.
All that to say that the output of IndexOf is very likely not the appropriate index to use to find a particular string after calling Split.
It's not clear from your code what your expected output is - if you add an example input and the expected output you can probable get a better method for getting what you need.

I would go with regular expression to parse data from your lines. Because it describes layout of input string instead of working with substrings and indexes of some separators. Also I would use custom object which would store person data in nicely named fields:
public class Person
{
public int Id {get;set;}
public string Name {get;set;}
public string Address {get;set;}
public string Appartment {get;set;}
public DateTime Date {get;set;}
}
And here is regular expression which defines groups for each part of data in input string:
(?<id>\d+) first group is id - sequence of digits
: then goes separator with some spaces
(?<name>[\w\s]+) name which consists of letters and spaces
/ second separator
(?<address>[\w\d\s]+) address - letters, digits, spaces
<\s*(?<app>\d+)\s*> appartment number - digits in angular brackets
\s+ some spaces
(?<date>\d{4}-\d{2}-\d{2}) date in yyyy-dd-MM format
Usage:
var regex = new Regex(#"(?<id>\d+):(?<name>[\w\s]+)/(?<address>[\w\d\s]+)<\s*(?<app>\d+)\s*>\s*(?<date>\d{4}-\d{2}-\d{2})");
var people = from line in lines
let match = regex.Match(line)
where match.Success
select new Person {
Id = Int32.Parse(match.Groups["id"].Value),
Name = match.Groups["name"].Value.Trim(),
Address = match.Groups["address"].Value.Trim(),
Appartment = match.Groups["app"].Value,
Date = DateTime.ParseExact(match.Groups["date"].Value.Trim(),"yyyy-dd-MM",null)
};
For this sample file
1: Jeroen Wasser Poppy Paashaas/ Bijloopstraat 21< 5> 2017-31-12;2:
Bob White/ Bijloopstraat 22< 15> 2016-28-10;
You will have two people parsed:
[
{
"Id": 1,
"Name": "Jeroen Wasser Poppy Paashaas",
"Address": "Bijloopstraat 21",
"Appartment": "5",
"Date": "2017-12-31T00:00:00"
},
{
"Id": 2,
"Name": "Bob White",
"Address": "Bijloopstraat 22",
"Appartment": "15",
"Date": "2016-10-28T00:00:00"
}
]
Then grab collection of people and display them in ListView. I.e. separate data access (reading file and parsing people) with presentation (displaying data on UI).
NOTE: You can also use unnamed groups in regex. It will be less readable, but more compact:
(\d+):([\w\s]+)/([\w\d\s]+)<\s*(\d+)\s*>\s*(\d{4}-\d{2}-\d{2})
And you will have to read groups by index instead of name
Id = Int32.Parse(match.Groups[1].Value),

Related

How to split up and divide long string

I have a string of data that I would like to split up, for example my one string contains multiple characters, their stats and abilities they each have.
Full String:
"Andy,135,Punch-Kick-Bite-Headbutt|Tom,120,Bite-Slap-Dodge-Heal|Nathan,105,Bite-Scratch-Tackle-Kick"
So the above string has the characters seperated by "|" and the abilities that are seperated by "-".
I managed to divide them up by each character so its "Andy,135,Punch-Kick-Bite-Headbutt" in one index of array by doing this:
string myString = "Andy,135,Punch-Kick-Bite-Headbutt|Tom,120,Bite-Slap-Dodge-Heal|Nathan,105,Bite-Scratch-Tackle-Kick";
string[] character = myString.ToString().Split('|');
for (int i = 0; i < character.Length; i++)
{
Debug.Log("Character data: " + character[i].ToString());
}
Now How would I turn something like "Andy,135,Punch-Kick-Bite-Headbutt" and only retrieve the stats into a stat array so its "Andy,135" and pull Abilities into a string array so it is: "Punch-Kick-Bite-Headbutt"
So I would have my statArray as "Andy,135" and abilityArray as "Punch-Kick-Bite-Headbutt"
Well I would strongly recommend defining class to store that data:
public class Character
{
public string Name { get; set; }
public int Stat { get; set; }
public string[] Abilities { get; set; }
}
The I would write following LINQ:
// First split by pipe character to get each character (person)
// in raw format separately
var characters = longString.Split('|')
// Another step is to separate each property of a character,
// so it can be used in next Select method.
// Here we split by comma
.Select(rawCharacter => rawCharacter.Split(','))
// Finally we use splitted raw data and upon this, we create
// concrete object with little help of casting to int and
// assign abilities by splitting abilities list by hyphen -
.Select(rawCharacter => new Character()
{
Name = rawCharacter[0],
Stat = int.Parse(rawCharacter[1]),
Abilities = rawCharacter[2].Split('-'),
})
.ToArray();

How to extract name and version from string

I have many filenames such as:
libgcc1-5.2.0-r0.70413e92.rbt.xar
python3-sqlite3-3.4.3-r1.0.f25d9e76.rbt.xar
u-boot-signed-pad.bin-v2015.10+gitAUTOINC+1b6aee73e6-r0.02df1c57.rbt.xar
I need to reliably extract the name, version and "rbt" or "norbt" from this. What is the best way? I am trying regex, something like:
(?<fileName>.*?)-(?<version>.+).(rbt|norbt).xar
Issue is the file name and version both can have multiple semi colons. So I am not sure if there is an answer by I have two questions:
What is the best strategy to extract values such as these?
How would I be able to figure out which version is greater?
Expected output is:
libgcc1, 5.2.0-r0.70413e92, rbt
python3-sqlite3, 3.4.3-r1.0.f25d9e76, rbt
u-boot-signed-pad.bin, v2015.10+gitAUTOINC+1b6aee73e6-r0.02df1c57, rbt
This will give you what you want without using Regex:
var fileNames = new List<string>(){
"libgcc1-5.2.0-r0.70413e92.rbt.xar",
"python3-sqlite3-3.4.3-r1.0.f25d9e76.rbt.xar",
"u-boot-signed-pad.bin-v2015.10+gitAUTOINC+1b6aee73e6-r0.02df1c57.rbt.xar"
};
foreach(var file in fileNames){
var spl = file.Split('-');
string name = string.Join("-",spl.Take(spl.Length-2));
string versionRbt = string.Join("-",spl.Skip(spl.Length-2));
string rbtNorbt = versionRbt.IndexOf("norbt") > 0 ? "norbt" : "rbt";
string version = versionRbt.Replace($".{rbtNorbt}.xar","");
Console.WriteLine($"name={name};version={version};rbt={rbtNorbt}");
}
Output:
name=libgcc1;version=5.2.0-r0.70413e92;rbt=rbt
name=python3-sqlite3;version=3.4.3-r1.0.f25d9e76;rbt=rbt
name=u-boot-signed-pad.bin;version=v2015.10+gitAUTOINC+1b6aee73e6-r0.02df1c57;rbt=rbt
Edit:
Or using Regex:
var m = Regex.Match(file,#"^(?<fileName>.*)-(?<version>.+-.+)\.(rbt|norbt)\.xar$");
string name = m.Groups["fileName"].Value;
string version = m.Groups["version"].Value;
string rbtNorbt = m.Groups[1].Value;
The output will be the same. Both approaches assum that "version" has one -.
Tested following code and work perfectly with Regex. I used option Right-To-Left
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication107
{
class Program
{
static void Main(string[] args)
{
string[] inputs = {
"libgcc1-5.2.0-r0.70413e92.rbt.xar",
"python3-sqlite3-3.4.3-r1.0.f25d9e76.rbt.xar",
"u-boot-signed-pad.bin-v2015.10+gitAUTOINC+1b6aee73e6-r0.02df1c57.rbt.xar"
};
string pattern = #"(?'prefix'.+)-(?'middle'[^-][\w+\.]+-[\w+\.]+)\.(?'extension'[^\.]+).\.xar";
foreach (string input in inputs)
{
Match match = Regex.Match(input, pattern, RegexOptions.RightToLeft);
Console.WriteLine("prefix : '{0}', middle : '{1}', extension : '{2}'",
match.Groups["prefix"].Value,
match.Groups["middle"].Value,
match.Groups["extension"].Value
);
}
Console.ReadLine();
}
}
}

Including control characters in a .txt file to be read by C#

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.

Delimit a string by character unless within quotation marks C#

I need to demilitarise text by a single character, a comma. But I want to only use that comma as a delimiter if it is not encapsulated by quotation marks.
An example:
Method,value1,value2
Would contain three values: Method, value1 and value2
But:
Method,"value1,value2"
Would contain two values: Method and "value1,value2"
I'm not really sure how to go about this as when splitting a string I would use:
String.Split(',');
But that would demilitarise based on ALL commas. Is this possible without getting overly complicated and having to manually check every character of the string.
Thanks in advance
Copied from my comment: Use an available csv parser like VisualBasic.FileIO.TextFieldParser or this or this.
As requested, here is an example for the TextFieldParser:
var allLineFields = new List<string[]>();
string sampleText = "Method,\"value1,value2\"";
var reader = new System.IO.StringReader(sampleText);
using (var parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(reader))
{
parser.Delimiters = new string[] { "," };
parser.HasFieldsEnclosedInQuotes = true; // <--- !!!
string[] fields;
while ((fields = parser.ReadFields()) != null)
{
allLineFields.Add(fields);
}
}
This list now contains a single string[] with two strings. I have used a StringReader because this sample uses a string, if the source is a file use a StreamReader(f.e. via File.OpenText).
You can try Regex.Split() to split the data up using the pattern
",|(\"[^\"]*\")"
This will split by commas and by characters within quotes.
Code Sample:
using System;
using System.Linq;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
string data = "Method,\"value1,value2\",Method2";
string[] pieces = Regex.Split(data, ",|(\"[^\"]*\")").Where(exp => !String.IsNullOrEmpty(exp)).ToArray();
foreach (string piece in pieces)
{
Console.WriteLine(piece);
}
}
}
Results:
Method
"value1,value2"
Method2
Demo

Function to locate a string in a text

What would be the most efficient way of searching for a specific string in a text then displaying only a portion of it?
Here is my situation: I am currently hosting a .txt file on my server. The function I want to create would access this .txt (maybe even download for efficiency?), search an ID (ex. 300000000) and then put the name in a string (ex. Island Andrew).
Here is an example of the .txt file hosted on my server:
ID: 300000000 NAME: Island Andrew
ID: 300000100 NAME: Island Bob
ID: 300000010 NAME: Island George
ID: 300000011 NAME: Library
ID: 300000012 NAME: Cellar
I have already complete code for a similar example, however, the formatting is different and it is not in c#.
Here it is;
If anyone can help me accomplish this in c#, it would be greatly appreciated.
Thanks.
Simplistic approach without proper error handling.
Main part to look at is regex stuff.
using System;
using System.Net;
using System.Text.RegularExpressions;
using System.Collections.Generic;
class Program
{
static void Main()
{
var map = new Map();
Console.WriteLine(map[300000011]);
}
}
public class Map: Dictionary<int, string>
{
public Map()
{
WebClient wc = new WebClient()
{
Proxy = null
};
string rawData = wc.DownloadString("<insert url with data in new format here>");
PopulateWith(rawData);
}
void PopulateWith(string rawText)
{
string pattern = #"ID: (?<id>\d*) NAME: (?<name>.*)";
foreach (Match match in Regex.Matches(rawText, pattern))
{
// TODO: add error handling here
int id = int.Parse( match.Groups["id"].Value );
string name = match.Groups["name"].Value;
this[id] = name;
}
}
}
You could try this to create an array of names in C#:
Dictionary<int,String> mapDictionary;
string[] mapNames = rawData.Split(splitChar, StringSplitOptions.None);
foreach(String str in mapNames)
{
{
String mapid = str.Substring(str.IndexOf(":"));
String mapname = str.Remove(0, str.IndexOf(':') + 1);
mapDictionary.Add(Convert.ToInt32(mapid), mapname);
}
}
Remove all carets (^)
Convert all member access operators (->) to dots
Change gcnew to new Convert array to string[]
Remove private and public modifiers from class, have them on methods
explicitly (e.g. public void CacheMaps())
Change ref class to static class
Change nullptr to null
Change catch(...) to only catch
Move using namespace to the very top of the file, and replace scope resolution operator (::) to dots.
That should be about it.
simplest way would be to do a token separator between ID: 30000 and Name: Andrew Island and remove the ID and Name as such
30000, Andrew Island
Then in your C# code you would create a custom class called
public class SomeDTO {
public long ID{get; set;}
public string Name {get; set;}
}
next you would create a new List of type SomeDTO as such:
var List = new List<SomeDTO>();
then as you're parsing the txt file get a file reader and read it line by line for each line ensure that you have a token separator that separates the two Values by the comma separation.
Now you can simply add it to your new List
var tempId = line[1];
var tempName = line[2];
List.Add(new SomeDTO{ ID = tempId, Name = tempName});
Now that you have the entire list in memory you can do a bunch of searching and what not and find all things you need plus reuse it because you've already built the list.
var first = List.Where(x => x.Name.Equals("Andrew Island")).FirstOrDefault();

Categories

Resources