I am reading txt file, and I would like to separate it into some parts. This example of my TXT file:
"Part error(1) devic[3].data_type(2)"
"Escape error(3) device[10].data_type(12)"
I want to achieve such a situation that, when I have first word "Part" I would like to have enum type for it, and in switch I would like to call some function that will work with whole line, and on the other hand, when I will have first word "Escape", there will another case in switch that will call other functions. How can I do it? This is my code so far:
class Row
{
public enum Category { Part, Escape }
public string Error{ get; set; }
public string Data_Type { get; set; }
public string Device{ get; set; }
}
public object HandleRegex(string items)
{
Row sk = new Row();
Regex r = new Regex(#"[.]");
var newStr = r.Replace(items, #" ");
switch(this.category)
{
case Category.Part:
//I want to call here function HandlePart with my line as a parameter
HandlePart(newStr);
break;
case Category.Escape:
//Here I want to call Function HandleEscape for line with "Escape" word
HandleEscape(newStr);
break;
}
}
public object HandleRegex(string items)
{
Regex r = new Regex(#"[.]");
var newStr = r.Replace(items, #" ");
try {
category = (Category) new EnumConverter(typeof(Category)).ConvertFromString(items.Split(new string[]{" "},StringSplitOptions.RemoveEmptyEntries)[0]);
}
catch {
throw new ArgumentException("items doesn't contain valid prefix");
}
switch(category)
{
case Category.Part:
HandlePart(newStr);
break;
case Category.Escape:
HandleEscape(newStr);
break;
}
}
You could use TryParse :
Category outCategory;
Enum.TryParse(this.category, out outCategory)
switch(outCategory)
{
case Category.Part:
//I want to call here function HandlePart with my line as a parameter
HandlePart(newStr);
break;
case Category.Escape:
//Here I want to call Function HandleEscape for line with "Escape" word
HandleEscape(newStr);
break;
default:
// Needs to be handled
}
You can create Dictionary<Category, Action<string>> and then use it to call code according to category:
static void Main(string[] args)
{
var input = #"Part error(1) devic[3].data_type(2)
Escape error(3) device[10].data_type(12)";
var functions = new Dictionary<Category, Action<string>>()
{
{ Category.Part, HandlePart},
{ Category.Escape, HandleEscape }
};
foreach (var line in input.Split(new [] {Environment.NewLine }, StringSplitOptions.None))
{
Category category;
if(Enum.TryParse<Category>(line.Substring(0, line.IndexOf(' ')), out category) && functions.ContainsKey(category))
functions[category](line);
}
}
static void HandlePart(string line)
{
Console.WriteLine("Part handler call");
}
static void HandleEscape(string line)
{
Console.WriteLine("Escape handler call");
}
Output of program above:
Part handler call
Escape handler call
if you read the file line by line then you can do
string str = file.ReadLine();
string firstWord = str.substring(0, str.IndexOf(' ')).Trim().ToLower();
now you have your first word you can do
switch(firstWord){
case "escape":// your code
case "Part":// your code
}
Related
I have an error on this part foreach( string code in text ) the error is saying can not convert char to string. how do i convert this to string
my list
class MyCipher : ICipherDecipher
{
private List<Code> alphabet;
public MyCipher()
{
alphabet = new List<Code>();
alphabet.Add(new Code("Aca", " 1234"));
alphabet.Add(new Code("Bb", " 1234"));
alphabet.Add(new Code("C1", " 1234"));
}
this is where im gtting the error on the foreach part , its saying cant convert to string from char
private string Cipher( string text )
{
StringBuilder result = new StringBuilder();
foreach( string code in text )
{
Code element =
alphabet.Where(x => x.MyCode == code.ToString()).SingleOrDefault();
if ( element != null)
{
result.Append(element.MyDecoded);
}
}
return result.ToString();
}
Edited code
class MyCipher : ICipherDecipher
{
private List<Code> alphabet;
public MyCipher()
{
alphabet = new List<Code>();
alphabet.Add(new Code("4", " take 4"));
alphabet.Add(new Code(" ", " a"));
alphabet.Add(new Code("4d", " for 4 days"));
}
public string Cipher(params string[] codes)
{
StringBuilder result = new StringBuilder();
foreach (string code in codes)
{
Code element =
alphabet.Where(x => x.MyCode == code).SingleOrDefault();
if (element != null)
{
result.Append(element.MyDecoded);
}
}
return result.ToString();
}
class Code
{
public string MyCode;
public string MyDecoded;
public Code(string code, string decode)
{
MyCode = code;
MyDecoded = decode;
}
}
}
Button code
public partial class Form1 : Form
{
private ICipherDecipher myCipher;
public Form1()
{
myCipher = new MyCipher();
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string textToBeCiphered = textBox1.Text;
string textCiphered = myCipher.Cipher(textToBeCiphered);
textBox2.Text = textCiphered;
}
}
Change this part of your code:
foreach( char code in text )
{
Code element =
alphabet.Where(x => x.MyCode == code.ToString()).SingleOrDefault();
if ( element != null)
{
result.Append(element.MyDecoded);
}
}
Then everywhere you use code you convert it to string by using code.ToString()
You're iterating over a string (text), so the code variable should be a char, not a string:
foreach( char code in text )
Alternatively, you can use var:
foreach( var code in text )
to make the compiler automatically assign the type it thinks it should be, but you should still be aware of the type it actually is because it impacts the operations the variable supports.
Going a bit deeper into it, some other languages (especially dynamic languages like JavaScript and Python) don't make this distinction but C# (like most other C-like languages) has a char datatype that holds a single element of the text. In the case of C#, that type holds a UTF-16 code unit. (Which is not always the same as a user-visible character, but that's a whole other story).
Based on the discussion in the comments, it seems you want to match the whole string instead of characters of the string. In that case you have two options, depending on what you want to do:
If you want the Cypher method to receive a single string and get the code that matches that string, just get rid of the loop and match against the parameter directly:
private string Cipher( string code )
{
StringBuilder result = new StringBuilder();
Code element =
alphabet.Where(x => x.MyCode == code).SingleOrDefault();
if ( element != null)
{
result.Append(element.MyDecoded);
}
return result.ToString();
}
Alternatively, if you want to pass multiple strings and process all of them, pass an IEnumerable<string> (from System.Collections.Generic) or a derived type such as string[]:
private string Cipher( IEnumerable<string> codes )
{
StringBuilder result = new StringBuilder();
foreach( string code in codes )
{
Code element =
alphabet.Where(x => x.MyCode == code).SingleOrDefault();
if ( element != null)
{
result.Append(element.MyDecoded);
}
}
return result.ToString();
}
// Usage examples:
private string CallSite() {
// (These are just some of the options, other enumerable types
// will work as well as long as they enumerate over strings)
return Cypher( new[] { "Aca", "Bb" });
return Cypher( new List<string> { "Aca", "Bb" });
}
Alternatively, if you replace IEnumerable<string> with params string[] you can call the function as if it had any number of string parameters:
private string Cipher( params string[] codes )
{
// ... (as above)
}
// Usage example:
private string CallSite() {
return Cypher( "Aca", "Bb" );
}
In any of these cases, you can get replace code.ToString() with simply code because it already is a string (and "string".ToString() just returns itself).
I am making a web service for an app with Tesseract Ocr 3.02.
I want to create variables on depending of how many informations I get on the business card and after that classify information from a string.
For example:
Tel. +496123456789$Mobil +49123456789$kai.kalsbach#gmail.com$www.google.com$Kai Kalsbach$Muster Str 1a$40599 Düsseldorf$"
And then like this:
-Telephone number
-First Name
-Last Name
-Email
-Address
That was my first idea:
string endText1 = text.Split('$')[0];
string endText2 = text.Split('$')[1];
string endText3 = text.Split('$')[2];
string endText4 = text.Split('$')[3];
string endText5 = text.Split('$')[4];
string endText6 = text.Split('$')[5];
string endText7 = text.Split('$')[6];
string endText8 = text.Split('$')[7];
and after that i would classify the variables.
but in many cases I get the following exception because the number of informations can vary depending of business card.
System.IndexOutOfRangeException: Index was outside the bounds of the array c#
The IndexOutOfRangeException exception is thrown because the code tries to access an item outside the array length.
My proposition : I created formattedArray with contains always 8 items and I copied the splited array to this formattedArray. With that, you have no more IndexOutOfRangeException because the item missing in text.Split('$') is null in formattedArray
var a = text.Split('$');
var formattedArray = new string[8];
Array.Copy(a, formattedArray, a.Length);
string endText1 = formattedArray [0];
string endText2 = formattedArray [1];
string endText3 = formattedArray [2];
string endText4 = formattedArray [3];
string endText5 = formattedArray [4];
string endText6 = formattedArray [5];
string endText7 = formattedArray [6];
string endText8 = formattedArray [7];
string[] Splitted = text.Split('$');
And you mentioned you want to make a decision based on the number of elements the split spits out
int Count = Splitted.Length;
switch(Count)
{ case 0: //DoStuff
break;
....
default:
break;
}
In your case, it is better to use the following:
string[] stringList = text.Split('$');
foreach(string val in stringList)
{
//your logic.
}
You can split the string once using the .Split method.
Then afterwards run it in a foreach or for loop. I believe your logic is based on the amount of strings, so you are looking for a 'for' loop.
string[] split = text.Split('$');
for (int i = 0; i < split.Length; i++)
{
var text = split[i];
// Your logic here...
switch (i) // for logic based on the index of the string
{
case 0:
// do something
break;
case 1:
// do something
break;
}
}
The IndexOutOfRangeException exception is thrown because the code tries to access the 8th item in a 7-item array :
string endText8 = text.Split('$')[7];
Indexes in .NET collections are 0-based which means 7 refers to the 8th element.
By default, String.Split will return empty fields as well. This means that either the string isn't the same as the one posted here, or that the StringSplitOptions.RemoveEmptyEntries was used
String.Split returns a string array that can be stored in a string[] variable. There's no need to repeat String.Split, or use multiple variables :
var items = text.Split(new[]{'$'},StringSplitOptions.RemoveEmptyEntries);
Creating a class from this array is simple enough that you probably don't need to create a custom parser :
class Record
{
public string Telephone {get;set;}
...
}
var items = text.Split('$');
var record=new Record
{
Telephone=items[0],
Mobile=items[1],
...
};
Another easy way to do that is to use a try, then all variables will be created until the index has reached its maximum.
string[] strArray = text.Split('$');
Try {
string endText1 = strArray[0];
string endText2 = strArray[1];
string endText3 = strArray[2];
string endText4 = strArray[3];
string endText5 = strArray[4];
string endText6 = strArray[5];
string endText7 = strArray[6];
string endText8 = strArray[7];
}
catch
{
//nothing
}
Create factory and recognizers
public class PhoneItem : IItem
{
public PhoneItem(string text)
{
// some code
}
}
public interface IRecognizer
{
IItem Recognize(int index, string text);
}
public class PhoneRecognizer : IRecognizer
{
public IItem Recognize(int index, string text)
{
return index == 0 ? new PhoneItem(text) : null;
}
}
public class ItemFactory
{
private IEnumerable<IRecognizer> _recognizers = new []
{
new PhoneRecognizer(),
new FullNameRecognizer()
};
public IItem CreateItem(int index, string text)
{
foreach (var rec in _recognizers)
{
var item = rec.Recognize(index, text);
if (item != null)
{
return item;
}
}
throw new Exception("Item not recognized");
}
}
Split string to pieces
var parts = text.Split('$');
Use the factory to create objects
var factory = new ItemFactory();
var items = new List<IItem>();
for (int i = 0; i < parts.Length; i++)
{
items.Add(factory.CreateItem(i, parts[i]));
}
// do whatever you wants
i just started learning C# and all these Classes, Objects and Methods still confuse me. So i came up with this piece of code, its working fine but i got the feeling i do things that i shouldnt do. Can some C# experts have a look over it?
Is this a good way to take Input and then work with it?
static void Main(string[] args)
{
input crInput = new input();
Console.WriteLine(crInput.evalInput(Console.ReadLine()));
}
private class input
{
public string InputString { get; set; }
public string evalInput(string _input)
{
string result = "";
string input = _input;
if (input == "1")
{
result = "1";
}
else if (input == "2")
{
result = "2";
}
else if (input == "3")
{
result = "3";
}
else
{
result = "NOTHING";
}
return result;
}
}
As I see in your code, several details can be fixed:
First, you can simplify the CRInput assignment by substituting the name of the class for the keyword var: var CRInput = new Input(); in C# var supports almost all types of data.
Another thing to take into account is that the names of the classes should start with Capital, this is the correct way.
Another thing is that you do not have to create another string input if you already have one previously.
The switch keyword simplifies the repetitive use of else if you can see, and it is recommended that each sentence have its own line.
static void Main(string[] args)
{
//var supports almost all types of data, which simplifies the object declaration process
var CRInput = new Input();
Console.WriteLine(CRInput.EvalInput(Console.ReadLine()));
}
//Class names must start with capitals
private class Input
{
public string InputString { get; set; }
public string EvalInput(string input)
{
//Initialize result with the value -> ""
string result = string.Empty;
//The switch structure simplifies and shortens the repetitive use of else if
switch (input)
{
//if
case "1":
result = "1";
break;
//else if
case "2":
result = "2";
break;
//else if
case "3":
result = "3";
break;
//else
default:
result = "NOTHING";
break;
}
return result;
}
}
I have a .csv file(I have no control over the data) and for some reason it has everything in quotes.
"Date","Description","Original Description","Amount","Type","Category","Name","Labels","Notes"
"2/02/2012","ac","ac","515.00","a","b","","javascript://"
"2/02/2012","test","test","40.00","a","d","c",""," "
I am using filehelpers and I am wondering what the best way to remove all these quotes would be? Is there something that says "if I see quotes remove. If no quotes found do nothing"?
This messes with the data as I will have "\"515.00\"" with unneeded extra quotes(especially since I want in this case it to be a decimal not a string".
I am also not sure what the "javascript" is all about and why it was generated but this is from a service I have no control over.
edit
this is how I consume the csv file.
using (TextReader textReader = new StreamReader(stream))
{
engine.ErrorManager.ErrorMode = ErrorMode.SaveAndContinue;
object[] transactions = engine.ReadStream(textReader);
}
You can use the FieldQuoted attribute described best on the attributes page here. Note that the attribute can be applied to any FileHelpers field (even if it type Decimal). (Remember that the FileHelpers class describes the spec for your import file.. So when you mark a Decimal field as FieldQuoted, you are saying in the file, this field will be quoted.)
You can even specify whether or not the quotes are optional with
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
Here is a console application which works with your data:
class Program
{
[DelimitedRecord(",")]
[IgnoreFirst(1)]
public class Format1
{
[FieldQuoted]
[FieldConverter(ConverterKind.Date, "d/M/yyyy")]
public DateTime Date;
[FieldQuoted]
public string Description;
[FieldQuoted]
public string OriginalDescription;
[FieldQuoted]
public Decimal Amount;
[FieldQuoted]
public string Type;
[FieldQuoted]
public string Category;
[FieldQuoted]
public string Name;
[FieldQuoted]
public string Labels;
[FieldQuoted]
[FieldOptional]
public string Notes;
}
static void Main(string[] args)
{
var engine = new FileHelperEngine(typeof(Format1));
// read in the data
object[] importedObjects = engine.ReadString(#"""Date"",""Description"",""Original Description"",""Amount"",""Type"",""Category"",""Name"",""Labels"",""Notes""
""2/02/2012"",""ac"",""ac"",""515.00"",""a"",""b"","""",""javascript://""
""2/02/2012"",""test"",""test"",""40.00"",""a"",""d"",""c"","""","" """);
// check that 2 records were imported
Assert.AreEqual(2, importedObjects.Length);
// check the values for the first record
Format1 customer1 = (Format1)importedObjects[0];
Assert.AreEqual(DateTime.Parse("2/02/2012"), customer1.Date);
Assert.AreEqual("ac", customer1.Description);
Assert.AreEqual("ac", customer1.OriginalDescription);
Assert.AreEqual(515.00, customer1.Amount);
Assert.AreEqual("a", customer1.Type);
Assert.AreEqual("b", customer1.Category);
Assert.AreEqual("", customer1.Name);
Assert.AreEqual("javascript://", customer1.Labels);
Assert.AreEqual("", customer1.Notes);
// check the values for the second record
Format1 customer2 = (Format1)importedObjects[1];
Assert.AreEqual(DateTime.Parse("2/02/2012"), customer2.Date);
Assert.AreEqual("test", customer2.Description);
Assert.AreEqual("test", customer2.OriginalDescription);
Assert.AreEqual(40.00, customer2.Amount);
Assert.AreEqual("a", customer2.Type);
Assert.AreEqual("d", customer2.Category);
Assert.AreEqual("c", customer2.Name);
Assert.AreEqual("", customer2.Labels);
Assert.AreEqual(" ", customer2.Notes);
}
}
(Note, your first line of data seems to have 8 fields instead of 9, so I marked the Notes field with FieldOptional).
Here’s one way of doing it:
string[] lines = new string[]
{
"\"Date\",\"Description\",\"Original Description\",\"Amount\",\"Type\",\"Category\",\"Name\",\"Labels\",\"Notes\"",
"\"2/02/2012\",\"ac\",\"ac\",\"515.00\",\"a\",\"b\",\"\",\"javascript://\"",
"\"2/02/2012\",\"test\",\"test\",\"40.00\",\"a\",\"d\",\"c\",\"\",\" \"",
};
string[][] values =
lines.Select(line =>
line.Trim('"')
.Split(new string[] { "\",\"" }, StringSplitOptions.None)
.ToArray()
).ToArray();
The lines array represents the lines in your sample. Each " character must be escaped as \" in C# string literals.
For each line, we start off by removing the first and last " characters, then proceed to split it into a collection of substrings, using the "," character sequence as the delimiter.
Note that the above code will not work if you have " characters occurring naturally within your values (even if escaped).
Edit: If your CSV is to be read from a stream, all your need to do is:
var lines = new List<string>();
using (var streamReader = new StreamReader(stream))
while (!streamReader.EndOfStream)
lines.Add(streamReader.ReadLine());
The rest of the above code would work intact.
Edit: Given your new code, check whether you’re looking for something like this:
for (int i = 0; i < transactions.Length; ++i)
{
object oTrans = transactions[i];
string sTrans = oTrans as string;
if (sTrans != null &&
sTrans.StartsWith("\"") &&
sTrans.EndsWith("\""))
{
transactions[i] = sTrans.Substring(1, sTrans.Length - 2);
}
}
I have the same predicament and I replace the quotes when I load the value into my list object:
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
LoadCSV();
}
private void LoadCSV()
{
List<string> Rows = new List<string>();
string m_CSVFilePath = "<Path to CSV File>";
using (StreamReader r = new StreamReader(m_CSVFilePath))
{
string row;
while ((row = r.ReadLine()) != null)
{
Rows.Add(row.Replace("\"", ""));
}
foreach (var Row in Rows)
{
if (Row.Length > 0)
{
string[] RowValue = Row.Split(',');
//Do something with values here
}
}
}
}
}
}
This code might help which I developed:
using (StreamReader r = new StreamReader("C:\\Projects\\Mactive\\Audience\\DrawBalancing\\CSVFiles\\Analytix_ABC_HD.csv"))
{
string row;
int outCount;
StringBuilder line=new StringBuilder() ;
string token="";
char chr;
string Eachline;
while ((row = r.ReadLine()) != null)
{
outCount = row.Length;
line = new StringBuilder();
for (int innerCount = 0; innerCount <= outCount - 1; innerCount++)
{
chr=row[innerCount];
if (chr != '"')
{
line.Append(row[innerCount].ToString());
}
else if(chr=='"')
{
token = "";
innerCount = innerCount + 1;
for (; innerCount < outCount - 1; innerCount++)
{
chr=row[innerCount];
if(chr=='"')
{
break;
}
token = token + chr.ToString();
}
if(token.Contains(",")){token=token.Replace(",","");}
line.Append(token);
}
}
Eachline = line.ToString();
Console.WriteLine(Eachline);
}
}
I have a string that looks like this:
123.45.67.890-1292 connected to EDS via 10.98.765.432-4300.
I need to split it like so:
"123.45.67.890-1292 connected to EDS via 10.98.765.432-4300."
-----+------- --+- -+- -----+------- --+-
| | | | |
ClientIP | ServiceName HostIP |
| |
ClientSession HostSession
I'm converting the code from vbscript that has a lot of complex InStr methods. Was wondering if there was a way to do this using a regEx.
(\d{,3}\.\d{,3}\.\d{,3}\.\d{,3})-(\d+) connected to ([A-Z]+) via (\d{,3}\.\d{,3}\.\d{,3}\.\d{,3})-(\d+)\.
Why can't you use split? Using regular expression for single task is inappropriate:
([^\-]+)\-(\S+)\s+connected\s+to\s+(\S+)\s+via\s+([^\-]+)\-(\S+)\.
C# code implementation (regular expression):
static void Main(string[] args)
{
String input = "123.45.67.890-1292 connected to EDS via 10.98.765.432-4300.";
String pattern = #"([^\-]+)\-(\S+)\s+connected\s+to\s+(\S+)\s+via\s+([^\-]+)\-(\S+)\.";
Match match = Regex.Match(input, pattern);
if (match.Success)
{
foreach (var group in match.Groups)
{
Console.WriteLine(group);
}
}
Console.ReadKey();
}
C# code implementation (splitting):
public class DTO
{
public string ClientIP { get; set; }
public string ClientSession { get; set; }
public string ServiceName { get; set; }
public string HostIP { get; set; }
public string HostSession { get; set; }
}
static void Main(string[] args)
{
String input = "123.45.67.890-1292 connected to EDS via 10.98.765.432-4300.";
String[] splits = input.Split(new char[] { ' ' });
DTO obj = new DTO();
for (int i = 0; i < splits.Length; ++i)
{
switch (i)
{
// connected
case 1:
// to
case 2:
// via
case 4:
{
break;
}
// 123.45.67.890-1292
case 0:
{
obj.ClientIP = splits[i].Split(new char[] { '-' })[0];
obj.ClientSession = splits[i].Split(new char[] { '-' })[1];
break;
}
// EDS
case 3:
{
obj.ServiceName = splits[i];
break;
}
// 10.98.765.432-4300.
case 5:
{
obj.HostIP = splits[i].Split(new char[] { '-' })[0];
obj.HostSession = splits[i].Split(new char[] { '-' })[1];
break;
}
}
}
Console.ReadKey();
}
(?<ClientIP>\d+\.\d+\.\d+\.\d+)-(?<ClientSession>\d+) connected to (?<ServiceName>.*?) via (?<HostIP>\d+\.\d+\.\d+\.\d+)-(?<HostSession>\d+)\.
Here's a RegExp to match/capture that:
([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+) connected to ([a-zA-Z]+) via ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+)
implementation:
string pat = #"([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+) connected to ([a-zA-Z]+) via ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+)";
Regex r = new Regex(pat, RegexOptions.IgnoreCase);
Match match = r.Match("123.45.67.890-1292 connected to EDS via 10.98.765.432-4300.");
foreach (var str in match.Groups)
Console.WriteLine(str);
Console.ReadKey();
Since I don't see why you rule out String.Split() :
var parts = test.Split(new string[] {" connected to ", " via "},
StringSplitOptions.None);
gives you
123.45.67.890-1292
EDS
10.98.765.432-4300
breaking of the -#### session parts would take 1 extra step, also possible with Split().
Or maybe easier:
var parts = test.Split(' ', '-');
and use parts 0, 1, 4, 6, 7