sort data from csv to txt - c#

I am writing a application which sorts out data from a csv to txt.
I have witten but I cannot get the required output.
Can someone please help, I do not see were I went wrong.
I initially thought its the File.WriteAllLines which was the problem but even when I write to a console I get the same results.
My file looks something like this
Georgina,Sinclair,408999703657,cheque,First National Bank,Fourways,275.00,12/01/2012
Zachary,Whitehead,409122372301,cheque,ABSA,Irene,70.25,12/01/2012
Toby,Henderson,401255489873,cheque,First National Bank,Edenvale,181.03,12/13/2012
Katherine,Cooke,409155874935,savings,ABSA,Southdowns,975.89,01/01/2013
Bradley,James,409254998,savings,ABSA,Melville,207.74,12/09/2012
Sophie,Lane,409771987,savings,ABSA,Roodepoort,207.74,12/31/2012
My output should be something like this
First National B0020000045603
GSinclair 408999703657 CH Fourways 002750001122012
THenderson 401255489873 CH Edenvale 001810313122012
ABSA 0040000146162
ZWhitehead 409122372301 CH Irene 000702501122012
KCooke 409155874935 SAVSouthdowns009758901012013
BJames 409254998 SAVMelville 002077409122012
SLane 409771987 SAVRoodepoort002077431122012
The code I currently have only returns the header and 2 lines which looks as follows.
ABSA 0040000146162
KCooke 409155874935 SAVSouthdowns 009758901012013
Please assist.
My code looks as follows
string text = #"C:\\Test\\output.txt";
var inputEntries = File.ReadLines(#"C:\\Test\\debitorders.csv").Select(line =>
{
var values = line.Split(',');
return new
{
accountholder = values[0].Trim().Substring(0, 1) + values[1].Trim(),
accountnumber = long.Parse(values[2].Trim()),
accounttype = values[3].Trim(),
bankname = values[4].Trim(),
branch = values[5].Trim(),
amount = 100 * double.Parse(values[6].Trim()),
date = DateTime.Parse(values[7].Trim())
};
});
var banks = inputEntries
.OrderBy(e => e.bankname)
.GroupBy(e => e.bankname, e => e);
foreach (var bank in banks)
{
var AccountName = bank.Key;
if (AccountName.Length >= 20)
{
AccountName = AccountName.Substring(0, 16);
}
else
{
AccountName += new string(' ', 20 - AccountName.Length);
}
var NumberOfAccounts = bank.Count();
var TotalAmount = bank.Select(acc => acc.amount).Sum();
var Header = AccountName + "\t" + NumberOfAccounts.ToString("000") + TotalAmount.ToString("0000000000");
var sortedAccounts = bank
.OrderBy(acc => acc.accountholder)
.OrderByDescending(acc => acc.amount);
foreach (var account in sortedAccounts)
{
var outputLine =
account.accountholder + "\t" +
account.accountnumber + "\t" +
//get first 2 characters
account.accounttype.Substring(0, 3).ToUpper() + account.branch + "\t" + "00" +
account.amount +
account.date.ToString("ddMMyyyy");
for (int i = 0; i < 15; i++)
{
File.WriteAllText(text, Header + Environment.NewLine + outputLine);
Console.WriteLine(Header + outputLine);
Console.ReadLine();
}
}
}

A better and cleaner solution will be to make use of List<string> to which you add your text. At the end of the code just convert the list to an array and write all lines to a file.
List<string> outputLine = new List<string>(); //note this addition to the code
foreach (var bank in banks)
{
//do header formatting stuff here
var Header = somecode
outputLine.Add(Header); //Add Header to outputLine
var sortedAccounts = bank.OrderBy(acc => acc.accountholder)
.OrderByDescending(acc => acc.amount);
foreach (var account in sortedAccounts)
{
var tempStringBuilder =
account.accountholder + "\t" +
account.accountnumber + "\t" +
//get first 2 characters
account.accounttype.Substring(0, 3).ToUpper() + account.branch + "\t" + "00" +
account.amount +
account.date.ToString("ddMMyyyy");
outputLine.Add(tempStringBuilder); //Add tempStringBuilder to outputLine
}
}
File.WriteAllLines("destination path", outputLine.ToArray()); //Write everything to your output file in one go
Alternative Excel solution:
Microsoft Excel has a really powerful tool called Pivot Tables, which is ideally suited to your needs. If you are unfamiliar with it, read some tutorials about it. At first it's a process to get your head around the workflow to use it but it is quite simple once you've grasped it. You just drag and drop fields by which you want to group.
You might also want to consider using Data Connections to link to your original data, which is also quite simple given the dataset you have.

