How to create multiple Files regarding to an object-attribute - c#

A method receives an object list each entity has the following attributes:
name
room
The list contains, for example, the following objects:
name: pc01 room: a1
name: pc02 room: a1
name: pc01 room: XYZ
name: pc02 room: XYZ
name: pc03 room: XYZ
The number of rooms and pc is not known before the program start, as well as the name of the rooms.
I would like to create a file for each room.
Each object should be printed in a line of the object.
For the example above it would mean:
File 1
File-Name = a1.txt
File-Content:{
pc01a1
pc02a1
}
File 2
File-Name = XYZ.txt
File-Content:{
pc01XYZ
pc02XYZ
pc03XYZ
}
I know how to loop the object-list and how to write into files, but I don't know, how to create dynamic file names.
I tried the following (results = list of objects):
foreach (PC currentPc in results)
{
//Path to the writer in relation to the attribute room
var pathFile = "D:\\" + currentPc.room + ".txt";
StreamWriter writerRoom = new StreamWriter(pathFile);
//Write Line
writerRoom.WriteLine(currentPc.room.ToLower() + currentPc.name.ToLower());
//Close Writer
writerRoom.Close();
}
The code creates the correct files for each room. The problem is, that each file only contains the last object of the list. I would like to have each object of that room.

Each time you encounter the same room again, you are overwriting the file you just created. Instead use AppendAllText:
foreach (PC currentPc in results)
{
// Path to file in relation to the attribute room
var filePath = $#"D:\{currentPc.room}.txt";
var textToAppend = $"{currentPc.room.ToLower()}{currentPc.name.ToLower()}\r\n";
System.IO.File.AppendAllText(filePath, textToAppend);
}
This method creates the file if it doesn't exist, appends to it and closes it again. \r\n is a line break to make it look nice.
EDIT:
To empty/delete the files you would need a separate loop to run before the generating one:
// Delete all files before running generating loop
foreach (PC currentPc in results)
{
var filePath = $#"D:\{currentPc.room}.txt";
// Check to see if the file exists
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
}
}

Related

Convert result of File.ReadAllBytes as File object without actually writing it

One Module reads some files using
File.ReadAllBytes("path")
and stores the result in a database table.
Later I take the result from the table and normaly use
File.WriteAllBytes("outputPath", binary.Data)
to write the file back.
Now I have to change the content of the file. Of course I can write the file to a temp folder, read it back in as a File object, change it, write it back to the destination folder.
But is there a smarter way to do that? Create the File object directly out of the binary data?
Got a solution. Its the Encoding Class:
var binary = GetBinaryFileFromDatabase();
var inputLines = Encoding.Default.GetString(binary).Split('\n');
var outputLines = new List<string>();
foreach (var line in inputLines)
{
var columns = line.Split(';');
if (columns.Length > 28 && columns[28] != null && columns[28].Length > 0)
{
columns[28] = columns[28].Replace(Path.GetFileName(columns[28]), "SomeValue");
}
outputLines.Add(string.Join(";", columns));
}
File.WriteAllLines(outputPath, outputLines);
Thanks everybody!

writing collection of collection to csv file using csv helper

I am using csvhelper to write data to a csv file. I am using C# and VS2010. The object I wish to write is a complex data type object of type Dictionary< long,List<HistoricalProfile>>
Below is the code I am using to write the data to the csv file:
Dictionary<long, List<HistoricalProfile>> profiles
= historicalDataGateway.GetAllProfiles();
var fileName = CSVFileName + ".csv";
CsvWriter writer = new CsvWriter(new StreamWriter(fileName));
foreach (var items in profiles.Values)
{
writer.WriteRecords(items);
}
writer.Dispose();
When it loops the second time I get an error
The header record has already been written. You can't write it more than once.
Can you tell me what I am doing wrong here. My final goal is to have a single csv file with a huge list of records.
Thanks in advance!
Have you seen this library http://www.filehelpers.net/ this makes it very easy to read and write CSV files
Then your code would just be
var profiles = historicalDataGateway.GetAllProfiles(); // should return a list of HistoricalProfile
var engine = new FileHelperEngine<HistoricalProfile>();
// To Write Use:
engine.WriteFile("FileOut.txt", res);
I would go more low-level and iterate through the collections myself:
var fileName = CSVFileName + ".csv";
var writer = new StreamWriter(fileName);
foreach (var items in profiles.Values)
{
writer.WriteLine(/*header goes here, if needed*/);
foreach(var item in items)
{
writer.WriteLine(item.property1 +"," +item.propery2...);
}
}
writer.Close();
If you want to make the routine more useful, you could use reflection to get the list of properties you wish to write out and construct your record from there.

