Monday, August 26, 2013

Properly Deleting A Directory, Optionally Using Regex Patterns

This method performs a deletion of a directory, and it's subdirectories and files.

Additionally, it will try to set all files to non-readonly before they're being deleted.

Optionally, it also allows to specify Regex patterns to match the names of the subdirectories or files to be deleted or excluded from the deletion (Regex options used are IgnoreCase and Singleline).

/// <summary>
/// Deletes the specified directory, subdirectories and files. 
/// <para>All files will be set to non-readonly before deleting.</para>
/// <para>_</para>
/// <para>Optionally, indicate the Regex patterns to match the names of the subdirectories or files to be deleted or excluded from deletion.</para>
/// <para>Note: Regex options used are IgnoreCase and Singleline.</para>
/// </summary>
/// <param name="path">The name of the directory to remove.</param>
/// <param name="subdirRegexPattern">Regex pattern for the name of the subdirectories to be deleted. Set null or empty for no checking.</param>
/// <param name="subdirExcludeRegexPattern">Regex pattern for the name of the subdirectories to be excluded from deletion. Set null or empty for no checking.</param>
/// <param name="fileRegexPattern">Regex pattern for the name of the files to be deleted. Set null or empty for no checking.</param>
/// <param name="fileExcludeRegexPattern">Regex pattern for the name of the files to be excluded from deletion. Set null or empty for no checking.</param>
public static void DeleteDirectory(string path, 
    string subdirRegexPattern, string subdirExcludeRegexPattern,
    string fileRegexPattern, string fileExcludeRegexPattern)
{
    if (!Directory.Exists(path))
        throw new DirectoryNotFoundException();

    DirectoryInfo dirInfo = new DirectoryInfo(path);

    #region Delete the files

    foreach (FileInfo fileInfo in dirInfo.GetFiles())
    {
        bool doDelete = true;

        #region Check regex patterns if any

        // Note: The static Regex.IsMatch() will take advantage of regex cache (default to last 15).

        if (!String.IsNullOrEmpty(fileRegexPattern) &&
            !Regex.IsMatch(fileInfo.Name, fileRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline))
            doDelete = false;

        if (!String.IsNullOrEmpty(fileExcludeRegexPattern) &&
            Regex.IsMatch(fileInfo.Name, fileExcludeRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline))
            doDelete = false;                   // Note: Exclude will take precedence.

        #endregion

        if (doDelete)
        {
            fileInfo.IsReadOnly = false;        // Make sure is not ReadOnly before delete.
            fileInfo.Delete();
        }
    }

    #endregion

    #region Delete the subdirectories

    foreach (DirectoryInfo subDirInfo in dirInfo.GetDirectories())
    {
        bool doDelete = true;

        #region Check regex patterns if any

        // Note: The static Regex.IsMatch() will take advantage of regex cache (default to last 15).

        if (!String.IsNullOrEmpty(subdirRegexPattern) &&
            !Regex.IsMatch(subDirInfo.Name, subdirRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline))
            doDelete = false;

        if (!String.IsNullOrEmpty(subdirExcludeRegexPattern) &&
            Regex.IsMatch(subDirInfo.Name, subdirExcludeRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline))
            doDelete = false;                   // Note: Exclude will take precedence.

        #endregion

        if (doDelete)
        {
            DeleteDirectory(subDirInfo.FullName, 
                subdirRegexPattern, subdirExcludeRegexPattern,
                fileRegexPattern, fileExcludeRegexPattern);     // Recursive.
        }
    }

    #endregion

    #region Delete the directory itself

    if (dirInfo.GetFiles().Length <= 0 && dirInfo.GetDirectories().Length <= 0)
    {
        dirInfo.Delete();                       // Delete only if no content.

        #region Make sure the directory is deleted before continue

        #region Note
        /*
         * To solve the intermittent file/directory deletion lagging issue, use retry if directory still exist.
         * Reference: http://stackoverflow.com/questions/9370012/waiting-for-system-to-delete-file
        */
        #endregion

        int retry = 0;
        while (dirInfo.Exists && retry <= 4)
        {
            retry += 1;
            System.Threading.Thread.Sleep(500); // Delay for half-second.

            // Note: If done all retries and still exist, do nothing and let the caller to handle (any delete exception will still be thrown).
        }

        #endregion
    }

    #endregion
}

Usage Examples:

To completely delete a directory and all it's contents (without Regex):
DeleteDirectory(@"D:\MyData\MyFolder", null, null, null, null);

To completely delete a directory and all it's contents, excluding subdirectories named "KeepDir" and files named "KeepFile.txt" (based on the Regex patterns specified):
DeleteDirectory(@"D:\MyData\MyFolder", 
    null, @"\bKeepDir\b",
    null, @"\bKeepFile.txt\b");



If you find this post helpful, would you buy me a coffee?


No comments:

Post a Comment