I think i found the solution:
File.AppendAllText(text, Header + Environment.NewLine + outputLine + Environment.NewLine);
Use File.AppendAllText instead of File.WriteAllText. With WriteAllText you always deleted the old content.
But consider to clean the file (File.WriteAllText(text, "");) before you begin to write on it, otherwise you will have the old data from last run also in it.
Try to use String.Format("{0,-10}", name) which means that the length of the name is filled up with spaces up to the length of 10. Minus means left alignment and positive causes right alignment.
I updated your code to:
string text = #"D:\C#\output.txt";
File.WriteAllText(text, "");
var inputEntries = File.ReadLines(#"D:\c#\debitorders.csv").Select(line =>
{
var values = line.Split(',');
return new
{
accountholder = values[0].Trim().Substring(0, 1) + values[1].Trim(),
accountnumber = long.Parse(values[2].Trim()),
accounttype = values[3].Trim(),
bankname = values[4].Trim(),
branch = values[5].Trim(),
amount = 100 * double.Parse(values[6].Trim()),
date = DateTime.ParseExact(values[7].Trim(), "MM/dd/yyyy", CultureInfo.InvariantCulture)
};
});
var banks = inputEntries.OrderBy(e => e.bankname)
.GroupBy(e => e.bankname, e => e);
foreach (var bank in banks)
{
var AccountName = bank.Key;
var NumberOfAccounts = bank.Count();
var TotalAmount = bank.Select(acc => acc.amount).Sum();
var Header = String.Format("{0,-20} {1,-10} {2}", AccountName, NumberOfAccounts.ToString("000"), TotalAmount.ToString("0000000000"));
var sortedAccounts = bank.OrderBy(acc => acc.accountholder)
.OrderByDescending(acc => acc.amount);
File.AppendAllText(text, Header + Environment.NewLine);
Console.WriteLine(Header);
foreach (var account in sortedAccounts)
{
var outputLine = String.Format("{0,-11} {1,15} {2,-3} {3,-10} {4,7} {5,-10}",
account.accountholder,
account.accountnumber,
account.accounttype.Substring(0, 3).ToUpper(),
account.branch,
account.amount,
account.date.ToString("ddMMyyyy")
);
//get first 2 characters
//account.accounttype.Substring(0, 3).ToUpper() + account.branch + "\t" + "00" +
// what are the "00" for? didn't include them you may this do by yourself
File.AppendAllText(text, outputLine + Environment.NewLine);
Console.WriteLine(outputLine);
}
File.AppendAllText(text, Environment.NewLine);
Console.WriteLine();
}
Output is:
ABSA 004 0000146162
KCooke 409155874935 SAV Southdowns 97589 01012013
BJames 409254998 SAV Melville 20774 09122012
SLane 409771987 SAV Roodepoort 20774 31122012
ZWhitehead 409122372301 CHE Irene 7025 01122012
First National Bank 002 0000045603
GSinclair 408999703657 CHE Fourways 27500 01122012
THenderson 401255489873 CHE Edenvale 18103 13122012

Related

Discord.Net how to go to the next line in an embed with text formatting?

I've been looking for this but I cannot seem to find the answer.
What I want to accomplish is the following:
Right now when I reply with my embed it shows for example:
footbal,baseball
But what I want it to be is the following:
football,
baseball
Spread over 2 different lines.
Does anyone know how to do this with text Code?
Thank you in advance
Here is the code:
var value = "";
int price = 0;
foreach (var Item in content)
{
value += Item.Item1 + ": " + Item.Item2.ToString();
price += Item.Item2;
}
return new EmbedFieldBuilder()
{
Name = category + " - " + price,
Value = value
};
Worked for me with simple "\n" or Environment.NewLine:
var embed = new EmbedBuilder
{
Author = new EmbedAuthorBuilder() { Name = "AuthorNameHere" },
Title = "Sports",
Color = Color.Orange,
Description = "Football" + "\n\n" + "Baseball"
}.Build();
//var channel = GetYourNeededChannel();
await channel.SendMessageAsync("", false, embed);
Also works with fields in embed:
Fields = new List<EmbedFieldBuilder>()
{
new EmbedFieldBuilder()
{
Name = "TestField1",
Value = "FieldValue1" + "\n\n" + "FieldValue2"
}
}

