c# System.ArgumentOutOfRangeException - c#

I'm making program that sends email with some data. I know that the System.ArgumentOutOfRangeException exception means that number in the array/list doesn't exist, but i don't know what i coded wrongly.
Here's a code:
public void SendMail()
{
StreamReader sr = new StreamReader(path1);
var lineCount = File.ReadLines(path1).Count();
List<string> data = new List<string>();
for (int i = 0; i < lineCount; i++)
{
data[i] = sr.ReadLine(); //Error Comes here.
}
string finaldata = data[0] + "/n" + data[1] + "/n" + data[2] + "/n" + data[3] + "/n" + data[4] + "/n" + data[5] + "/n" +
data[6] + "/n" + data[7] + "/n" + data[8] + "/n" + data[9] + "/n" + data[10];
var fromAddress = new MailAddress("tutorialvideohd#gmail.com", "From Name");
var toAddress = new MailAddress("tutorialvideohd#example.com", "To Name");
const string fromPassword = "*****";
string subject = "Some Users Data.";
string body = finaldata;
var smtp = new SmtpClient
{
Host = "smtp.gmail.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
};
using (var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
})
{
smtp.Send(message);
}
}

When you do this, it's only creating an empty list:
List<string> data = new List<string>();
So if you try to assign a value using an index, you're trying to assign a value to something that doesn't exist.
Instead you should Add() as follows:
data.Add(sr.ReadLine());
EDIT:
Also, unrelated to the question asked, but I don't see you closing the StreamReader you've opened, which is always a bad practice. I suggest, instead, using a using statement which will take care of the opening and closing of the StreamReader for you. Also, getting the lineCount is redundant, you could do something like this taking advantage of the fact that you don't need to set the number of items in a list in advance.
List<string> data = new List<string>();
using (StreamReader sr = new StreamReader(path1))
{
while(!sr.EndOfStream)
data.Add(sr.ReadLine());
}

First, you have an empty list of strings, you can't fill data with by accessing the indexes, since those indexes don't yet exist. You have to use data.Add(sr.ReadLine()) to create the new index and add the value in it.
string finaldata = data[0] + "/n" + data[1] + "/n" + data[2] + "/n" + data[3] + "/n" + data[4] + "/n" + data[5] + "/n" +
data[6] + "/n" + data[7] + "/n" + data[8] + "/n" + data[9] + "/n" + data[10];
Hardcoded IDs will mean you need at least 11 items in the list and it will break if you have less. Why don't you do this instead?
string finalData = String.Join("/n", data);
This way it joins your list of strings, using the newline as a separator and doesn't matter if you have more or less items in the list.

You are setting an item in the array list that has not yet been initialized. Use the following line where the exception is thrown.
data.Add(sr.ReadLine());

You are doing the same thing twice.
This block of code:
StreamReader sr = new StreamReader(path1);
// ... you had some code here, but not relevant to the point...
for (int i = 0; i < lineCount; i++)
{
data[i] = sr.ReadLine(); //Error Comes here.
}
Populates the list data with a bunch of strings.
This block of code:
var lineCount = File.ReadLines(path1).ToList();
Populates lineCount with exactly the list of strings that you're trying to fill into data.
So just do this:
StreamReader sr = new StreamReader(path1);
var lineCount = File.ReadLines(path1).Count();
List<string> data = lineCount;
And get rid of this block of code:
for (int i = 0; i < lineCount; i++)
{
data[i] = sr.ReadLine(); //Error Comes here.
}
And notice how data is now properly filled in. You really don't need to have that StreamReader either, but hey, I don't want to rewrite your entire body of code at this point.
Key takeaway: read the documentation, try to understand what each function call is doing, especially the .NET Framework ones.

Related

ArgumentOutOfRangeException when I think my code should work

