Read CSV File and Store Column in list double - c#

carry the code below where I embarked.
class readFile{
List<double> out1 = new List<double>();
List<double> out2 = new List<double>();
List<double> out3 = new List<double>();
public readFile()
{
}
public void aproCSV()
{
var reader = new StreamReader(File.OpenRead(#"C:\altraprova.csv"));
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(';');
out1.Add(values[0]);
out2.Add(values[1]);
out3.Add(values[2]);
}
}
}
Allow me to explain my intent ....
I open my csv file
the goal is to read the second column and insert it into a list of type double and then get the value MAX.
If you use lists of type string not get erors .... but since the values contain only numbers you are signed semicolon, then I need to use the type double.. using the double type get the following error:
error 5 The best match of the overloaded method for
'System.Collections.Generic.List <double> .Add (double)' has some
invalid arguments

Split() returns a string[] and your lists are of type List<double> which means you need to parse the strings into doubles like the following:
double value1 = double.Parse(values[0]); etc
then add them to your list: out1.Add(value1);
Do be aware that you have no error handling so if the value is not a double, the code will still throw an exception.

CSV are surprisingly not so straightforward to parse, there are a lot of special cases which you have to take into consideration. For example, if your data would contain the "separation character" you'd need to put the data between quotes ("). If he contains quotes, then you have to escape them with a backslash (\) (or doubling them, I'm not sure).
So, except if you know the data you're going to be importing and are sure that those case won't happen, a simple "split" won't be sufficient.
I really recommend using an existing parser to help you on this task. I've used CsvHelper with success. It's a nice library and quite easy to use.

You need to do some parsing, because a generic list only accepts elements of the given type (double in your case):
var line = reader.ReadLine();
var values = line.Split(';');
out1.Add(double.Parse(values[0]));
out2.Add(double.Parse(values[1]));
out3.Add(double.Parse(values[2]));
This is the quick and dirty trick, you should use double.TryParse
for safety because maybe not everything you get is a double.
From you code I suppose the you need to get the max from list out2 (your second column). For this, use List.Max Method

Related

Trouble using ExcelRange.ToArray<>() in EPPlus

I am using EPPlus and don't seem to be able to get the ToArray<> method to work. I am trying to pull out an array of string variables of column headers in a worksheet.
My code...
public static string[] GetWshHeaders(string WbkNm)
{
using (ExcelPackage package = new ExcelPackage(new FileInfo(WbkNm)))
{
ExcelWorksheet wsData = package.Workbook.Worksheets.First();
int noHdrs = wsData.Dimension.Columns;
ExcelRange hdrs = wsData.Cells[1, 1, 1, noHdrs];
string[] wsHdrs = hdrs.ToArray<string>();
return wsHdrs;
}
}
Intellisense flags the hdrs variable in the line string[] wsHdrs = hdrs.ToArray();. The message is: 'ExcelRange' does not contain a definition for 'ToArray' and the best extension method overload 'Enumerable.ToArray(IEnumerable)' requires a receiver of type 'IEnumerable'.
I have played around with any number of variations of the above code but, well, I wouldn't be posting this question if I had hit upon the correct syntax.
Any help would be appreciated!
ExcelRange has ExcelRangeBase as a base class which is declared list this:
public class ExcelRangeBase : ExcelAddress, IExcelCell, IDisposable, IEnumerable<ExcelRangeBase>, IEnumerator<ExcelRangeBase>
{
....
}
So you are trying to use Linq to do an implicit cast from IEnumerable<ExcelRangeBase> to String[] which will not work. You need to use a Select to get the Value properties of each range object. And since each Value property is of type Object you will need to call its ToString() method:
string[] wsHdrs = hdrs
.Select(erb => erb.Value.ToString())
.ToArray();
The above will get you the array of strings you are looking for but keep in mind that you are loosing information since Value can be a mix of numeric types and string. Not a problem if you are only interested in, say, simply printing their content. If you plan to write them back in some way to excel you will have everything as string.

send a String array as parameter to a function

