Archiving each string using by looping through an array - c#

I am currently making a piece of software that will allow the user to enter up to 6 directories, each directory is saved as a string (within an array) the loop is then meant to check through the array and any that are not null i.e. actually have a directory assigned are meant to be zipped into their own archive. This is the code I have so far.
private void ZipIt()
{
int nxtFileNum = 0;
string Destination = #"C:\tmpZip" + nxtFileNum + ".zip";
// Check all fields, check if empty, if not save to Selection array
// Seems a inefficient - Possibly loop through Text box control type and collect?
if (String.IsNullOrEmpty(tboxSelect1.Text) == false) { BckupArray[0] = tboxSelect1.Text; };
if (String.IsNullOrEmpty(tboxSelect2.Text) == false) { BckupArray[1] = tboxSelect2.Text; };
if (String.IsNullOrEmpty(tboxSelect3.Text) == false) { BckupArray[2] = tboxSelect3.Text; };
if (String.IsNullOrEmpty(tboxSelect4.Text) == false) { BckupArray[3] = tboxSelect4.Text; };
if (String.IsNullOrEmpty(tboxSelect5.Text) == false) { BckupArray[4] = tboxSelect5.Text; };
if (String.IsNullOrEmpty(tboxSelect6.Text) == false) { BckupArray[5] = tboxSelect6.Text; };
// Create a new ZipFile entity and then loop through each array member, checking if
// it has an assigned value, if so compress it, if not, skip it.
using (ZipFile ZipIt = new ZipFile())
{
nxtFileNum++;
foreach (String q in BckupArray)
{
if (q != null)
{
ZipIt.AddDirectory(q);
ZipIt.Comment = "This archive was created at " + System.DateTime.Now.ToString("G");
ZipIt.Save(Destination);
}
}
}
}
What I am trying to get this to do is save the first user given location to tmpZip0.7z, the second to tmpZip1.7z and so on however at the moment all it is doing is adding each directory to tmpZip0.zip.
Also as a side note, how would I get it to name each archive after the directory selected to be archived?
I am currently using DotNetZip (Ionic.Zip) dll.
I hope I gave enough information guys.

You need to switch some stuff:
foreach (String q in BckupArray)
{
nxtFileNum++;
if (q != null)
{
using (ZipFile ZipIt = new ZipFile())
{
string Destination = #"C:\tmpZip" + nxtFileNum + ".zip";
ZipIt.AddDirectory(q);
ZipIt.Comment = "This archive was created at " +
System.DateTime.Now.ToString("G");
ZipIt.Save(Destination);
}
}
}
Reasons:
The string Destination is fixed after you created it. It doesn't change, just because you increment nxtFileNum.
You created only one ZipFile and you incremented nxtFileNum only once, because the those were outside of your foreach loop
Putting the part that creates the ZipFile into the if makes sure an instance is only created if it is really used.