I'm working on a bit of code for school but I keep getting an ArgumentOutOfRangeException
With this code I'm trying to read some data from a .csv file and if it equals the name of the image I want it to remove it from the .csv file whilst keeping the structure intact.
public void checkPair(Image card1, Image card2)
{
this.Image1 = card1;
this.Image2 = card2;
if (Convert.ToString(card1.Source) == Convert.ToString(card2.Source) && (card1 != card2))
{
getPoint(card1, card2);
string path = #"Save1.csv";
var reader = new StreamReader(File.OpenRead(path));
var data = new List<List<string>>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(';');
data.Add(new List<String> { values[0], values[1]
});
}
reader.Close();
string delimiter = ";";
for (int i = 1; i < 5; i++)
{
for (int x = 0; x < 4; x++)
{
if (data[i][x] == Convert.ToString(card1.Source))
{
data[i][x] = null;
}
}
}
File.WriteAllText(path, data[0][0] + delimiter + data[0][1] + Environment.NewLine + data[1][0] + delimiter + data[1][1] + delimiter + data[1][2] + delimiter + data[1][3] + Environment.NewLine + data[2][0] + delimiter + data[2][1] + delimiter + data[2][2] + delimiter + data[2][3] + Environment.NewLine + data[3][0] + delimiter + data[3][1] + delimiter + data[3][2] + delimiter + data[3][3] + Environment.NewLine + data[4][0] + delimiter + data[4][1] + delimiter + data[4][2] + delimiter + data[4][3] + Environment.NewLine + "ready");
I have no idea why I get this error and how to fix it
Initially, I'd change your last line from
File.WriteAllText(path, data[0][0] + delimiter + data[0][1] ....
to something like
var obj1 = data[0][0];
var obj2 = data[0][1];
File.WriteAllText(path, obj1 + delimiter + obj2 .... etc)
If you over inline functions or array accessing, when you get an exception the stack trace won't be that helpful. At least you'll have an idea of the statement that caused the issue.
This technique can prove to be very helpful, if you are looking at an in exception in the logs, after the fact.

C# compare id from text file in filestream

I need to fill a text file with information about workers. Then I need to read from the file and search for an ID that user tries to find. For example my file contains ids 1,2,3 and if I try to find id 3 and it matches, then this worker's all information is written in console. Otherwise it writes a text A worker cannot be found.
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
string file = "C:\\Temp\\registery.txt";
FileStream fOutStream = File.Open(file, FileMode.Append, FileAccess.Write);
StreamWriter sWriter = new StreamWriter(fOutStream);
int[] id = { 1, 2, 3 };
string[] name = { "John", "Carl", "Thomas" };
float[] salary = { 3500, 4800, 2100 };
for (int i = 0; i < id.Length; i++)
{
sWriter.WriteLine(id[i] + " " + name[i] + " " + salary[i]);
}
sWriter.Flush();
sWriter.Close();
FileStream fInStream = File.OpenRead(file);
StreamReader sReader = new StreamReader(fInStream);
int id2;
Console.WriteLine("Type worker's id");
id2 = int.Parse(Console.ReadLine());
bool a;
a = sReader.ReadToEnd().Contains(id2);
Console.WriteLine(a);
sReader.Close();
}
}
If you want to create a text file to be searchable, it should be delimited by a separator like comma /TAB
so modify your code:
sWriter.WriteLine(id[i] + "," + name[i] + "," + salary[i]);
To search your text file by id/name/..whatever and use AND/OR, you can use the method described here:
How would I convert data in a .txt file into xml? c#
BTW: Re-factor your code to create the file in a separate method, and the search in other one.
I found a solution myself to my problem and it worked good enough. It might not be the best solution. I removed bool things and I replaced the whole thing with this:
string line;
while ((line = sReader.ReadLine()) != null)
{
if (line.Contains("id: " + id2))
{
Console.WriteLine(line);
break;
}
else if ((line = sReader.ReadLine()) == null)
{
Console.WriteLine("Worker not found with id " + id2);
}
}
And I fixed the upper for loop to look like this:
sWriter.WriteLine("id: " + id[i] + " name: " + name[i] + " salary: " + salary[i]);

csv modify file

