C# Remove file extension from string list - c#

What my program does is basically it lists file names (including it's extension) from a directory into a listbox. It then has a sorting function which sorts the list strings into alphabetical order.
Lastly it has a binary search function that allows the users to input any string which the program will then compare and display the matched results into a listbox.
Now, all these functions work perfectly however I can't seem to remove the extension off of a file name after a search.
For example in the scanning and sorting part it lists the file names as: filename.mp3
Now, what I want it do when the searching button is clicked is to remove the file extension and display just the filename.
private void buttonSearch_Click(object sender, RoutedEventArgs e)
{
listBox1.Items.Clear();
string searchString = textBoxSearchPath.Text;
int index = BinarySearch(list1, 0, list1.Count, searchString);
for (int n = index; n < list1.Count; n++)
{
//Removes file extension from last decimal point ''not working''
int i = list1[n].LastIndexOf(".");
if (i > 0)
list1[n].Substring(0, i);
// Adds items to list
if (list1[n].IndexOf(searchString, StringComparison.OrdinalIgnoreCase) != 0) break;
listBox1.Items.Add(list1[n]);
}
MessageBox.Show("Done");
}

C# is so easy that if something takes more than 2 minutes, there probably is a method for it in the Framework.

The Substring method returns a new fresh copy of the string, copied from the source one. If you want to "cut the extension off", then you must fetch what Substring returns and store it somewhere, i.e.:
int i = list1[n].LastIndexOf(".");
if (i > 0)
list1[n] = list1[n].Substring(0, i);
However, this is quite odd way to remove an extension.
Firstly, use of Substring(0,idx) is odd, as there's a Remove(idx)(link) which does exactly that:
int i = list1[n].LastIndexOf(".");
if (i > 0)
list1[n] = list1[n].Remove(i);
But, sencondly, there's even better way of doing it: the System.IO.Path class provides you with a set of well written static methods that, for example, remove the extension (edit: this is what L-Three suggested in comments), with full handling of dots and etc:
var str = System.IO.Path.GetFileNameWithoutExtension("myfile.txt"); // == "myfile"
See MSDN link
It still returns a copy and you still have to store the result somewhere!
list1[n] = Path.GetFileNameWithoutExtension( list1[n] );

Try like below ite will help you....
Description : Filename without Extension
listBox1.Items.Add(Path.GetFileNameWithoutExtension(list1[n]));

Use Path.GetFileNameWithoutExtension

Use Path.GetFileNameWithoutExtension method. Quite easy I guess.
http://msdn.microsoft.com/en-us/library/system.io.path.getfilenamewithoutextension.aspx

Not sure how you've implemented your directory searching, but you can leverage LINQ to your advantage in these situations for clean, easy to read code:
var files = Directory.EnumerateFiles(#"\\PathToFiles")
.Select(f => Path.GetFileNameWithoutExtension(f));
If you're using .NET 4.0, Enumerate files seems to be a superior choice over GetFiles. However it also sounds like you want to get both the full file path and the file name without extension. Here's how you could create a Dictionary so you'd eliminate looping through the collection twice:
var files = Directory.EnumerateFiles(#"\\PathToFiles")
.ToDictionary(f => f, n => Path.GetFileNameWithoutExtension(n));

A way to do this if you don't have a file path, just a file Name
string filePath = (#"D:/" + fileName);
string withoutExtension = Path.getFileNameWithoutExtension(filePath);

Related

How to remove rows from IEnumerable

I'm loading CSV Files into a IEnumerable.
string[] fileNames = Directory.GetFiles(#"read\", "*.csv");
for (int i = 0; i < fileNames.Length; i++)
{
string file = #"read\" + Path.GetFileName(fileNames[i]);
var lines = from rawLine in File.ReadLines(file, Encoding.Default)
where !string.IsNullOrEmpty(rawLine)
select rawLine;
}
After that I work with the Data but now there are couple of Files that are pretty much empty and only have ";;;;;;" (the amount varies) written in there.
How can I delete those rows before working with them and without changing anything in the csv files?
If the amount of ; characters per line is variable, this is what your "where" condition should look like:
where !string.IsNullOrEmpty(rawLine) && !string.IsNullOrEmpty(rawLine.Trim(';'))
rawLine.Trim(';') will return a copy of the string with all ; characters removed. If this new string is empty, it means this line can be ignored, since it only contained ; characters.
You can't remove anything from an IEnumerable(like from a List<T>), buty ou can add a filter:
lines = lines.Where(l => !l.Trim().All(c => c == ';'));
This won't delete anything, but you won't process these lines anymore.
You can't remove rows from an enumerable - https://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx.
Instead try creating a new array with the filtered data or filter it on the where clause that you presented like:
string[] fileNames = Directory.GetFiles(#"read\", "*.csv");
for (int i = 0; i < fileNames.Length; i++)
{ string file = #"read\" + Path.GetFileName(fileNames[i]);
var lines = from rawLine in File.ReadLines(file, Encoding.Default) where !string.IsNullOrEmpty(rawLine) && rawLine != ";;;;;;" select rawLine;}
There are multiple solution.
Convert enumerable to List, then delete from List. This is bit expensive.
Create one function. // You can apply multiple filter in case required.
public IEnumrable<string> GetData(ref IEnumrable<string> data)
{
return data.Where(c=> !String.Equals(c,"<<data that you want to filter>>");
}
As another option to read CSV file is to make use of TextFieldParser class. It has CommentTokens and Delimiters which may help you on this.
Specifying ; as a CommentTokens may help you.
Tutorial

Get files from directory and subdirectories quickly order by latest creation date

I am looking for a method that will take a file extension type and directory and return all the files within this directory and sub directories ordered by the latest creation date, i.e. latest files first.
So far i have identified the following method which is meant to be fast however is there a better way of doing this and i need it to return FileInfo rather than a string and ordered as described above.
public static IEnumerable<string> GetFileList(string fileSearchPattern, string rootFolderPath)
{
Queue<string> pending = new Queue<string>();
pending.Enqueue(rootFolderPath);
string[] tmp;
while (pending.Count > 0)
{
rootFolderPath = pending.Dequeue();
tmp = Directory.GetFiles(rootFolderPath, fileSearchPattern);
for (int i = 0; i < tmp.Length; i++)
{
yield return tmp[i];
}
tmp = Directory.GetDirectories(rootFolderPath);
for (int i = 0; i < tmp.Length; i++)
{
pending.Enqueue(tmp[i]);
}
}
}
When I have researched this problem space I've found there isn't a fast way to do this. The reason is no matter what approach you take, you end up having to go to the Operating System for the list of files in a directory. And the file system doesn't cache / index the way a search engine would. So you end up need to recrawl the file system yourself.
Once you have the raw information, however, you can index it yourself.
The below will work for your purposes. You want to use Directory.EnumerateFiles(...) to allow the file list to use less memory up front. It will only go looking for the next element when you ask for it instead of loading the entire collection in to memory at the start.
Directory.EnumerateFiles(rootFolderPath, fileSearchPattern, System.IO.SearchOption.AllDirectories).OrderBy(file => new FileInfo(file).CreationTime)
One additional consideration. Since you are doing a fairly blind search through the file system, if you try to enumerate a file and an exception is thrown, it will invalidate the enumerator causing it to exit without finishing. I have posted a solution to that problem here
Directory.GetFiles does have an option to search recursively.
The following should work, although I haven't tried it.
IEnumerable<FileInfo> GetFileList(string directory, string extension)
{
return Directory.GetFiles(directory, "*" + extension, SearchOption.AllDirectories)
.Select(f => new FileInfo(f))
.OrderByDescending(f => f.CreationTime);
}

How to extract an unknown amount of text from a file

This sort of ties back to a question I had earlier about a regex to search for a method containing a particular string, and someone suggested I use this MS tool called Roslyn but it's not available for VS2010 since 2012 came out.
So I'm writing this small utility to keep a list of every file in my solution that contains a particular method declaration (something like 3k of the 25k files overload this method). Then I simply want to filter that list of files to only ones that contain += inside the body of the method.
static void DirSearch(string dir)
{
string[] files = Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories);
foreach (var file in files)
{
var contents = File.ReadAllText(file);
if (contents.Contains("void DetachEvents()"))
{
//IF DetachEvents CONTAINS += THEN...
WriteToFile(file);
}
}
}
This method iterates over all the folders and writes the file name to a text file if it contains the key method, but I have no idea how to extract just whatevers in the method body, since it's overloaded all 3K instances of the method are different.
Would the best approach to be get the index of the method name, then the index of each { and } until I encounter the next accessor modifier (signifying I've gotten to the end of DetachEvents)? Then I could just search between indexOfMethod and indexOfEndMethod for +=.
But it sounds really sloppy, I was hoping someone might have a better idea?
Do you have to do this in code? Is this a one time utility to identify the problem methods? Why not use something like Notepad++ and it's Find in Files capabilities. You can filter your find pretty easily and even apply regex (I think). From there you can copy the results which include the file name (i.e. someclassfile.cs) and get a list from there.
I wrote this really sloppy winform that lets the user type in the folder to the code base, the method name, and the flagrant text they're looking for. Then it loops over every file in the directory and calls this method on a string that contains all the text of the file. It returns true if the user-entered flagrant data is present, then the method that calls this adds the file its on to a list. Anyways, here's the major code:
private bool ContainsFlag(string contents)
{
int indexOfMethodDec = contents.IndexOf(_method);
int indexOfNextPublicMethod = contents.IndexOf("public", indexOfMethodDec);
if (indexOfNextPublicMethod == -1)
indexOfNextPublicMethod = int.MaxValue;
int indexOfNextPrivateMethod = contents.IndexOf("private", indexOfMethodDec);
if (indexOfNextPrivateMethod == -1)
indexOfNextPrivateMethod = int.MaxValue;
int indexOfNextProtectedMethod = contents.IndexOf("protected", indexOfMethodDec);
if (indexOfNextProtectedMethod == -1)
indexOfNextProtectedMethod = int.MaxValue;
int[] indeces = new int[3]{indexOfNextPrivateMethod,
indexOfNextProtectedMethod,
indexOfNextPublicMethod};
int closestToMethod = indeces.Min();
if (closestToMethod.Equals(Int32.MaxValue))
return false; //This should probably do something different.. This condition is true if the method you're reading is the last method in the class, basically
if (closestToMethod - indexOfMethodDec < 0)
return false;
string methodBody = contents.Substring(indexOfMethodDec, closestToMethod - indexOfMethodDec);
if (methodBody.Contains(_flag))
return true;
return false;
}
Plenty of room for improvement, this is mostly just a proof-of-concept thing that'll get used maybe twice per year internally. But for my purposes it worked. Should be a good starting-point for something more sophisticated if anyone needs it.

An elegant way of renaming a file if it already exists when archiving

I'd like to write an archiving function that takes all files in a folder and move them into an archive sub folder using the current date. The process could be run several times in a day and therefore needs to handle duplicates.
The rules for the archiving are below:
If a file name already exists, I'd like to add an underscore "_" and
a number (starting from 1).
If the file name has already been modified the I'd like
to increment the number.
I can do this using lots of File.Exist and LastIndexOf calls but is there a more elegant way? maybe with LINQ?
EDIT:
This is the code I have already. It's a bit rough and ready but it gives an idea of what I want to do.
/// <summary>
/// Move the local file into the archive location.
/// If the file already exists then add a counter to the file name or increment the existing counter
/// </summary>
/// <param name="LocalFilePath">The full path of the file to be archived</param>
/// <param name="ArchiveFilePath">The proposed full path of the file once it's been archived</param>
private void ArchiveFile(string LocalFilePath, string ArchiveFilePath)
{
// Ensure the file name doesn't already exists in the location we want to move it to
if (File.Exists(ArchiveFilePath) == true)
{
// Does the archive file have a number at the end?
string[] archiveSplit = Path.GetFileNameWithoutExtension(ArchiveFilePath).Split('_');
if( archiveSplit.Length == 1)
{
// No number detected so append the number 1 to the filename
string newArcFileName = string.Format("{0}_1.{1}",
Path.GetFileNameWithoutExtension(ArchiveFilePath), Path.GetExtension(ArchiveFilePath));
// Create the new full path
string newArcPath = Path.Combine(Path.GetDirectoryName(ArchiveFilePath), newArcFileName);
// recursively call the archive folder to ensure the new file name doesn't exist before moving
ArchiveFile( LocalFilePath, newArcPath);
}
else
{
// Get the number of the last element of the split
int lastNum = Convert.ToInt32( archiveSplit.Last().Substring(1) ) +1;
// Rebuild the filename using the incremented number
string newArcFileName = archiveSplit[0];
for (int i = 1; i < archiveSplit.Length; i++)
{
newArcFileName += archiveSplit[i];
}
// finally add the number and extension
newArcFileName = string.Format("{0}_{1}.{2}", newArcFileName, lastNum, Path.GetExtension(ArchiveFilePath));
// Create the new full path
string newArcPath = Path.Combine(Path.GetDirectoryName(ArchiveFilePath), newArcFileName);
// recursively call the archive folder to ensure the new file name doesn't exist before moving
ArchiveFile(LocalFilePath, newArcPath);
}
}
else
{
// There is no file with a matching name
File.Move(LocalFilePath, ArchiveFilePath);
}
}
The Directory class has a method to receive a list of all files within. That method allows you to specify a filter string, like so:
Directory.GetFiles(directoryPath, filterString);
If you already know your filename prefix, you can use that filter string to get all the files within that pattern:
filterString = string.Format("{0}_*.{1}", defaultFileNameWithoutExtension, defaultExtension);
You can then simply select the one with the highest suffix, extract the suffix digits, increase it and build your new (unused) file name.
Disclaimer: This was written by heart, feel free to edit in case of errors :)
File.Exists would still need to be called even if you use LINQ, that will not change.
I suggest keeping things simple - looping with File.Exists and LastIndexOf is a suitable solution, unless performance is imperative.
Maybe, you should use the "Path" API and use EndsWith instead of LastIndexOf :).
You can also have a file wich store the tree of files. (Took an eye to rsync)
Do you really want to make several duplicates of the same files even if it doesn't change ? Are you looking for an updated "modified datetime" ?
http://msdn.microsoft.com/en-us/library/system.io.path.aspx : Path

how to check if i have At least one file *.bak in my folder?

how to check if i have At least one file *.bak in my folder ?
You can list all files in a particular directory using Directory.GetFiles(). The second parameter is a pattern to search for (which includes wildcards). Something like this should do it:
var hasBak = Directory.GetFiles(yourdir, "*.bak").Length > 0;
Directory.GetFiles is correct, but not the best solution if you are using C# 4.0, because we have:
bool exist = Directory.EnumerateFiles(#"C:\mydir", "*.bak").Any();
Directory.GetFiles returns all those matched files, and you can check the Length property. But when we invoke Any to Directory.EnumerateFiles, essentially we get its enumerator and MoveNext, the method returns as soon as we found any item in it(in this way we always don't need to loop through all the files). I checked their implementation, and test with:
Directory.EnumerateFiles(#"C:\Windows", "*.log").Any();
GetFiles costs 4x time than EnumerateFiles(run them 10000 times, measuring by StopWatch).
Well, you can use Directory.GetFiles(directory. "*.bak") to get the list of bak files, and then just check whether the length is 0 or not.
if (Directory.GetFiles(directory, "*.bak").Length == 0)
{
// Complain to the user or whatever you want to do
}
public bool IsAtleastOneFilePresent()
{
string[] filePaths = Directory.GetFiles(#"c:\MyDir\", "*.bak");
if(filePaths.Length > 0) return true; else return false;
}
string[] files = Directory.GetFiles(#"c:\SomeDirectory\", "*.bak");
and ensure that files.Length > 0
Use Directory.GetFiles
Directory.GetFiles([dir], "*.bak")
http://msdn.microsoft.com/en-us/library/wz42302f.aspx

Categories

Resources