Well, you can do this with:
var strings = Controls.OfType<TextBox>()
.Select(x => x.Text)
.Where(text => !string.IsNullOrEmpty(text))
.ToList();
using (ZipFile ZipIt = new ZipFile())
{
nxtFileNum++;
string comment = string.Format("This archive was created at {0:G}",
DateTime.Now);
foreach (string directory in strings)
{
ZipIt.AddDirectory(directory);
ZipIt.Comment = comment;
ZipIt.Save(Destination + "." + nxtFileNum);
}
}
That will obviously pull all the textboxes though. An alternative is to have a collection of type List<TextBox> or something similar instead of the six different variables.
Note that that will always create .1, .2, .3 etc even if the user didn't specify the first three names. Let me know if you want to be absolutely faithful to the positioning the user gave.
It's not clear to me that you should really be reusing the same ZipFile object, by the way. I'd expect this to be more appropriate:
string comment = string.Format("This archive was created at {0:G}",
DateTime.Now);
int fileIndex = 0;
foreach (string directory in strings)
{
fileIndex++;
using (ZipFile zipFile = new ZipFile())
{
zipFile.AddDirectory(directory);
zipFile.Comment = comment;
zipFile.Save(Destination + "." + fileIndex);
}
}
(Note how I've renamed the variables to be more conventional, by the way - variables typically start with a lower case letter.)

Related

How to check if a string is in an array

I am trying to check whether a string is in an array and if continues even though the fileInfo.Name.Contains a string that is in files.Any:
\\FILES LIKE DATABASE.MDB IS IN C:PROJECTS\HOLON\DATABASE.MDB
**if (files.Any((fileInfo.Name.Contains)))**
\\DO SOMETHING
Console.WriteLine(
fileInfo.Name, fileInfo.Length,
If you alread have the filenames collected in an array, then you should either do it this way:
if (files.Any() && files.Contains(fileInfo.Name))
{
// Do something
}
If you just want to check if a file exists then you can use File.Exists:
if(System.IO.File.Exists(fileInfo.Name))
{
// Do Something
}
So you have a collection of full file paths? And you want to check if one or more of those list entries match with a specific file name?
Perhaps this would work for you:
string fileToSearch = "DATABASE.MDB";
bool found = files.Any(fileName => new FileInfo(fileName).Name.ToUpper() == fileToSearch.ToUpper());
Edit:
An alternative to constructing new FileInfo objects would be to use System.IO.Path:
bool found = files.Any(fileName => Path.GetFileName(fileName).ToUpper() == fileToSearch.ToUpper());
Edit 2:
On the other hand, if you want to search for a specific file name, and you want to use the result, you could do something like this:
var fileToSearch = "DATABASE.MDB";
var fileInfo =
(from f in files
let fi = new FileInfo(f)
where fi.Name.ToUpper() == fileToSearch.ToUpper()
select fi).FirstOrDefault();
if (fileInfo != null)
{
if (fileInfo.Exists)
{
Console.WriteLine($"{fileInfo.Name} ({fileInfo.Length} bytes).");
}
else
{
Console.WriteLine($"{fileInfo.Name} (does not exist).");
}
}
I used a LINQ query here for readability. You could use the extension methods (files.Select(f => new FileInfo(f)).Where(fi => fi.Name.ToUpper() == fileToSearch.ToUpper()).FirstOrDefault()) as well, but that's up to you.
if (Array.Exists(files, element => element.Contains(fileInfo.Name)))

Sorting Dictionary by Value?

I have a dictionary. Everything is working fine but sorting. I have even tried a SortedDictionary.
Here's what I am doing, I have a Dictionary that contains FilePath,FileName alright.
Well I am trying to sort by the Value and then put then put the (Keys) into a list.
Here is my code. (UPDATED: ENTIRE METHOD).
public static void DisplayScriptListNames(){
scriptsList.Clear ();
fileInfo = new DirectoryInfo (Application.dataPath);
if (EclecticGlobalSettings._cSharp && isSharp) {
sharpFiles = fileInfo.GetFiles ("*.cs", SearchOption.AllDirectories).ToList();
} if(EclecticGlobalSettings._usScripts && !isSharp) {
javaFiles = fileInfo.GetFiles ("*.js", SearchOption.AllDirectories).ToList();
}
if (EclecticGlobalSettings._cSharp && isSharp) {
// C#
if (sharpFiles.Count != 0) {
foreach (FileInfo i in sharpFiles){
string line = i.Name.ToString ();
string checkPath = Path.GetDirectoryName (i.FullName);
string assetsPath = checkPath.Substring (i.FullName.IndexOf ("Assets"));
if (!assetsPath.Contains("Editor") && !assetsPath.Contains("Standard Assets")) {
scriptDictionary.Add(i.FullName,i.Name);
scriptsFound += 1;
}
}
}
}
if(EclecticGlobalSettings._usScripts && !isSharp){
//JS
foreach (FileInfo i in javaFiles) {
//string line = i.FullName.ToString ();
string line = i.Name.ToString ();
string checkPath = Path.GetDirectoryName (i.FullName);
string assetsPath = checkPath.Substring (i.FullName.IndexOf ("Assets"));
if (!assetsPath.Contains("Editor") && !assetsPath.Contains("Standard Assets")) {
Debug.Log (i.Name);
scriptDictionary.Add(i.FullName,i.Name);
scriptsFound += 1;
}
}
}
foreach (KeyValuePair<string,string> item in scriptDictionary.OrderBy(key=>key.Value)) {
Debug.Log (item);
scriptsList.Add (item.Key);
}
//scriptsList.AddRange (scriptDictionary.Keys);
//scriptsList.Sort (Path.GetFileName);
//foreach (string ii in scriptsList) {
// Debug.Log (ii);
//
//}
}
Okay, the Debug.Log() is Unity's way of a Console.WriteLine. And it does in fact say it's sorting it. But when I do.
scriptsList.Add (item.Key);
It's unorganized as it was before.
Is there some simple little step I am missing? Because the console does in fact say it's sorted perfectly the way I'd like. But for some reason, the scriptsList.Add(item.key) < For the PATH to the file. Says it isn't sorted.
I would do scriptList.Sort(); But remember, the scriptList is the keys (File Paths). Which is why I've been trying to sort via Values (the file names).
Which again, says it does sort them.
Here's an example of what compiler says
C:/Cat.txt, Cat.txt. C:/Dog.txt, Dog.txt. C:/Wolf.txt,
Wolf.txt.
But when I go to add them to the list.
C:/Wolf.txt. C:/Dog.txt. C:/Cat.txt.
I figured it out guys! I feel like a total dummy.... I had left an old part of code at the top of one of my if statements, and I had, had if(scriptList.Count < 1) { Sort It }. Removed that, and now good to go :)

Complicated XML Diff

I have been doing a lot of research on various ways to compare directories of XML files to each other, such that each "actual build" XML file has a matching "template build" XML file. These templates are going to be the actual config files for future builds, so I need to go back through the currently working config files and check for differences in data. These differences will be included as client changeable configurations for the future builds.
I have looked at the XML Diff and Patch (both GUI and VisStu forms) and tried to get differences, but it returns exceptions left and right and can never get the diffGram created. It seems that the XD&P is looking for library elements that no longer exist or have been changed in ways that break it.
Now, I am brand new to XML and LINQ, but I know this is where my answer lies. I have been thinking about creating path strings for every line such that the following xml file:
<configuration>
<title>#ClientOfficialName# Interactive Map</title>
<subtitle>Powered By Yada</subtitle>
<logo>assets/images/mainpageglobe.png</logo>
<style alpha="0.9">
<colors>0xffffff,0x777777,0x555555,0x333333,0xffffff</colors>
<font name="Verdana"/>
<titlefont name="Verdana"/>
<subtitlefont name="Verdana"/>
</style>
Would create strings like:
configuration/title/"#ClientOfficialName# Interactive Map"
configuration/subtitle/"Powered By Yada"
configuration/logo/"assets/iamges/mainpageglobe.png"
configuration/style/alpha/"0.9"
configuration/style/colors/"0xffffff,0x777777,0x555555,0x333333,0xffffff"
and so on like this.
In this manner, I can get every line from the Actual and the Template files and compare them based on "if they are of the same node path, then compare text. If text of all exact siblings does not match, put string into differenceOutput.txt".
So far this is the best concept I have come up with. If anyone can help me achieve this (through this or any other method), I would greatly appreciate it.
I currently have the directory system working no problem, I just have no idea where to start with the population of the string containers from the xml file:
static void Main(string[] args)
{
//Set Up File Paths
var actualBuildPath = #"C:\actual";
var templateBuildPath = #"C:\template";
//Output File Setups
var missingFileList = new List<string>();
var differenceList = new List<string>();
//Iterate through Template Directory checking to see if the Current Build Directory
//has everything and finding differences if they exist
foreach (var filePath in Directory.GetFiles(templateBuildPath, "*.xml", SearchOption.AllDirectories))
{
//Announce Current File
Console.WriteLine("File: {0} ", filePath);
//Make Sure file Exists in current build
if (File.Exists(filePath.Replace(templateBuildPath, actualBuildPath)))
{
//Fill in String Containers as prep for comparison
var templateBuildFormattedXmlLines = PopulateStringContainerFromXML(filePath);
var actualBuildFormattedXmlLines = PopulateStringContainerFromXML(filePath.Replace(templateBuildPath, actualBuildPath));
//COMPARISON SECTION-------------------------------------------------------
xmlFileCompare(templateBuildFormattedXmlLines, actualBuildFormattedXmlLines);
}
//Put missing file into missing file output file
else
missingFileList.Add("Missing: " + filePath.Replace(templateBuildPath, actualBuildPath));
}
//Create Output Folder and Output Files
if (!Directory.Exists(actualBuildPath + #"\Outputs"))
Directory.CreateDirectory(actualBuildPath + #"\Outputs");
File.WriteAllLines(actualBuildPath + #"\Outputs\MissingFiles.txt", missingFileList);
File.WriteAllLines(actualBuildPath + #"\Outputs\differenceList.txt", differenceList);
//Wait to close console until user interacts
Console.ReadLine();
}
Assuming all the configuration files are the same (grammatically) I would recomend to read them into a object and compare these objects, with this you have the possibility to make a finer grained comparison e.g the subtitle may be left out of the comparison.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace XMLTest
{
class Program
{
static void Main(string[] args)
{
//You can use the XDocument.Load() Method to load a xml from a file path rather than a string
string xml = "<configuration><title>#ClientOfficialName# Interactive Map</title><subtitle>Powered By Yada</subtitle><logo>assets/images/mainpageglobe.png</logo><style alpha=\"0.9\"> <colors>0xffffff,0x777777,0x555555,0x333333,0xffffff</colors> <font name=\"Verdana\"/> <titlefont name=\"Verdana\"/> <subtitlefont name=\"Verdana\"/></style></configuration>";
XDocument d = XDocument.Parse(xml);
Configuration c = new Configuration();
c.Title = d.Descendants().Where(x => x.Name == "title").FirstOrDefault().Value;
c.SubTitle = d.Descendants().Where(x => x.Name == "subtitle").FirstOrDefault().Value;
c.Logo = d.Descendants().Where(x => x.Name == "logo").FirstOrDefault().Value;
Configuration.Style s = new Configuration.Style();
s.Alpha = (from attr in d.Descendants().Attributes() select attr).Where(x => x.Name == "alpha").FirstOrDefault().Value;
string tmp = d.Descendants().Where(x => x.Name == "colors").FirstOrDefault().Value;
foreach (string str in tmp.Split(','))
{
s.Colors.Add(Convert.ToInt32(str, 16));
}
s.FontName = (from attr in d.Descendants().Where(x=>x.Name =="font").Attributes() select attr).Where(x => x.Name == "name").FirstOrDefault().Value;
s.TitleFontName = (from attr in d.Descendants().Where(x => x.Name == "titlefont").Attributes() select attr).Where(x => x.Name == "name").FirstOrDefault().Value;
s.SubtitleFontName = (from attr in d.Descendants().Where(x => x.Name == "subtitlefont").Attributes() select attr).Where(x => x.Name == "name").FirstOrDefault().Value;
c.MyStyle = s;
Console.WriteLine(c.ToString());
Console.ReadKey();
}
}
public class Configuration : IComparable
{
public string Title;
public string SubTitle;
public string Logo;
public Style MyStyle;
public override string ToString()
{
return string.Format("Configuration : Title: {0}, Subtitle {1}, Logo {2}, Style: {3}",Title,SubTitle,Logo,MyStyle.ToString());
}
public class Style
{
public string Alpha;
public List<int> Colors = new List<int>();
public string FontName;
public string TitleFontName;
public string SubtitleFontName;
public override string ToString()
{
string s = "Alpha :" +Alpha;
s+= ", Colors: ";
foreach(int i in Colors){
s += string.Format("{0:x},",i);
}
s += " FontName :" + FontName;
s += " TitleFontName :" + TitleFontName;
s += " SubTitleFontName :" + SubtitleFontName;
return s;
}
}
public int CompareTo(object obj)
{
if ((obj as Configuration) == null)
{
throw new ArgumentException("Not instance of configuration");
}
//Very simple comparison, ranks by the title in the comparison object, here you could compare all the other values e.g Subtitle , logo and such to test if two instances are Equal
return String.Compare(this.Title, ((Configuration)obj).Title, true);
}
}
}
For a more Complete overview of Implementing Comparison see: https://msdn.microsoft.com/en-us/library/system.icomparable.compareto%28v=vs.110%29.aspx

improve way of testing in C# if file format matches any in list

In short I'm building a treeview like structure of nodes. I'm looping through each folder and each file within the folder looking for specific file formats. I know the function im using for IsValidFileFormat is not ideal. I want to be able to pass it list of fileformats such as {.txt, .ms, .png} and upon first test returning true it returns True. That way it doesn't continue looping through the other formats if it doesn't need to.
The below example loops through each directory using a try catch so it doesn't error on folders which don't have permission.
// tests if given filepath has a compatible extension
static bool IsValidFileType(string filename)
{
bool results = false;
string ext = Path.GetExtension(filename);
// add multiple acceptable file extensions
if (string.Equals(".mse", ext, StringComparison.OrdinalIgnoreCase)) return true;
if (string.Equals(".ms", ext, StringComparison.OrdinalIgnoreCase)) return true;
return results;
}
public static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in directoryInfo.GetDirectories())
{
try
{
// ignore direcotories which start with an 'underscore'
if (!directory.Name.StartsWith("_"))
{
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
}
}
catch (UnauthorizedAccessException) { }
}
foreach (var file in directoryInfo.GetFiles())
{
if (IsValidFileType(file.FullName))
{
TreeNode node = new TreeNode(file.Name, 1, 1);
node.Tag = file.FullName;
node.ForeColor = toolColor;
directoryNode.Nodes.Add(node);
// add to global fileList which is used for searches
fileList.Add(file.FullName);
}
}
// if (directoryNode.Nodes.Count != 0)
return directoryNode;
}
you could make an extension method like this:-
public static IEnumerable<FileInfo> GetFilesByExtensions(this DirectoryInfo dir,
params string[] extensions)
{
if (extensions == null)
throw new ArgumentNullException("extensions");
IEnumerable<FileInfo> files = dir.EnumerateFiles();
return files.Where(f => extensions.Contains(f.Extension));
}
and the use that like :
directoryInfo.GetFilesByExtensions(".ms",".mse")
which will return only the files you are interested in.
All you really need to do is create a list or array (etc) to hold the acceptable file types, then break as soon as you hit one.
static bool IsValidFileType(string filename)
{
bool results = false;
string ext = Path.GetExtension(filename);
List<string> fileTypes = new List<string>();
fileTypes.Add("exe");
fileTypes.Add("mse");
//etc
for (int i = 0; i < fileTypes.Count; i++)
{
if (ext == fileTypes[i])
{
results = true;
break;
//or just return true;
}
}
return results;
}
You could also move this list/array/whatever out to the class level and use the same list for different methods.
You can also use a LINQ query to acheive the same result:
results = fileTypes.Any(fileType => fileType.Equals(ext));
This will look through the list of fileTypes and return true if any match the right side of => -- In this case, String.Equals(ext).
Note that the LINQ method will return an ArgumentNullException if either fileTypes or ext is null, so if they CAN be null, make sure you check that first.
EDIT: If using the LINQ method, you'll still need to test for case. You can do that in a few different ways:
Your current method, using an overload of String.Equals: ... => fileType.Equals(ext, StringComparison.OrdinalIgnoreCase));
In-line: results = fileTypes.Any(fileType => fileType.ToLower().Equals(ext.ToLower()));
Same as above, but set the variable ext to lower case: 'string ext = Path.GetExtension(filename).ToLower();
etc.
Personally I think the StringComparison.OrdinalIgnoreCase is the cleaner way, the other methods are kind of ugly.
How are you fetching list of files? You can use Directory.EnumerateFiles Method (String, String, SearchOption) https://msdn.microsoft.com/en-us/library/dd383571(v=vs.110).aspx and use the searchPattern parameter. You just need to call it for every extension then concat the lists. This is an enumeration so it will only load the file info as you request it.
Note: you can also use Directory.GetFiles https://msdn.microsoft.com/en-us/library/ms143316(v=vs.110).aspx to load them immediately.
If I understand your questions correctly, you want your "IsValidFileType" function to accept a list of acceptable formats and see if a file matches one of them. You also want "true" returned at the first match. This should work:
static bool IsValidFileType(string filename, List<string> AcceptableExtensions)
{
string ext = Path.GetExtension(filename);
foreach (string AccExt in AcceptableExtensions)
if (ext.ToUpper() == AccExt.ToUpper())
return true;
return false;
}
Then you can call it like this:
List<string> AcceptableExtensions = new List<string>();
AcceptableExtensions.Add(".mse");
AcceptableExtensions.Add(".ms");
foreach (var file in directoryInfo.GetFiles())
{
if (IsValidFileType(file.FullName, AcceptableExtensions))
{
TreeNode node = new TreeNode(file.Name, 1, 1);
node.Tag = file.FullName;
node.ForeColor = toolColor;
directoryNode.Nodes.Add(node);
// add to global fileList which is used for searches
fileList.Add(file.FullName);
}
}

Removing the path from a GetFiles return

I am attempting to simply for this bit create a list of the songs within the MyMusic folder and display them in a listbox. The strings will also be used later for voice commands but adding those will not be a problem. My problem is that despite my attempts, I have been unable to remove the path from the displayed name.
InitializeComponent();
string path = #"C:\Users\Toby\Music";
string[] Songs = Directory.GetFiles(path, "*.mp3", SearchOption.TopDirectoryOnly);
List<string> SongList = new List<string>();
int pathlngth = path.Length;
int i = 0;
string fix;
foreach (string Asong in Songs)
{
fix = Asong.Remove(0,pathlngth);
fix = Asong.Remove(Asong.Length-4);
SongList.Add(fix);
i = i + 1;
}
SongList.Add("");
SongList.Add("There are " + i + " songs");
SongBox.Datasource = SongList;
To me at least, this should work. However the results from my Listbox will appear as so:
C:\Users\Toby\Music\Across the line
C:\Users\Toby\Music\Behind Closed Doors
And so on...
Any idea what's wrong? I managed to finally remove the extension. I have tried replacing pathlngth with path.Length to no change at all.
To get FileName from a path
string strSongName = System.IO.Path.GetFileName(FileFullPath);
To get FileNameWithoutExtension from a path
string sFileNameWithOutExtension = Path.GetFileNameWithoutExtension(FileFullPath);
Your Solution:
List<string> SongList = new List<string>();
string path = #"C:\Users\Toby\Music";
string[] Songs = Directory.GetFiles(path, "*.mp3", SearchOption.TopDirectoryOnly);
SongList.Add("");
SongList.Add("There are " + Songs.Length + " songs");
foreach (string Asong in Songs)
{
string sFileNameWithOutExtension = Path.GetFileNameWithoutExtension(Asong);
SongList.Add(sFileNameWithOutExtension);
}
SongBox.DataSource = SongList;
There is an API that already does exactly that - Path.GetFileName
foreach (string song in Songs)
{
SongList.Add(System.IO.Path.GetFileName(song));
}
This will give you the name + extension, if you want to omit the extension you can use Path.GetFileNameWithoutExtension instead.
You are assigning the value of "fix" and then immediately overwriting it.
fix = Asong.Remove(0,pathlngth);
fix = Asong.Remove(Asong.Length-4);
Should probably be
fix = Asong.Remove(0,pathlngth);
fix = fix.Remove(Asong.Length-4);
The other option is to just use Path.GetFileName(Asong); but you'll still need to manipulate it to remove the extension.
If you care for saving the path and just don't want to display it, than any of the previous solutions will be good for you.
If you only care of of the file names than the following will be just for you.
var resultFileNames = Songs.Select(s => Path.GetFileName(s));
This will produce a new list based on Songs but will only store their file names.

Categories

Resources