Generating IDs using StreamReader (Last Line)

I'm trying to generate Item IDs using StreamReader on my .CSV file (It has to be a .csv file). The Item ID should start at 1000 and go up (1001, 1002, etc.)
Right now, if the user presses "Generate ID", it will search the entire file for the value "1000", if it doesn't exist, it will write "1000" in the textbox.
Here's what I need help with: If the file contains "1000", I want it to read the LAST line, increase it by 1, then write the value in the textbox.. So, if my last value is 1005 in the .csv file, I want it to write 1006 in the textbox.
private void GenerateID_Click(object sender, EventArgs e)
{
try
{
string searchString = "1000";
using (StreamReader sr = new StreamReader("file.csv"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (line.Contains(searchString))
{
/* If file contains 1000, read the LAST line
* (Whatever number that may be: 1001, 1002, 1003, etc.)
* and increase that number by 1, then write to textbox. */
}
else
{
invItemIDField.Text = Convert.ToString("1000");
}
}
}
}
catch (Exception)
{
MessageBox.Show("The file could not be read");
}
}
I suggest you use FileHelpers. It's the most suitable library for reading CSV files.
To install this, you need to install first NuGet. Once installed, go to Tools > Library Package Manager > Package Manager Console:
Then, type in: Install-Package Filehelpers
You're good to go!
Import FileHelpers to your code
using FileHelpers;
Create a class that describes the structure of your CSV:
DelimitedRecord("'")]
public class MyCsv
{
public int Column1; // Your ID column
public string SomeOtherColumn;
}
Create a List<MyCsv>:
List<MyCsv> myList;
Then, to load your CSV:
FileHelperEngine<MyCsv> engine = new FileHelperEngine<MyCsv>();
myList = new List<MyCsv>(engine.ReadFile("my.csv")); // replace with your filename or variable containing the filename
You can now read your CSV by accessing the list myList:
foreach(MyCsv line in myList) {
// Do something;
}
Each object inside that list corresponds to each row in your CSV. In order to access the first column of a row (given the foreach loop above):
line.Column1
So, if you need to compare values, you can either use LINQ or do the traditional loop-search:
foreach(MyCsv line in myList) {
if (txtId.Text == line.Column1.ToString()) {
found = true;
break;
}
}
Then, to get the id of the last row:
myList.[myList.Count - 1].Column1
You can do the rest. Cheers!
Here's my go at it, it's slighlty different from yours, but it works. Granted there are things you must consider, such as are the elements surrounded in quotes, are the line breaks \r\n, and the like:
int TextBoxValue = 1000;
var reader = new StreamReader(File.OpenRead(#"C:\Users\J\Desktop\New Text Document (4).txt"));
var contents = reader.ReadToEnd().Split(new string[] {"\r\n"}, StringSplitOptions.None);
var iValueExists = (from String sLine in contents
where sLine.Contains("1000")
select sLine).Count();
if (iValueExists > 0)
{
TextBoxValue = int.Parse(contents.Last().Split(new string[] {","}, StringSplitOptions.None).First()) + 1;
}
invItemIDField.Text = TextBoxValue;
reader.Close();

Delete files from directory if filename contains a certain word

I need to check a directory to see if there are any files whose file name contains a specific keyword and if there are, to delete them. Is this possible?
For example, delete all existing files in "C:\Folder" whose file name contains the keyword "Apple".
To expand on Henk's answer, you need:
string rootFolderPath = #"C:\\SomeFolder\\AnotherFolder\\FolderCOntainingThingsToDelete";
string filesToDelete = #"*DeleteMe*.doc"; // Only delete DOC files containing "DeleteMe" in their filenames
string[] fileList = System.IO.Directory.GetFiles(rootFolderPath, filesToDelete);
foreach(string file in fileList)
{
System.Diagnostics.Debug.WriteLine(file + "will be deleted");
// System.IO.File.Delete(file);
}
BE VERY CAREFUL!
Note that I've commented out the delete command. Run it and test it carefully before you let it actually delete anything!
If you wish to recursively delete files in ALL subfolders of the root folder, add ,System.IO.SearchOption.AllDirectories); to the GetFiles call.
If you do this it is also a very good idea to refuse to run if the rootFolderPath is less than about 4 characters long (a simple protection against deleting everything in C:\ - I've been there and done that and it's not fun!!!)
You can use System.IO.Directory.GetFiles() to a list of the files, in string[] format.
Then you can use System.IO.File.ReadAllText() to read complete files, or if they are very big, open a TextReader with System.IO.File.OpenText().
If you are looking for a literal keyword, String.Contains() is all you need.
Deleting a file can be done with System.IO.File.Delete(). Make sure the file is closed again.
Edit, 2 examples of GetFiles():
string[] fileNames = System.IO.Directory.GetFiles(#"C:\");
string[] fileNames = System.IO.Directory.GetFiles(#"C:\", #"*.sys");
new List<string>(Directory.GetFiles(#"C:\Folder")).ForEach(file => {
if (file.IndexOf("apple", StringComparison.OrdinalIgnoreCase) >= 0)
File.Delete(file);
});
or
new List<string>(Directory.GetFiles(#"C:\Folder")).ForEach(file => {
Regex re = new Regex("apple", RegexOptions.IgnoreCase);
if (re.IsMatch(file))
File.Delete(file);
});
More or less, this:
string DeleteThis = "apple";
string[] Files = Directory.GetFiles(#"C:\Folder");
foreach (string file in Files)
{
if (file.ToUpper().Contains(DeleteThis.ToUpper()))
{
File.Delete(file);
}
}
new List<string>(Directory.GetFiles(#"C:\Folder")).ForEach(file => { if (file.ToUpper().Contains("apple".ToUpper())) File.Delete(file); });

Get list of titles from xml files

I am trying to get titles of xml files from a folder call "bugs".
My code:
public virtual List<IBug> FillBugs()
{
string folder = xmlStorageLocation + "bugs" + Path.DirectorySeparatorChar;
List<IBug> bugs = new List<IBug>();
foreach (string file in Directory.GetFiles(folder, "*.xml", SearchOption.TopDirectoryOnly))
{
var q = from b in bugs
select new IBug
{
Title = b.Title,
Id = b.Id,
};
return q.ToList();
}
return bugs;
}
But I'm not geting out the titles from all the xml files in the folder "bugs".
the biggest problem is to get eatch files to singel string and not string[].
Your code as written doesn't make any sense. Perhaps you meant something more like this:
public virtual List<IBug> FillBugs()
{
// is this actually correct or did you mix up the concatenation order?
// either way, I suggest Path.Combine() instead
string folder = xmlStorageLocation + "bugs" + Path.DirectorySeparatorChar;
List<IBug> bugs = new List<IBug>();
foreach (string file in Directory.GetFiles(folder, "*.xml",
SearchOption.TopDirectoryOnly))
{
// i guess IBug is not actually an interface even though it starts
// with "I" since you made one in your code
bugs.Add(new IBug {
Title = file, Id = 0 /* don't know where you get an ID */ });
}
return bugs;
}
"from b in bugs" selects from an empty list. you need to initialize bugs from the file at the start of your foreach loop
Do you need a backslash (Path.DirectorySeparatorChar) between xmlStorageLocation and "bugs"?
You don't use file in your loop anywhere - Is that correct or did you miss to push it into the collection?

Categories