I have 5 DataTables that needs to be converted to TXT files. Instead of creating them separately, I thought I use a for loop. Here's my code:
StringBuilder sb = new StringBuilder();
for (int i = 1; i < 5; i++)
{
DataTable dtFile1 = file1BLO.SelectFile1ForCSV()
foreach (DataRow dr in dtFile1.Rows)
{
string[] fields = dr.ItemArray.Select(field => field.ToString()).ToArray();
sb.AppendLine(string.Join("|", fields) + "|");
}
Response.ContentType = "application/text";
Response.AddHeader("content-disposition", "attachment;filename=CAPRES-FILE1-"
+ DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".txt");
Response.Output.Write(sb);
Response.Flush();
Response.End();
sb.Clear();
}
I would want the iterator to be appended to the variable names and methods. Like this DataTable dtFile + i = file + i + BLO.SelectFile + i + ForCSV();
Thanks!
Requested Code for SelectFile1ForCSV()
public DataTable SelectFile1ForCSV()
{
return file1DAO.SelectFile1ForCSV();
}
EDIT: I am sorry, it seems that I haven't provided enough details, and my question has caused confusion. Edited now.
You cannot just append a number to a variable name at runtime to magically reference a new variable. What you should do instead is:
Define an an interface:
public interface IFileBLO
{
DataTable SelectFileForCSV();
}
Have File1BLO, File2BLO etc all implement IFileBLO and fix the method names so that they are all SelectFileForCSV rather than SelectFile1ForCSV etc.
Add a lookup for reference these objects:
var bloList = new IFileBLO[]
{
file1BLO, file2BLO, file3BLO, file4BLO, file5BLO
};
Finally, change your loop to:
for (int i = 0; i < 5; i++)
{
var dtFile = bloList[i].SelectFileForCSV();
foreach (var dr in dtFile.Rows)
{
...
There are not enough information in the question to know exactly what the problem is, so I'm just guessing here. Your problem is that you have five objects, that all have a method, and these method have different names. The methods return the same thing, though, DataTable, that can be used in a loop.
If that's the case then just take out of the loop that which is different, so that in the loop remains that which is identical. Something like this:
DataTable[] fiveTables =
{
file1BLO.SelectFile1ForCSV(),
file2BLO.SelectFile2ForCSV(),
file3BLO.SelectFile3ForCSV(),
file4BLO.SelectFile4ForCSV(),
file5BLO.SelectFile5ForCSV()
}
for (int i = 1; i <= 5; i++)
{
// Use fiveTables[i] for DataTable, and i for file name
}
Use this:
Response.AddHeader("content-disposition", "attachment;filename=CAPRES-FILE"
+ i
+ "-"
+ DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".txt");
This will create a filename containing the value of i.
Related
I am trying to use DotNetZip open source library for creating large zip files.
I need to be able to write to each stream writer part of the data row content (see the code below) of the data table. Other limitation I have is that I can't do this in memory due to the contents being large (several giga bytes each entry).
The problem I have is that despite writing to each stream separately, the output is all written to the last entry only. The first entry contains blank. Does anybody have any idea on how to fix this issue?
static void Main(string fileName)
{
var dt = CreateDataTable();
var streamWriters = new StreamWriter[2];
using (var zipOutputStream = new ZipOutputStream(File.Create(fileName)))
{
for (var i = 0; i < 2; i++)
{
var entryName = "file" + i + ".txt";
zipOutputStream.PutNextEntry(entryName);
streamWriters[i] = new StreamWriter(zipOutputStream, Encoding.UTF8);
}
WriteContents(streamWriters[0], streamWriters[1], dt);
zipOutputStream.Close();
}
}
private DataTable CreateDataTable()
{
var dt = new DataTable();
dt.Columns.AddRange(new DataColumn[] { new DataColumn("col1"), new DataColumn("col2"), new DataColumn("col3"), new DataColumn("col4") });
for (int i = 0; i < 100000; i++)
{
var row = dt.NewRow();
for (int j = 0; j < 4; j++)
{
row[j] = j * 1;
}
dt.Rows.Add(row);
}
return dt;
}
private void WriteContents(StreamWriter writer1, StreamWriter writer2, DataTable dt)
{
foreach (DataRow dataRow in dt.Rows)
{
writer1.WriteLine(dataRow[0] + ", " + dataRow[1]);
writer2.WriteLine(dataRow[2] + ", " + dataRow[3]);
}
}
Expected Results:
Both file0.txt and file1.txt need to written.
Actual results:
Only file1.txt file is written all content. file0.txt is blank.
It seems to be the expected behaviour according to the docs
If you don't call Write() between two calls to PutNextEntry(), the first entry is inserted into the zip file as a file of zero size. This may be what you want.
So to me it seems that it is not possible to do what you want through the current API.
Also, as zip file is a continuous sequence of zip entries, it is probably physically impossible to create entries in parallel, as you would have to know the size of each entry before starting a new one.
Perhaps you could just create separate archives and then combine them (if I am not mistaken there was a simple API to do that)
I have the code
while (reader.Read())
{
if (reader[incrementer]!=DBNull.Value){
string playerToInform = reader.GetString(incrementer).ToString();
string informClientMessage = "ULG=" + clientIP + ","; //User Left Game
byte[] informClientsMessage = new byte[informClientMessage.Length];
informClientsMessage = Encoding.ASCII.GetBytes(informClientMessage);
playerEndPoint = new IPEndPoint(IPAddress.Parse(playerToInform), 8001);
clientSocket.SendTo(informClientsMessage, playerEndPoint);
}
incrementer++;
}
which after debugging my code i see contains 4 entries. However only the first result is ever read from the reader. After the first iteration to find if the result returned is null or not the loop starts again and immediately finishes even though there are three more rows to read.
Any ideas as to why this may be occuring would be apprechiated.
edit - this is the reader i used
OleDbDataReader reader = dBConn.DataSelect("SELECT player1_IP, player2_IP, player3_IP, player4_IP FROM running_games WHERE game_name = '" + gameName + "'", updateGameList);
The indexer of DbDataReader (DataReader is something else) or a database specific subclass, returns the value of the specified (by index or name).
While DbDataReader.Read() moves to the next row.
If you want to apply the same logic to multiple columns you need to loop over the columns, and the rows:
while (db.Read()) {
for (var colIdx = 0; colIdx < columnCount. ++colIdx) {
if (!db.IsDbNll(colIdx)) {
string value = db.GetString(colIdx);
// Process value
}
}
}
You're incrementing "incrementer" as if that was the row number, but a DataReader holds only one row per Read() and the indexing is for the field number.
Use this:
while (reader.Read())
{
for(int colNum = 0; colNum < 4; colNum++)
{
if (reader[colNum]!=DBNull.Value)
{
string playerToInform = reader.GetString(colNum).ToString();
string informClientMessage = "ULG=" + clientIP + ","; //User Left Game
byte[] informClientsMessage = new byte[informClientMessage.Length];
informClientsMessage = Encoding.ASCII.GetBytes(informClientMessage);
playerEndPoint = new IPEndPoint(IPAddress.Parse(playerToInform), 8001);
clientSocket.SendTo(informClientsMessage, playerEndPoint);
}
}
}
Incrementer is unnecessary. reader.Read() advances to next record and returns false if there are no more rows.
Check documentation on msdn
hey guys i been trying to read multi-selected csv files and display them in a wpf data grid but i am having problems with the code. here my code below
OpenFileDialog ofd = new OpenFileDialog();
DataTable dtt = new DataTable();
[DelimitedRecord(",")]
private class myCSVFile
{
public string Supplier;
public string Product;
public string Price;
}
private void btnImport_Click(object sender, RoutedEventArgs e)
{
FileHelperEngine engine = new FileHelperEngine(typeof(myCSVFile));
myCSVFile[] result= new myCSvFile[];
foreach (string filepath in ofd.FileNames)
{
for (int i = 0; i < ofd.FileNames.Lenght; i++)
{
result[i] = File.ReadAllLines(System.IO.Path.ChangeExtension(filepath,".csv"));
}
}
dtt.Columns.Add("Suplier", typeof(string));
dtt.Columns.Add("Supplier Type", typeof(string));
dtt.Columns.Add("Price", typeof(string));
foreach (myCSVFile c in result)
{
Console.WriteLine(c.Supplier + " " + c.Product + " " + c.Price);
dtt.Rows.Add(c.Supplier, c.Product, c.Price);
dataGridv.DataContext = dtt.DefaultView;
}
}
i have a file helper reference that i downloaded online to help read the .csv and this works for a single csv file but not for the multi selected. i used ofd.FileNames to get and array of paths and am trying to use a loop to readAlllines of a particular path but it gives me an error at
result[i] = File.ReadAllLines(System.IO.Path.ChangeExtension(filepath,".csv"));
it says cannot implicity convert type 'string[]' to 'Spurs.Import.myCSVFile' please what am i doing wrong. is there another way to do this please am new to c#
You initialize myCSVFile[] result = null; thus result[i] = ... will fail.
EDIT: In the updated question you initialze myCSVFile[] result = new myCSVFile[]; which is invalid syntax. You must give the size of the array.
You traverse the FileNames collections twice in a nested loop
foreach (string filepath in ofd.FileNames) {
for (int i = 0; i < ofd.FileNames.Length; i++) {
result[i] = //somestuff
}
}
Thus result[i] will be assigned n^2 times, if you selected n files.
File.ReadAllLines() delivers an string[]. Each element of the array is on line in the given file. myresult[i] must be an Instance of myCSVFile. You will have to do some additional parsing of the filecontents to create such an instance.
EDIT: I'm not sure, what to say on the parsing part, without knowing a file structure. But, to be honest, if you don't understand why you cannot assign a string[] to some custom class, we won't be able to help you here.
Basically im trying to save a new password and avatar for my twitter type website.
Any help would be appreciated
My coding is:
string newPasswordString = Server.MapPath("~") + "/App_Data/tuitterUsers.txt";
string[] newPasswordArray = File.ReadAllLines(newPasswordString);
string newString = Server.MapPath("~") + "/App_Data/tuitterUsers.txt";
newString = File.ReadAllText(newString);
string[] newArray = newString.Split(' ');
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; i++)
{
newArray[1] = newPasswordTextBox.Text;
newArray[2] = avatarDropDownList.SelectedValue;
newPasswordArray.Replace(" " + Session["Username"].ToString() + " " + Session["UserPassword"].ToString() + " " + Session["UserAvatarID"].ToString() + " ", " " + Session["Username"].ToString() + " " + newPasswordArray[1] + " " + newPasswordArray[2]);
}
}
string newPasswordString = string.Join(Environment.NewLine, newPasswordArray);
File.WriteAllText(Server.MapPath("~") + "/App_Data/tuitterUsers.txt", newPasswordString);
If I understand your problem correctly you need to move the
File.WriteAllText(Server.MapPath("~") + "/App_Data/tuitterUsers.txt", newPasswordArray);
outside the loop, otherwise you rewrite the file at each loop, but this is not enough, you need also to rebuild the Whole text file
string fileToWrite = string.Join(Environment.NewLine, newPasswordArray);
File.WriteAllText(Server.MapPath("~") + "/App_Data/tuitterUsers.txt", fileToWrite);
EDIT: After the code update and the comment below
The looping is totally wrong as well the rebuilding of the array
string userDataFile = Server.MapPath("~") + "/App_Data/tuitterUsers.txt";
string[] userDataArray = File.ReadAllLines(userDataFile);
for(int x = 0; x < userDataArray.Length; x++)
{
string[] info = userData[x].Split(' ');
if(Session["Username"].ToString() == info[0])
{
userData[x] = string.Join(" ", Session["UserName"].ToString(),
newPasswordTextBox.Text,
avatarDropDownList.SelectedValue.ToString());
break;
}
}
string fileToWrite = string.Join(Environment.NewLine, userDataArray);
File.WriteAllText(Server.MapPath("~") + "/App_Data/tuitterUsers.txt", fileToWrite);
Keep in mind that this works for a limited number of users.
If you are lucky and you site becomes the new Twitter, you cannot think to use a solution where you read in memory the names of all your users.
Firstly, what you're doing is A Bad Idea™. Given that a web server can have multiple threads in operation, you can't be certain that two threads aren't going to be writing different data at the same time. The more users you have the larger your user file will be, which means it takes longer to read and write the data, which makes it more likely that two threads will come into conflict.
This is why we use databases for things like this. Instead of operating on the whole file every time you want to read or write, you operate on a single record. There are plenty of other reasons to do it to.
That said, if you insist on using a text file...
If you treat each line in the file as a record - a single user's details in this case - then it makes sense to build a class to handle the content of those records, and make that class able to read and write the line format.
Something like this:
class UserRecord
{
public string Name;
public string Password;
public string Avatar;
public UserRecord(string name, string password, string avatar)
{
Name = name;
Password = password;
Avatar = avatar;
}
// static factory method
public static UserRecord Parse(string source)
{
if (string.IsNullOrEmpty(source))
return null;
string[] parts = source.Split(',');
if (parts.Length < 3)
return null;
return new UserRecord(parts[0], parts[1], parts[2]);
}
// convert to string
public string ToString()
{
return (new string[] { Name, Password, Avatar }).Join(",");
}
}
Adjust the Parse method to handle whatever format you're using for the data in the line, and change the ToString method to produce that format.
Once you have that working, use it to parse the contents of your file like this:
// Somewhere to put the data - a Dictionary is my first choice here
Dictionary<string, UserRecord> users = new Dictionary<string, UserRecord>();
// Don't forget to use 'using' where appropriate
using (TextReader userfile = File.OpenText(userDataFile))
{
string srcline;
while ((srcline = userfile.ReadLine()) != null)
{
UserRecord user = UserRecord.Parse(line);
if (user != null)
users[user.Name] = user;
}
}
Then you can access the user's data by username, manipulate it as required, and save it back out whenever you like.
Writing the data back out from a Dictionary of users is as simple as:
StringBuilder sb = new StringBuilder;
foreach (UserRecord user in users.Values)
{
sb.AppendFormat("{0}\n", user);
}
File.WriteAllText(userDataFile, sb.ToString());
Meanwhile, you have a users collection that you can save for future checks and manipulations.
I still think you should use a database though. They're not hard to learn and they are far better for this sort of thing.
I'm getting content from a database and returning it for Ajax processing via Javascript. Pretty simple stuff. The issue here is that I can't seem to find a good way to loop through the data and the MSDN documentation is obscenely poor for its odbcreader methods.
using (OdbcCommand com = new OdbcCommand("SELECT * FROM pie_data WHERE Pie_ID = ?",
con)) {
if (Request.Form["reference_id"] == "") {
returnError();
} else {
com.Parameters.AddWithValue("", Request.Form["reference_id"]);
com.ExecuteNonQuery();
using (OdbcDataReader reader = com.ExecuteReader()) {
string finalstring = "";
while (reader.Read()) {
if(reader.HasRows) {
finalstring = reader.GetString(9) + ",";
for (int i = 0; i <= 8; i = i + 1) {
finalstring = finalstring + reader.GetValue(i).ToString() + ",";
}
finalstring = finalstring + "|";
reader.NextResult();
}
}
if (finalstring != "") {
finalstring = finalstring.Remove(finalstring.Length -1, 1);
Response.Write(finalstring);
}
}
noredirect = 1;
}
}
However, here is the sample output:
00001,0,Pie Johnson,piesaregreat#yum.com,,,10/7/2010 12:00:00 AM,Bakery,N/A,N/A,
As you can see, the second deliminator is not appearing at all, when it really should. Also, this query, when run in heidisql, returns a good number of rows, not just this one result. Once I have it passed to the Javascript, I can figure it out since I have much more experience with that and I've actually done this before via PHP.
I would use a DataTable and a DataAdapter:
String finalString;
var tblPieData = new DataTable();
using(var con = new OdbcConnection(connectionString))
using (OdbcDataAdapter da = new OdbcDataAdapter("SELECT * FROM pie_data WHERE Pie_ID = ?", con))
{
da.SelectCommand.Parameters.AddWithValue("Pie_ID", reference_id);
da.Fill(tblPieData);
var rowFields = tblPieData.AsEnumerable()
.Select(r => string.Join(",", r.ItemArray));
finalString = string.Join("|", rowFields);
}
You are replacing the value of finalstring in every iteration of the loop. This will cause only the values for a single row to be returned:
finalstring = reader.GetString(9) + ",";
as well as removing the last character at the end of each iteration through the columns in the rows (the pipe, not the trailing comma as I'm expecting you want):
finalstring = finalstring.Remove(finalstring.Length -1, 1);
EDIT:
It also looks like you are skipping over every other record by both looping on reader.Read() as well as calling reader.NextResult()
This line:
finalstring = finalstring.Remove(finalstring.Length -1, 1);
is going to remove the last instance of your pipe-delimiter, so if there is only one record (which there appears to be,) you shouldn't see one.
Unless I am missing something...
EDIT
Actually, if you are looking for two records, you are probably missing the second one because you start your loop with while(reader.Read()) and end it with reader.NextResult();. These will both advance the reader, causing you to miss every other record.
EDIT 2
You are also overwriting finalstring on each iteration; you probably want to append to this (making it a StringBuilder would make the most sense for efficiency's sake, if you don't know how many records to expect.)
Your looping structure with the reader appears to have some problems, not the least of which is the reset of finalAnswer within your loop and giving you only a single result combined with using both .Read() and .NextResult:
Suggested fixes...not tested, so all standard caveats apply :) :
Edited per Mark Averius' comment and OP confirmation of original intent:
using (OdbcDataReader reader = com.ExecuteReader()) {
if (reader.HasRows())
{
string finalstring = "";
while (reader.Read()) {
finalstring = finalstring + reader.GetString(9) + ",";
for (int i = 0; i <= 8; i++) {
finalstring = finalstring + reader.GetValue(i).ToString() + ",";
}
finalstring = finalstring + "|";
}
if (finalstring != "") {
finalstring = finalstring.Remove(finalstring.Length -1, 1);
Response.Write(finalstring);
}
}
}