C# extract data from csv

I have a CSV file like this:
field1,field2,field3,field4,...,fieldN
1,2,3,,...,N
and I want to extract exactly the 14th record of the second row.
I've tried this code:
string nomeFile = "externalSourceMsg " + this.DataSource.CodSorgEst + this.DataSource.CodIdSorgEst + ".csv";
string content = "Field1,Field2,Field3,Field4,Field5,Field6,Field7" + Environment.NewLine;
content += externalMessage.ToString();
content = content.Replace(',', ';');
int i = 0;
while (content){
int field;
if(i++ == 13)
field = content+i;
}
but it doesn't work. What did I do wrong?
Split the string and you can access fields by indexes.
string nomeFile = "externalSourceMsg " + this.DataSource.CodSorgEst + this.DataSource.CodIdSorgEst + ".csv";
string content = "Field1,Field2,Field3,Field4,Field5,Field6,Field7" + Environment.NewLine;
content += externalMessage.ToString();
content = content.Replace(',', ';');
// Split the string by your separator -> you will get an array filled with fields
string[] contentArr = content.Split(';');
// Access any field you'd like by indexes
var The14thField = contentArr[13];
Try:
// Read all lines and get the second (this can be done
// in more optimal way, just by reading two first lines)
var secondLine = File.ReadLines("Path")[1];
// Split byb comma and get 14th item in returned array
var entry = secondLine.Split(",")[13];

c# Windows Form, replace string in textbox (file content) with another string

I have a textbox that contains all of the lines of a loaded file.
It looks like this:
I am able to load a specific line of the file that contains a specific string using this in the app:
How would I be able to update the file/main textbox after I press the "Edit Module" button, if any of the textboxes would be changed .
For example, I would change Exam Weighting: "0.4" to Exam Weighting: "0.6", then press the "Edit Module" button which would edit the main textbox(file content). Which then would allow me to save the file with the updated content.
This is the code I am using to get a specific line from the file based on string from a textbox:
private void editModuleButton_Click(object sender, EventArgs e)
{
citation = editModuleComboBox.Text;
citationChange();
}
private void citationChange()
{
List<string> matchedList = new List<string>();
string[] linesArr = File.ReadAllLines(fileName);
//find matches
foreach (string s in linesArr)
{
if (s.Contains(citation))
{
matchedList.Add(s); //matched
}
}
//output
foreach (string s in matchedList)
{
string citationLine = s;
string[] lineData = citationLine.Split(',');
selectedModuleLabel.Text = lineData[2];
moduleTitleTextBox.Text = lineData[3];
creditsTextBox.Text = lineData[4];
semesterTextBox.Text = lineData[5];
examWeightingTextBox.Text = lineData[6];
examMarkTextBox.Text = lineData[7];
testWeightingTextBox.Text = lineData[8];
testMarkTextBox.Text = lineData[9];
courseworkWeightingTextBox.Text = lineData[10];
courseworkMarkTexbox.Text = lineData[11];
}
}
If somebody with enough rep could insert the images to this post, that would be great. Thanks
This solution might not be the perfect, but should work for you. What you need to do is whenever the Edit Module button is pressed, create a new string based on the text fields and replace it with the original line. First declare a string variable private string ChangedString = ""; inside the class, then:
foreach (string s in matchedList)
{
string citationLine = s;
string[] lineData = citationLine.Split(',');
string Stream = lineData[0]; //Store this somewhere so that it can be accessed later
string Stage = lineData[1]; //Store this somewhere so that it can be accessed later
selectedModuleLabel.Text = lineData[2];
moduleTitleTextBox.Text = lineData[3];
creditsTextBox.Text = lineData[4];
semesterTextBox.Text = lineData[5];
examWeightingTextBox.Text = lineData[6];
examMarkTextBox.Text = lineData[7];
testWeightingTextBox.Text = lineData[8];
testMarkTextBox.Text = lineData[9];
courseworkWeightingTextBox.Text = lineData[10];
courseworkMarkTexbox.Text = lineData[11];
}
store Stream and Stage in any Textbox/ComboBox if you already haven't then replace them accordingly in the following line. Now in EditButton_Click [Click Event] write:
ChangedString = Stream + "," + Stage + "," + selectedModuleLabel.Text + "," + moduleTitleTextBox.Text
+ "," + creditsTextBox.Text + "," + semesterTextBox.Text + "," + examWeightingTextBox.Text + ","
+ examMarkTextBox.Text + "," + courseworkWeightingTextBox.Text + "," + courseworkMarkTexbox.Text;
Now replace this string with the original line.
Edit: As you would get the line number which is being edited, store it in a variable, let's say
int LineBeingEdited = 3 //Supposing line number three is being edited.
Then again in the same Click event you can write this:
ChangedString = Stream + "," + Stage + "," + selectedModuleLabel.Text + "," + moduleTitleTextBox.Text
+ "," + creditsTextBox.Text + "," + semesterTextBox.Text + "," + examWeightingTextBox.Text + ","
+ examMarkTextBox.Text + "," + courseworkWeightingTextBox.Text + "," + courseworkMarkTexbox.Text;
var lines = TextBox1.Lines;
lines[LineBeingEdited] = ChangedString;
TextBox1.Lines = lines;
EDIT 2: To get the line number I would suggest you to modify your for each loop to for loop. Also add a int variable to store the line number inside the class like : private int LineBeingEdited = 0;
Modify this for each :
foreach (string s in linesArr)
{
if (s.Contains(citation))
{
matchedList.Add(s); //matched
}
}
To for loop:
for (int a = 0; a < linesArr.Length; a++)
{
if (s.Contains(citation))
{
matchedList.Add(linesArr[a]); //matched
LineBeingEdited = a;
break; //breaks the loop when a match is found
}
}
The above method is being used, taking into consideration that there will always be a single match. LineBeingEdited will now have the line number and can be accessed from anywhere in the class