I have a function in a class called Function, like below:
public int SearchedRecords(String [] recs)
{
int counter = 0;
String pat = "-----";
String[] records = recs;
foreach (String line in records)
{
if (line.Contains(pat) == true)
{
counter++;
}
}
return counter;
}
And I am calling this method from my main class this way:
String [] file = File.ReadAllLines("C:/Users.../results.txt");
int counter = Function.SearchedRecords( []file);
But I get an error saying:
;expected
What is wrong?
Another question: The function above is counting from a file all the lines with the pattern ----- in them (even if with more dashes, or if the line has some chars before or after the dashes). Am I right?
It's something like the patterns in Java so maybe there is an other way.
Can you enlighten me?
Remove the [] from your parameter.
e.g.
int counter = Function.SearchedRecords(file);
And yes, your assumption about the behavior of the Contains method is correct - you'll match any line containing five consecutive dashes, regardless of what characters are before or after them.
If you want to parse for exactly five dashes, with nothing before or after them I suggest looking into the RegEx class (regular expressions).
Change
int counter = Function.SearchedRecords( []file);
to
int counter = Function.SearchedRecords(file);
and yes, this will work, for that string.
However Contains is case sensitive, if you were matching on a name, or another string with alphabetic characters, the case would have to be identical to match e.g. line.Contains("Binary Worrier") will not match a string "Hello binary worrier".
Also, reading the entire file into memory is fine if you know that the file will always be small, this method gets less efficient the larger the file.
Better to always use something like System.IO.StreamReader or System.IO.File.ReadLines (available in .Net 4 and later), these allow you to consume the file one line at a time. e.g.
using (var reader = new System.IO.StreamReader("MyFile.txt"))
{
while(!reader.EndOfStream)
{
string line = reader.ReadLine();
if (line.Contains(pattern))
counter++;
}
}
Change it to
int counter = Function.SearchedRecords(file);
Remove '[]' from a method call. Yes, your function seems to count what you want.
First of all you need to create an instance of function class and then run the function. Hope following code helps
Function fb = new Function();
int counter = fb.SearchedRecords(file);
Right now, you are using SearchRecords as an static function of a static class which doesn't require instantiation.
You can do this in a shorter way using LINQ:
int counter = file.Count(line => line.Contains("-----"));

How to get the value of a System.String object instead of returning "System.String[]"

I am working on a file parser, and this bit of code is not giving me what I want. Before I go any farther, I should mention that I did not write this program, I am only editing the source to fix this specific problem. Also, I can compile the code, so that is not a problem (you know how downloaded programs always have compile errors). Here's the code.
case EsfValueType.Binary4E: //System.String[]
{
int size = (int)(this.reader.ReadUInt32() - ((uint)this.reader.BaseStream.Position));
var strings = new string[size / 4];
for (int i = 0; i < size / 4; i++)
strings[i] = this.stringValuesUTF16[this.reader.ReadUInt32()];
esfValue.Value = strings.ToString();
break;
}
Now, I added the .ToString(); part to the above line, but it made no difference. The problem is that esfValue.Value ends up with System.String[] as it's value, and I want the value of the System.String object. If you can make sense out of this and tell me what is wrong, it would be appreciated.
The program name is ESF Editor 1.4.8.0.
case EsfValueType.Binary4E: //System.String[]
{
int size = (int)(this.reader.ReadUInt32() - ((uint)this.reader.BaseStream.Position));
var strings = new StringBuilder();
for (int i = 0; i < size / 4; i++)
{
strings.Append(this.stringValuesUTF16[this.reader.ReadUInt32()]); //or AppendLine, depending on what you need
}
esfValue.Value = strings.ToString();
break;
}
The strings variable is an array of strings - the Array class does not override the default ToString() implementation which returns the type of the object.
You need to concatenate all the strings in the array - either looping and concatenating or using LINQ and assign the resulting string to esfValue.Value. Of course, this assumes you want the values all in one string, one after the other.
Your issue is that strings isn't a single string, its an array of strings. As a result your call to ToString is calling Object.ToString(), which returns the type of the object.
Maybe you want something like
esfValue.Value = strings.Aggregate((acc, next) => acc + next)
which will simply concatenate all the strings together.
When you do a .ToString() on a class that doesn't override the .ToString() base method to return a custom string (which string[] doesn't), you're always going to get the type's namespace/class as the result.
Arrays, in and of themselves, don't have values. What value are you trying to get? Are you trying to join the array into a single, character-delimited string? If so, this would work:
esfValue.Value = string.Join(",", strings);
Just replace the , with whatever character you want to delimit the array with.
I think you just need to join the string values contained in the string array. In order to do so, you need to call String.Join and pass the string separator and the string array. It returns a single System.String.

String.Format - Problem with Argument list

