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();
Related
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();
}
}
}
I have n number of request urls, like below
https://{user.id}/{user.name}/testing1
https://{user.number}/{user.name}/testing1
https://{user.age}/{user.height}/testing1
https://{user.gender}/{user.name}/testing1
https://{user.height}/{user.age}/testing1
https://{user.weight}/{user.number}/testing1
I have the below test data class which has n number of values.
public class User{
public string id = "123";
public string name = "456";
public string age = "789";
public string gender = "1478";
public string height = "5454";
public string weight = "54547";
public string number = "88722";
.......
.......
.......
}
And I need to make the url
https://{user.number}/{user.name}/testing1 into
https://{88722}/{456}/testing1
In my code, I will be randomly getting a request url(from a json file) and i need to replace the parameters with the values given in the class. Can this be done? If yes please help. Thanks
I have tried using string.format() - but it doesnot work, because I am randomly getting the url and I am not sure which value needs to be replaced.
I also tried using the interpolation, but found not helpful either unless i can do something like
User user = new User();
string requesturl = GetRequestJSON(filepath);
//The requesurl variable will have a value like
//"https://{user.id}/{user.name}/testing1";
string afterreplacing = $+requesturl;
Update: After some more googling, I found out that my question is very much similar to this. I can use the first answer's alternate option 2 as a temporary solution.
Unless I am clearly missing the point (and lets assume all the values are in when you do new user(); )
You just want
User user = new User();
string requesturl = $"https://{user.id}/{user.name}/testing1";
then all the variables like {user.id} are replaced with values. such as "https://88722/456/testing1" if you need the {} in there you can add extra {{ to go with such as
string requesturl = $"https://{{{user.id}}}/{{{user.name}}}/testing1";
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),
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.
The object has 139 fields; the string initializer may have some or all of these fields. It is formatted like this: "FirstName":"Bart","LastName":"Simpson","Company":"Fat Tony's","Address":"55 Maple Drive" etc. I could just look for the fields like this:
if (initializerString.contains("FirstName:")
FirstName="get the next series of chars until the ", or end of string
and so forth. But is there a more compact way to do this?
Seeing as your format is incredibly similar to JSON (except for the lack of braces, actually), as people commented you'll fare better by using JSON.NET.
If you have complete control over this string, just transform it into a json and deserialize it:
JsonConvert.DeserializeObject<YourClass>(yourString);
It will automatically set the correct properties while deserializing your data.
In case you don't have control of this format, and you need to parse it anyway, just put up the braces and you're good:
JsonConvert.DeserializeObject<YourClass>("{" + yourString + "}");
And if you don't have a specific class for this, you can also replace YourClass for a Dictionary<string,object>
You'll find this library as Newtonsoft.Json, and I believe it's the most popular library for dealing with JSON data.
I've made a working example so you can see it in action (note that I kept your string format, but please try to use straight json):
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public class Information
{
public string FirstName{get;set;}
public string LastName{get;set;}
public string Company{get;set;}
public string Address{get;set;}
}
public static void Main()
{
string myObject = "\"FirstName\":\"Bart\",\"LastName\":\"Simpson\",\"Company\":\"Fat Tony's\",\"Address\":\"55 Maple Drive\"";
var converted = JsonConvert.DeserializeObject<Dictionary<string, object>>("{"+myObject+"}");
var converted2 = JsonConvert.DeserializeObject<Information>("{"+myObject+"}");
Console.WriteLine(String.Join("\n", converted.Select(c=> c.Key + ": " + c.Value)));
Console.WriteLine(converted2.FirstName);
}
}
And here's a bonus fiddle:
https://dotnetfiddle.net/fudUYZ
Using regex you could do:
string firstName = Regex.Match(yourstring,#"(?<=""FirstName"":"").*?(?="")").Value;
However this really looks like a json string and there are easier ways to get your data.
You could create a Dictionary<string,object> dict in which there is the name of the property and the property.
You can then split the string,
//Remove the "
initializerString = initializerString.Replace('"', '');
//Split by ,
var tmp = initializerString.Split(",");
//Foreach pair key/value split by :
foreach( var x in tmp){
var tmp2=x.Split(":");
//Assign the value to the property in the Dictionary
dict[tmp2[0]]=tmp2[1];
}
Warning. Since I don't actually know what you're using this code for, this is more of a general idea than working code.