I am a bit in a pickle regarding a consolidation application we are using in our company. We create a csv file from an progress database this csv file has 14 columns and NO header.
The CSV file contains payments (around 173 thousand rows). Most of these rows are the same except for the column amount (last column)
Example:
2014;MONTH;;SC;10110;;;;;;;;EUR;-6500000
2014;01;;SC;10110;;;;;;;;EUR;-1010665
2014;01;;LLC;11110;;;;;;;;EUR;-6567000
2014;01;;SC;10110;;;;;;;;EUR;-1110665
2014;01;;LLC;11110;;;;;;;;EUR;65670.00
2014;01;;SC;10110;;;;;;;;EUR;-11146.65
(around 174000 rows)
As you can see some of these lines are the same except for the amount column. What i need is to sort all rows, add up the amount and save one unique row instead of 1100 rows with different amounts.
My coding skills are failing me to get the job done within a certain timeframe, maybe one of you can push me in the right direction solving this problem.
Example code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input = File.ReadAllText(#"c:\temp\test.txt");
string inputLine = "";
StringReader reader = new StringReader(input);
List<List<string>> data = new List<List<string>>();
while ((inputLine = reader.ReadLine()) != null)
{
if (inputLine.Trim().Length > 0)
{
string[] inputArray = inputLine.Split(new char[] { ';' });
data.Add(inputArray.ToList());
}
}
//sort data by every column
for (int sortCol = data[0].Count() - 1; sortCol >= 0; sortCol--)
{
data.OrderBy(x => x[sortCol]);
}
//delete duplicate rows
for (int rowCount = data.Count - 1; rowCount >= 1; rowCount--)
{
Boolean match = true;
for (int colCount = 0; colCount < data[rowCount].Count - 2; colCount++)
{
if(data[rowCount][colCount] != data[rowCount - 1][colCount])
{
match = false;
break;
}
}
if (match == true)
{
decimal previousValue = decimal.Parse(data[rowCount - 1][data[rowCount].Count - 1]);
decimal currentValue = decimal.Parse(data[rowCount][data[rowCount].Count - 1]);
string newStrValue = (previousValue + currentValue).ToString();
data[rowCount - 1][data[rowCount].Count - 1] = newStrValue;
data.RemoveAt(rowCount);
}
}
string output = string.Join("\r\n",data.AsEnumerable()
.Select(x => string.Join(";",x.Select(y => y).ToArray())).ToArray());
File.WriteAllText(#"c:\temp\test1.txt",output);
}
}
}
Read the CSV file line by line, and build an in-memory dictionary in which you keep the totals (and other information you require). As most of the lines belong to the same key, it will probably not cause out of memory issues. Afterwards, generate a new CSV based on the information in the dictionary.
As I interpret your question, your problem and the solution you are asking for are how to take your input that are in the form of
#"2014;MONTH;;SC;10110;;;;;;;;EUR;-6500000
2014;01;;SC;10110;;;;;;;;EUR;-1010665
2014;01;;LLC;11110;;;;;;;;EUR;-6567000
2014;01;;SC;10110;;;;;;;;EUR;-1110665
2014;01;;LLC;11110;;;;;;;;EUR;65670.00
2014;01;;SC;10110;;;;;;;;EUR;-11146.65"
Get the last column and then sum it up? If so this is actually very easy to do with something like this
public static void Main()
{
string input = #"2014;MONTH;;SC;10110;;;;;;;;EUR;-6500000
2014;01;;SC;10110;;;;;;;;EUR;-1010665
2014;01;;LLC;11110;;;;;;;;EUR;-6567000
2014;01;;SC;10110;;;;;;;;EUR;-1110665
2014;01;;LLC;11110;;;;;;;;EUR;65670.00
2014;01;;SC;10110;;;;;;;;EUR;-11146.65";
var rows = input.Split('\n');
decimal totalValue = 0m;
foreach(var row in rows)
{
var transaction = row.Substring(row.LastIndexOf(';') +1);
decimal val = 0m;
if(decimal.TryParse(transaction, out val))
totalValue += val;
}
Console.WriteLine(totalValue);
}
But maybe I have misunderstood what you were asking for?
Sorry answering my post so late but this is my final solution
Replacing all " characters and write the output to the stream writer. (going from 25mb to a 15mb file.). Than copy my CSV file to the SQL server so i can bulk insert. After my insert i just query the table and read / write the result set to a new file. My new file is only +/-700KB!
The Filldata() method is filling a datagridview in my application so you can review the result instead of opening the file in excel.
I am new with C#, i am currently writing a new solution to query the csv file directly or in memory and write it back to a new file.
Method1:
string line;
StreamWriter sw = new StreamWriter(insertFile);
using (StreamReader sr = new StreamReader(sourcePath))
{
while ((line = sr.ReadLine()) != null)
{
sw.WriteLine(line.Replace("\"", ""));
}
sr.Close();
sw.Close();
sr.Dispose();
sw.Dispose();
File.Copy(insertFile, #"\\SQLSERVER\C$\insert.csv");
}
Method2:
var destinationFile = #"c:\insert.csv";
var querieImportCSV = "BULK INSERT dbo.TABLE FROM '" + destinationFile + "' WITH ( FIELDTERMINATOR = ';', ROWTERMINATOR = '\n', FIRSTROW = 1)";
var truncate = #"TRUNCATE TABLE dbo.TABLE";
string queryResult =
#"SELECT [Year]
,[Month]
,[Week]
,[Entity]
,[Account]
,[C11]
,[C12]
,[C21]
,[C22]
,[C3]
,[C4]
,[CTP]
,[VALUTA]
,SUM(AMOUNT) as AMOUNT
,[CURRENCY_ORIG]
,[AMOUNTEXCH]
,[AGENTCODE]
FROM dbo.TABLE
GROUP BY YEAR, MONTH, WEEK, Entity, Account, C11, C12, C21, C22, C3, C4, CTP, VALUTA, CURRENCY_ORIG, AMOUNTEXCH, AGENTCODE
ORDER BY Account";
var conn = new SqlConnection(connectionString);
conn.Open();
SqlCommand commandTruncate = new SqlCommand(truncate, conn);
commandTruncate.ExecuteNonQuery();
SqlCommand commandInsert = new SqlCommand(querieImportCSV, conn);
SqlDataReader readerInsert = commandInsert.ExecuteReader();
readerInsert.Close();
FillData();
SqlCommand commandResult = new SqlCommand(queryResult, conn);
SqlDataReader readerResult = commandResult.ExecuteReader();
StringBuilder sb = new StringBuilder();
while (readerResult.Read())
{
sb.Append(readerResult["Year"] + ";" + readerResult["Month"] + ";" + readerResult["Week"] + ";" + readerResult["Entity"] + ";" + readerResult["Account"] + ";" +
readerResult["C11"] + ";" + readerResult["C12"] + ";" + readerResult["C21"] + ";" + readerResult["C22"] + ";" + readerResult["C3"] + ";" + readerResult["C4"] + ";" +
readerResult["CTP"] + ";" + readerResult["Valuta"] + ";" + readerResult["Amount"] + ";" + readerResult["CURRENCY_ORIG"] + ";" + readerResult["AMOUNTEXCH"] + ";" + readerResult["AGENTCODE"]);
}
sb.Replace("\"","");
StreamWriter sw = new StreamWriter(homedrive);
sw.WriteLine(sb);
readerResult.Close();
conn.Close();
sw.Close();
sw.Dispose();

how do i convert a string with multiple types in c#

I am getting a cast error for the Url value. Does anyone know how to cast a value to a string that contains multiple value types?
CmdRecipients = new OleDbCommand(QueryRecipients, ConnRecipients);
Recipients = CmdRecipients.ExecuteReader();
while (Recipients.Read())
{
Url = "https://bulktext.vodafone.ie/sendmessage.aspx"
+ "?user=user&password=user1!&api_id=89&to="
+ Recipients.GetString(8)
+ "&text=" + Confirmations.GetString(4)
+ "%0d%0a" + Confirmations.GetString(5)
+ "%0d%0a" + Confirmations.GetString(6)
+ "&from=Service";
Console.Write("Sending text to " + Recipients.GetString(8) + " ... ");
//Send SMS Here
HttpWebRequest req = WebRequest.Create(Url) as HttpWebRequest;
string result = null;
using (HttpWebResponse resp = req.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(resp.GetResponseStream());
result = reader.ReadToEnd();
if (result == "0\nRequest deferred successfully")
{
QueryRecipients = "UPDATE [Test].[dbo].[MessagesToSend] SET Previous_Status = Current_Status,"
+ "Current_Status = \"Sent\", DateTime_sent = Now() "
+ "WHERE Task_ID = \"" + Recipients.GetString(2) + "\";";
OleDbCommand Update = new OleDbCommand(QueryRecipients, ConnConfirmations);
Update.ExecuteNonQuery();
Console.WriteLine("Done!");
}
else
{
Console.WriteLine("The text wasn't delivered properly.");
}
}
}
Your question is a bit vague as you're not telling us where the exception is thrown, nor what the exception is exactly.
I would suspect that the problem lies in your use of Confirmations.GetString(x) and Recipients.GetString(x). As I understand, these are results from database calls. If the respective fields returned by the select don't contain strings, the GetString call will fail. You need to use the method that matches the field's type, like GetBoolean, GetInt32, etc.

Reading a large CSV file and processing in C#. Any suggestions?

I have a large CSV file around 25G. I need to parse each line which has around 10 columns and do some processing and finally save it to a new file with parsed data.
I am using dictionary as my datastructure. To avoid the memory overflow I am writing the file after 500,000 records and clearing the dictionary.
Can anyone suggest whether is this good way of doing. If not, any other better way of doing this? Right now it is taking 30 mins to process 25G file.
Here is the code
private static void ReadData(string filename, FEnum fileType)
{
var resultData = new ResultsData
{
DataColumns = new List<string>(),
DataRows = new List<Dictionary<string, Results>>()
};
resultData.DataColumns.Add("count");
resultData.DataColumns.Add("userid");
Console.WriteLine("Start Processing : " + DateTime.Now);
const long processLimit = 100000;
//ProcessLimit : 500000, TimeElapsed : 30 Mins;
//ProcessLimit : 100000, TimeElaspsed - Overflow
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Dictionary<string, Results> parsedData = new Dictionary<string, Results>();
FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
using (StreamReader streamReader = new StreamReader(fileStream))
{
string charsRead = streamReader.ReadLine();
int count = 0;
long linesProcessed = 0;
while (!String.IsNullOrEmpty(charsRead))
{
string[] columns = charsRead.Split(',');
string eventsList = columns[0] + ";" + columns[1] + ";" + columns[2] + ";" + columns[3] + ";" +
columns[4] + ";" + columns[5] + ";" + columns[6] + ";" + columns[7];
if (parsedData.ContainsKey(columns[0]))
{
Results results = parsedData[columns[0]];
results.Count = results.Count + 1;
results.Conversion = results.Count;
results.EventList.Add(eventsList);
parsedData[columns[0]] = results;
}
else
{
Results results = new Results {
Count = 1, Hash_Person_Id = columns[0], Tag_Id = columns[1], Conversion = 1,
Campaign_Id = columns[2], Inventory_Placement = columns[3], Action_Id = columns[4],
Creative_Group_Id = columns[5], Creative_Id = columns[6], Record_Time = columns[7]
};
results.EventList = new List<string> {eventsList};
parsedData.Add(columns[0], results);
}
charsRead = streamReader.ReadLine();
linesProcessed++;
if (linesProcessed == processLimit)
{
linesProcessed = 0;
SaveParsedValues(filename, fileType, parsedData);
//Clear Dictionary
parsedData.Clear();
}
}
}
stopwatch.Stop();
Console.WriteLine(#"File : {0} Batch Limit : {1} Time elapsed : {2} ", filename + Environment.NewLine, processLimit + Environment.NewLine, stopwatch.Elapsed + Environment.NewLine);
}
Thank you
The Microsoft.VisualBasic.FileIO.TextFieldParser class looks like it could do the job. Try it, it may speed things up.

Categories

Resources