string nr = "42245555" //just an example number
string expression = "{5}{6}/{7}{8}";
string res = String.Format(expression, new string[] {
nr[0].ToString(),nr[1].ToString(),
nr[2].ToString(), nr[3].ToString(),
nr[4].ToString(), nr[5].ToString(),
nr[6].ToString(), nr[7].ToString()
});
Why is this not working and how can I solve it?
I want expression to be either "{5}{6}/{7}{8}" or "{0}{3}/{7}{1}" or whatever the user wants.
You must supply at least the same number of parameters (or an array with at least the same number of elements) as the highest placeholder value plus one (placeholder values are zero indexed)
Max placeholder value {3}, you must supply at least four additional parameters.
Try this:
string res = String.Format(expression,
nr[0], nr[1],
nr[2], nr[3],
nr[4], nr[5],
nr[6], nr[7]);
Note that I took out new string[] { ... } I also took out all the ToString() because they are not required.
The relevant overload of Format is:
public static string Format(string format, params Object[] args)
This means you can either call it like this:
Format("...",new object[]{...})//Each array element is used
Or with the objects directly as parameters:
Format("...",object1, object2,...)//the objects are used
Unlike what I originally thought, a string[] does indeed go into the first variant of this code. This is due to array covariance in C#/.net which allows a string[] to be implicitly converted to a object[].
string.Format("{0} {1}",new string[]{"A","B"})
prints A B.
On the other hand if you try similar code with a int[] it won't work:
string.Format("{0} {1}",new int[]{1,2})
Will throw an exception because it goes into the second variation, and thus only a single parameter of type int[] is seen by string.Format. This difference is because array covariance only works on arrays with members that are a reference type like string and not a value type like int. This means int[] is not implicitly convertible to object[].
So the problem with your original code is probably just that you used the index {8} which is out of range.
The parameters in expression must start from "{0}" and the array must contain the same number of parameters as the expression and it must be an array of objects : new object[]{...
Your parameters are numbered 0..7, easy to read back. Yet you use {8} : Index out of Range
You don't need the new string[] { } around the parameters. It is allowed though.
without the new string[] { } you don't need the .ToString() calls either.
IF ur user wants {5}{6}/{7}{8} , then ur code will be:
String.Format("{0}{1}/{2}{3}",nr[4],nr[5],nr[6],nr[7]);
ur indexes in the expression must always start with 0..In my example if u want to display this for strings.. u start by 0,1,2,3.. 0 for nr[4], 1 for nr[5], 2 for nr[6], 3 for nr[7], etc..
You have to use:
nr.Substring(...)
in String.Format([expression], [values]) expression is a string represent a string with placeholders while values are inserted in those placeholders.
updated
string nr = "42245555" //just an example number
string res = String.Format("{0}{1}/{2}{3}", new string[] {
nr[5].ToString(),nr[6].ToString(),
nr[7].ToString(), nr[8].ToString()
});
or
string res = String.Format("{0}{1}/{2}{3}", new string[] {
nr[0].ToString(),nr[3].ToString(),
nr[7].ToString(), nr[1].ToString()
});

String insertion problem in c#

I am trying to insert a string at a position for C# string, its failing
here is the snippet.
if(strCellContent.Contains("<"))
{
int pos = strCellContent.IndexOf("<");
strCellContent.Insert(pos,"<");
}
please tell me the solution
The return value contains the new string that you desire.
strCellContent = strCellContent.Insert(pos,"<");
Gunner and Rhapsody have given correct changes, but it's worth knowing why your original attempt failed. The String type is immutable - once you've got a string, you can't change its contents. All the methods which look like they're changing it actually just return a new value. So for example, if you have:
string x = "foo";
string y = x.Replace("o", "e");
the string x refers to will still contain the characters "foo"... but the string y refers to will contain the characters "fee".
This affects all uses of strings, not just the particular situation you're looking at now (which would definitely be better handled using Replace, or even better still a library call which knows how to do all the escaping you need).
I think you might be better of with a Replace instead of an Insert:
strCellContent = strCellContent.Replace("<", "<");
Maybe doing Server.HtmlEncode() is even better:
strCellContent = Server.HtmlEncode(strCellContent);
When I look at your code I think you want to do a replace, but try this:
if(strCellContent.Contains("<"))
{
int pos = strCellContent.IndexOf("<");
strCellContent = strCellContent.Insert(pos,"<");
}
.Contains is not a good idea here, because you need to know the position. This solution will be more efficient.
int pos = strCellContent.IndexOf("<");
if (pos >= 0) //that means the string Contains("<")
{
strCellContent = strCellContent.Insert(pos,"<"); //string is immutable
}
As others have explained with the code, I will add that
The value of the String object is the
content of the sequential collection,
and that value is immutable (that is,
it is read-only).
For more information about the immutability of strings, see the Immutability and the StringBuilder Class section.
from: http://msdn.microsoft.com/en-us/library/system.string.aspx

Categories

Resources