How to increment a variable based on a column parsed from a list of files in a directory

I'm trying to create an import file based on a list of files in a directory. The problem is now I've been asked to increment the value of one of the output directories based on a specific part of the file name.
Below the LinkName is hard coded to Full Image_0. I actually want it to be more like LinkName = "Full Image_" + intXYZ.toString();
intXYZ would be a variable that starts at 0 and goes up by 1 for each file with identical PartNums.
Here is relevant code:
ImageName = Directory.GetFiles(#"\\192.168.0.144\iApps_Final_Images\ProductImages\", "*.*", SearchOption.AllDirectories);
ImageItem = Directory.GetFiles(#"\\192.168.0.144\iApps_Final_Images\ProductImages\", "*.*", SearchOption.AllDirectories).Select(file => Path.GetFileNameWithoutExtension(file)).ToArray();
// Set path for output file and open
FilePath = #"\\vhome\public\p21\Images.txt";
var writer = new StreamWriter(FilePath);
// Go through each file found (not named thumbs) and output row of data needed for inv_mast_links import
foreach (var item in ImageName)
{
SetNum = SetNum + 1;
LinkPath = item.ToString();
PartNum = ImageItem[SetNum - 1].ToString().Split('_').Last();
LinkName = "Full Image_0";
var line = SetNum + delimiter + PartNum + delimiter + LinkName + delimiter + LinkPath + delimiter + "Item Maintenance";
if (PartNum != "Thumbs")
{
writer.WriteLine(line);
}
}
// Close the output file
writer.Close();
Example: If files in directory are: TVImage_567.jpg, FrontView_888.jpg, BackView_888.jpg
then since two of the images are for the same PartNum of 888 the desired three LinkName outputs would be: Full Image_0, Full Image_0, Full Image_1.
If I understood your question correctly this is what you want:
Dictionary<string, Integer> counts = new Dictionary<string, Integer>();
foreach (var item in ImageName)
{
SetNum = SetNum + 1;
LinkPath = item.ToString();
PartNum = ImageItem[SetNum - 1].ToString().Split('_').Last();
if (counts.ContainsKey(PartNum)) {
counts[PartNum]++;
}
else
{
counts.Add(PartNum, 0);
}
LinkName = "Full Image_" + counts[PartNum];
var line = SetNum + delimiter + PartNum + delimiter + LinkName + delimiter + LinkPath + delimiter + "Item Maintenance";
if (PartNum != "Thumbs")
{
writer.WriteLine(line);
}
}
It is not quite clear what SetNum does so I just left it there. The idea is simply to store your the previous item's "number" and increment a counter until the actual "number" isn't equal to the previous one.
How about this, You dont require a SetNum
string[] ImageNames = Directory.GetFiles(#"Source-Directory\", "*.*", SearchOption.AllDirectories);
string[] ImageItem = ImageNames.Select(file => Path.GetFileNameWithoutExtension(file)).ToArray();
string FilePath = #"Destination.txt";
using (var writer = new StreamWriter(FilePath))
{
for (int i = 1; i < ImageNames.Length - 1; i++)
{
string LinkPath = ImageNames[i].ToString();
string PartNum = ImageItem[i - 1].ToString().Split('_').Last();
string LinkName = "Full Image_" + i;
var line = i + delimiter + PartNum + delimiter + LinkName + delimiter + LinkPath + delimiter + "Item Maintenance";
if (PartNum != "Thumbs")
{
writer.WriteLine(line);
}
}
}

how to increase the size of array or free the memory after each iteration. Error: Index was outside the bounds of the array c#

I read data from a text file which is 27 MB file and contains 10001 rows, I need to handle large data. I perform some kind of processing in each row of data and then write it back to a text file. This is the code I have am using
StreamReader streamReader = System.IO.File.OpenText("D:\\input.txt");
string lineContent = streamReader.ReadLine();
int count = 0;
using (StreamWriter writer = new StreamWriter("D:\\ft1.txt"))
{
do
{
if (lineContent != null)
{
string a = JsonConvert.DeserializeObject(lineContent).ToString();
string b = "[" + a + "]";
List<TweetModel> deserializedUsers = JsonConvert.DeserializeObject<List<TweetModel>>(b);
var CreatedAt = deserializedUsers.Select(user => user.created_at).ToArray();
var Text = deserializedUsers.Where(m => m.text != null).Select(user => new
{
a = Regex.Replace(user.text, #"[^\u0000-\u007F]", string.Empty)
.Replace(#"\/", "/")
.Replace("\\", #"\")
.Replace("\'", "'")
.Replace("\''", "''")
.Replace("\n", " ")
.Replace("\t", " ")
}).ToArray();
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
writer.WriteLine(TextWithTimeStamp);
}
lineContent = streamReader.ReadLine();
}
while (streamReader.Peek() != -1);
streamReader.Close();
This code helps does well up to 54 iterations as I get 54 lines in the output file. After that it gives error "Index was outside the bounds of the array." at line
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
I am not very clear about the issue if the maximum capacity of array has been violated, if so how can I increase it or If I can write the individual line encountered in loop through
writer.WriteLine(TextWithTimeStamp);
And clean the storage or something that can solve this issue. I tried using list insead of array , still issue is the same.Please help.
Change this line
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
to
var TextWithTimeStamp = (Text.Any() ? Text.First().a : string.Empty) +
" (timestamp:" + (CreatedAt.Any() ? CreatedAt.First() : string.Empty) + ")";
As you are creating Text and CreatedAt collection objects, they might be empty (0 total item) based on some scenarios and conditions.
Those cases, Text[0] and CreatedAt[0] will fail. So, before using the first element, check if there are any items in the collection. Linq method Any() is used for that purpose.
Update
If you want to skip the lines that do not contain text, change this lines
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
writer.WriteLine(TextWithTimeStamp);
to
if (Text.Any())
{
var TextWithTimeStamp = Text.First().a + " (timestamp:" + CreatedAt.First() + ")";
writer.WriteLine(TextWithTimeStamp);
}
Update 2
To include all the stringss from CreatedAt rather than only the first one, you can add all the values in comma separated strings. A general example
var strings = new List<string> { "a", "b", "c" };
var allStrings = string.Join(",", strings); //"a,b,c"

Categories