971 lines · 46.8 KB · C#
RawDownload
//******************************************************************************************************//  FilePath.cs - Gbtc////  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  02/05/2003 - J. Ritchie Carroll//       Generated original version of source code.//  12/29/2005 - Pinal C. Patel//       Migrated 2.0 version of source code from 1.1 source (GSF.Shared.FilePath).//  08/22/2007 - Darrell Zuercher//       Edited code comments.//  09/19/2008 - J. Ritchie Carroll//       Converted to C#.//  10/24/2008 - Pinal C. Patel//       Edited code comments.//  12/17/2008 - F. Russell Robertson//       Fixed issue in GetFilePatternRegularExpression().//  06/30/2009 - Pinal C. Patel//       Removed FilePathHasFileName() since the result was error prone.//  09/14/2009 - Stephen C. Wills//       Added new header and license agreement.//  09/17/2009 - Pinal C. Patel//       Modified GetAbsolutePath() to remove dependency on HttpContext.Current.//  04/19/2010 - Pinal C. Patel//       Added GetApplicationDataFolder() method.//  04/21/2010 - Pinal C. Patel//       Updated GetApplicationDataFolder() to include the company name if available.//  01/28/2011 - J. Ritchie Carroll//       Added IsValidFileName function.//  02/14/2011 - J. Ritchie Carroll//       Fixed issue in GetDirectoryName where last directory was being truncated as a file name.//  06/06/2011 - Stephen C. Wills//       Fixed issue in GetFileName where path suffix was being removed before extracting the file name.//  07/29/2011 - Pinal C. Patel//       Updated GetApplicationDataFolder() to use the TEMP directory for web applications.//  12/14/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;using System.Text.RegularExpressions;using System.Threading; namespace GSF.IO{    /// <summary>    /// Contains File and Path manipulation methods.    /// </summary>    public static partial class FilePath    {        #region [ Members ]         // Fields        private static readonly string s_directorySeparatorCharPattern;        private static readonly string s_fileNameCharPattern;         #endregion         #region [ Constructor ]         static FilePath()        {            char[] directorySeparatorChars =            {                Path.DirectorySeparatorChar,                Path.AltDirectorySeparatorChar            };             char[] encodedInvalidFileNameChars = Path.GetInvalidFileNameChars()                .SelectMany(c => c.RegexEncode())                .ToArray();             char[] encodedDirectorySeparatorChars = directorySeparatorChars                .Distinct()                .SelectMany(c => c.RegexEncode())                .ToArray();             // Defines a regular expression pattern for a valid directory separator character.            s_directorySeparatorCharPattern = $"[{new string(encodedDirectorySeparatorChars)}]";             // Defines a regular expression pattern for a valid file name character. We do this by            // allowing any characters except those that would not be valid as part of a filename.            // This essentially builds the "?" wildcard pattern match.            s_fileNameCharPattern = $"[^{new string(encodedInvalidFileNameChars)}]";        }         #endregion         #region [ Methods ]         /// <summary>        /// Returns the names of the subdirectories (including their paths) that match the specified search pattern in the specified directory, and optionally searches subdirectories.        /// </summary>        /// <param name="path">The relative or absolute path to the directory to search. This string is not case-sensitive.</param>        /// <param name="searchPattern">The search string to match against the names of subdirectories in <paramref name="path"/>. This parameter can contain a combination of valid literal and wildcard characters, but doesn't support regular expressions.</param>        /// <param name="searchOption">One of the enumeration values that specifies whether the search operation should include all subdirectories or only the current directory.</param>        /// <param name="exceptionHandler">Handles exceptions thrown during directory enumeration.</param>        /// <returns>An array of the full names (including paths) of the subdirectories that match the specified criteria, or an empty array if no directories are found.</returns>        public static string[] GetDirectories(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.AllDirectories, Action<Exception> exceptionHandler = null)        {            return EnumerateDirectories(path, searchPattern, searchOption, exceptionHandler).ToArray();        }         /// <summary>        /// Returns an enumerable collection of file names that match a search pattern in a specified path, and optionally searches subdirectories.        /// </summary>        /// <param name="path">The relative or absolute path to the directory to search. This string is not case-sensitive.</param>        /// <param name="searchPattern">The search string to match against the names of directories in <paramref name="path"/>. This parameter can contain a combination of valid literal path and wildcard (* and ?) characters, but doesn't support regular expressions.</param>        /// <param name="searchOption">One of the enumeration values that specifies whether the search operation should include only the current directory or should include all subdirectories.</param>        /// <param name="exceptionHandler">Handles exceptions thrown during directory enumeration.</param>        /// <returns>An enumerable collection of the full names (including paths) for the directories in the directory specified by <paramref name="path"/> and that match the specified search pattern and option.</returns>        public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.AllDirectories, Action<Exception> exceptionHandler = null)        {            try            {                return (searchOption == SearchOption.TopDirectoryOnly)                    ? Directory.EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly)                    : Directory.EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly)                        .Concat(Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)                            .SelectMany(directory => EnumerateDirectories(directory, searchPattern, searchOption, exceptionHandler)));            }            catch (Exception ex)            {                exceptionHandler?.Invoke(ex);            }             return Enumerable.Empty<string>();        }         /// <summary>        /// Returns the names of files (including their paths) that match the specified search pattern in the specified directory, using a value to determine whether to search subdirectories.        /// </summary>        /// <param name="path">The relative or absolute path to the directory to search. This string is not case-sensitive.</param>        /// <param name="searchPattern">The search string to match against the names of files in <paramref name="path"/>. This parameter can contain a combination of valid literal path and wildcard (* and ?) characters, but doesn't support regular expressions.</param>        /// <param name="searchOption">One of the enumeration values that specifies whether the search operation should include all subdirectories or only the current directory.</param>        /// <param name="exceptionHandler">Handles exceptions thrown during file enumeration.</param>        /// <returns>An array of the full names (including paths) for the files in the specified directory that match the specified search pattern and option, or an empty array if no files are found.</returns>        public static string[] GetFiles(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.AllDirectories, Action<Exception> exceptionHandler = null)        {            return EnumerateFiles(path, searchPattern, searchOption, exceptionHandler).ToArray();        }         /// <summary>        /// Returns an enumerable collection of file names that match a search pattern in a specified path, and optionally searches subdirectories.        /// </summary>        /// <param name="path">The relative or absolute path to the directory to search. This string is not case-sensitive.</param>        /// <param name="searchPattern">The search string to match against the names of files in <paramref name="path"/>. This parameter can contain a combination of valid literal path and wildcard (* and ?) characters, but doesn't support regular expressions.</param>        /// <param name="searchOption">One of the enumeration values that specifies whether the search operation should include only the current directory or should include all subdirectories.</param>        /// <param name="exceptionHandler">Handles exceptions thrown during file enumeration.</param>        /// <returns>An enumerable collection of the full names (including paths) for the files in the directory specified by <paramref name="path"/> and that match the specified search pattern and option.</returns>        public static IEnumerable<string> EnumerateFiles(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.AllDirectories, Action<Exception> exceptionHandler = null)        {            try            {                return (searchOption == SearchOption.TopDirectoryOnly)                    ? Directory.EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly)                    : Directory.EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly)                        .Concat(Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)                            .SelectMany(directory => EnumerateFiles(directory, searchPattern, searchOption, exceptionHandler)));            }            catch (Exception ex)            {                exceptionHandler?.Invoke(ex);            }             return Enumerable.Empty<string>();        }         /// <summary>        /// Ensures the supplied path name is valid.        /// </summary>        /// <param name="filePath">The file path to be validated.</param>        /// <remarks>        /// Throws argument exceptions if the <see param="pathName"/> is invalid.        /// </remarks>        public static void ValidatePathName(string filePath)        {            if (filePath.IsNullOrWhiteSpace())                throw new ArgumentNullException(nameof(filePath), "Path cannot be null or empty space");             if (filePath.IndexOfAny(Path.GetInvalidPathChars()) >= 0)                throw new ArgumentException("Path has invalid characters.", nameof(filePath));        }         /// <summary>        /// Determines whether the specified file name matches any of the given file specs (wildcards are defined as '*' or '?' characters).        /// </summary>        /// <param name="fileSpecs">The file specs used for matching the specified file name.</param>        /// <param name="filePath">The file path to be tested against the specified file specs for a match.</param>        /// <param name="ignoreCase"><c>true</c> to specify a case-insensitive match; otherwise <c>false</c>.</param>        /// <returns><c>true</c> if the specified file name matches any of the given file specs; otherwise <c>false</c>.</returns>        /// <exception cref="ArgumentException"><paramref name="filePath"/> contains one or more of the invalid characters defined in <see cref="Path.GetInvalidPathChars"/>.</exception>        /// <remarks>        /// The syntax for <paramref name="fileSpecs"/> adheres to the following rules:        ///         /// <ul>        /// <li>Either '\' or '/' (as defined by <see cref="Path.DirectorySeparatorChar"/> and <see cref="Path.AltDirectorySeparatorChar"/>) can match the other.</li>        /// <li>A single '\' or '/' at the beginning of the pattern matches any valid path root (such as "C:\" or "\\server\share").</li>        /// <li>A '?' matches a single character which would be valid in a file name (as defined by <see cref="Path.GetInvalidFileNameChars"/>).</li>        /// <li>A '*' matches any number of characters which would be valid in a file name.</li>        /// <li>A sequence of "**\" or "**/" matches any number of sequential directories.</li>        /// <li>Any other character matches itself.</li>        /// </ul>        /// </remarks>        public static bool IsFilePatternMatch(string[] fileSpecs, string filePath, bool ignoreCase)        {            foreach (string fileSpec in fileSpecs)            {                if (IsFilePatternMatch(fileSpec, filePath, ignoreCase))                    return true;            }             return false;        }         /// <summary>        /// Determines whether the specified file name matches the given file spec (wildcards are defined as '*' or '?' characters).        /// </summary>        /// <param name="fileSpec">The file spec used for matching the specified file name.</param>        /// <param name="filePath">The file path to be tested against the specified file spec for a match.</param>        /// <param name="ignoreCase"><c>true</c> to specify a case-insensitive match; otherwise <c>false</c>.</param>        /// <returns><c>true</c> if the specified file name matches the given file spec; otherwise <c>false</c>.</returns>        /// <exception cref="ArgumentException"><paramref name="filePath"/> contains one or more of the invalid characters defined in <see cref="Path.GetInvalidPathChars"/>.</exception>        /// <remarks>        /// The syntax for <paramref name="fileSpec"/> adheres to the following rules:        ///         /// <ul>        /// <li>Either '\' or '/' (as defined by <see cref="Path.DirectorySeparatorChar"/> and <see cref="Path.AltDirectorySeparatorChar"/>) can match the other.</li>        /// <li>A single '\' or '/' at the beginning of the pattern matches any valid path root (such as "C:\" or "\\server\share").</li>        /// <li>A '?' matches a single character which would be valid in a file name (as defined by <see cref="Path.GetInvalidFileNameChars"/>).</li>        /// <li>A '*' matches any number of characters which would be valid in a file name.</li>        /// <li>A sequence of "**\" or "**/" matches any number of sequential directories.</li>        /// <li>Any other character matches itself.</li>        /// </ul>        /// </remarks>        public static bool IsFilePatternMatch(string fileSpec, string filePath, bool ignoreCase)        {            if (string.IsNullOrWhiteSpace(filePath))                return false;             // Define regular expression patterns for the three possible sequences that can match the path root.            string recursiveDirPattern = $"{(Regex.Escape("**"))}{s_directorySeparatorCharPattern}";            string pathRootPattern = string.Format("({0}[^{0}]|{0}$)", Regex.Escape(Path.DirectorySeparatorChar.ToString()));            string altPathRootPattern = string.Format("({0}[^{0}]|{0}$)", Regex.Escape(Path.AltDirectorySeparatorChar.ToString()));             // If any of the three patterns are found at the start of fileSpec and the fileName refers to a rooted path,            // remove the path root from fileName and then remove all leading directory separator chars from both fileSpec and fileName.            if (Regex.IsMatch(fileSpec, $"^({recursiveDirPattern}|{pathRootPattern}|{altPathRootPattern})") && Path.IsPathRooted(filePath))            {                fileSpec = fileSpec.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);                filePath = Regex.Replace(filePath, $"^{Regex.Escape(Path.GetPathRoot(filePath))}", "").TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);            }             // Use regular expression matching to determine whether fileSpec matches fileName.            return new Regex(GetFilePatternRegularExpression(fileSpec), (ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None)).IsMatch(filePath);        }         /// <summary>        /// Determines if the specified file name and path is valid.        /// </summary>        /// <param name="filePath">The file name with optional path to test for validity.</param>        /// <returns><c>true</c> if the specified <paramref name="filePath"/> is a valid name; otherwise <c>false</c>.</returns>        public static bool IsValidFileName(string filePath)        {            if (string.IsNullOrWhiteSpace(filePath))                return false;             try            {                // Attempt to parse directory and file name - this will catch most issues                string directory = Path.GetDirectoryName(filePath);                string filename = Path.GetFileName(filePath);                 // Check for invalid directory characters                if (!string.IsNullOrWhiteSpace(directory) && directory.IndexOfAny(Path.GetInvalidPathChars()) >= 0)                    return false;                 // Check for invalid file name characters                if (string.IsNullOrWhiteSpace(filename) || filename.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)                    return false;                 // Recurse in to check validity of each directory name                if (!string.IsNullOrWhiteSpace(directory) && !string.IsNullOrWhiteSpace(Path.GetFileName(directory)))                    return IsValidFileName(directory);            }            catch            {                // If you can't parse a file name or directory, file path is definitely not valid                return false;            }             return true;        }         /// <summary>        /// Gets a valid file name by replacing invalid file name characters with <paramref name="replaceWithCharacter"/>.        /// </summary>        /// <param name="fileName">File name to validate.</param>        /// <param name="replaceWithCharacter">Character to replace invalid file name characters with. Set to '\0' to remove invalid file name characters.</param>        /// <returns>A valid file name with no invalid file name characters.</returns>        /// <remarks>        /// This function expects a file name, not a file name with a path. To properly get a valid file path, where all directory        /// names and the file name are validated, use the <see cref="GetValidFilePath"/>. Calling the <see cref="GetValidFileName"/>        /// function will a full path will yield all directory separators replaced with the <paramref name="replaceWithCharacter"/>.        /// </remarks>        public static string GetValidFileName(string fileName, char replaceWithCharacter = '_')        {            if (replaceWithCharacter == '\0')                return fileName.RemoveInvalidFileNameCharacters();             return fileName.ReplaceInvalidFileNameCharacters(replaceWithCharacter);        }         /// <summary>        /// Gets a valid file path by replacing invalid file or directory name characters with <paramref name="replaceWithCharacter"/>.        /// </summary>        /// <param name="filePath">File path to validate.</param>        /// <param name="replaceWithCharacter">Character to replace invalid file or directory name characters with. Set to '\0' to remove invalid file or directory name characters.</param>        /// <returns>A valid file path with no invalid file or directory name characters.</returns>        public static string GetValidFilePath(string filePath, char replaceWithCharacter = '_')        {            string[] fileParts = filePath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);             for (int i = 0; i < fileParts.Length; i++)            {                // Leave any volume specification alone                if (i == 0 &&                    Path.VolumeSeparatorChar != Path.DirectorySeparatorChar &&                    Path.VolumeSeparatorChar != Path.AltDirectorySeparatorChar &&                    fileParts[0].IndexOfAny(new[] { Path.VolumeSeparatorChar }) > 0)                    continue;                 fileParts[i] = GetValidFileName(fileParts[i], replaceWithCharacter);            }             return string.Join(Path.DirectorySeparatorChar.ToString(), fileParts);        }         /// <summary>        /// Gets the file name and extension from the specified file path.        /// </summary>        /// <param name="filePath">The file path from which the file name and extension is to be obtained.</param>        /// <returns>File name and extension if the file path has it; otherwise empty string.</returns>        public static string GetFileName(string filePath)        {            return Path.GetFileName(filePath);        }         /// <summary>        /// Gets the extension from the specified file path.        /// </summary>        /// <param name="filePath">The file path from which the extension is to be obtained.</param>        /// <returns>File extension.</returns>        public static string GetExtension(string filePath)        {            return Path.GetExtension(RemovePathSuffix(filePath));        }         /// <summary>        /// Gets the file name without extension from the specified file path.        /// </summary>        /// <param name="filePath">The file path from which the file name is to be obtained.</param>        /// <returns>File name without the extension if the file path has it; otherwise empty string.</returns>        public static string GetFileNameWithoutExtension(string filePath)        {            return Path.GetFileNameWithoutExtension(RemovePathSuffix(filePath));        }         /// <summary>        /// Gets the size of the specified file.        /// </summary>        /// <param name="fileName">Name of file whose size is to be retrieved.</param>        /// <returns>The size of the specified file.</returns>        public static long GetFileLength(string fileName)        {            try            {                return (new FileInfo(fileName)).Length;            }            catch            {                return -1;            }        }         /// <summary>        /// Gets a list of files under the specified path. Search wildcard pattern (c:\Data\*.dat) can be used for         /// including only the files matching the pattern or path wildcard pattern (c:\Data\*\*.dat) to indicate the         /// inclusion of files under all subdirectories in the list.        /// </summary>        /// <param name="path">The path for which a list of files is to be returned.</param>        /// <param name="exceptionHandler">Handles exceptions thrown during file enumeration.</param>        /// <returns>A list of files under the given path.</returns>        public static string[] GetFileList(string path, Action<Exception> exceptionHandler = null)        {            string directory = GetDirectoryName(path);            string filePattern = GetFileName(path);            SearchOption options = SearchOption.TopDirectoryOnly;             if (string.IsNullOrEmpty(filePattern))            {                // No wildcard pattern was specified, so get a listing of all files.                filePattern = "*.*";            }             if (GetLastDirectoryName(directory) == "*")            {                // Path wildcard pattern is used to specify the option to include subdirectories.                options = SearchOption.AllDirectories;                directory = directory.Remove(directory.LastIndexOf("*", StringComparison.OrdinalIgnoreCase));            }             if ((object)exceptionHandler == null)                return Directory.GetFiles(directory, filePattern, options);             return GetFiles(directory, filePattern, options, exceptionHandler);        }         /// <summary>        /// Gets a regular expression pattern that simulates wildcard matching for filenames (wildcards are defined as '*' or '?' characters).        /// </summary>        /// <param name="fileSpec">The file spec for which the regular expression pattern if to be generated.</param>        /// <returns>Regular expression pattern that simulates wildcard matching for filenames.</returns>        public static string GetFilePatternRegularExpression(string fileSpec)        {            List<Tuple<string, string>> replacements = new List<Tuple<string, string>>()            {                // Replaces directory separator characters with their equivalent regular expressions.                Tuple.Create($"{s_directorySeparatorCharPattern}+", $"{s_directorySeparatorCharPattern}+"),                                // Replaces wildcard file patterns with their equivalent regular expression.                Tuple.Create(Regex.Escape("?"), s_fileNameCharPattern),                Tuple.Create(Regex.Escape("**") + s_directorySeparatorCharPattern, $"({s_fileNameCharPattern}*{s_directorySeparatorCharPattern})*"),                Tuple.Create(Regex.Escape("*"), $"{s_fileNameCharPattern}*")            };             StringBuilder input;            StringBuilder output;            Tuple<string, string> replacement;            Match match;                        input = new StringBuilder(fileSpec);            output = new StringBuilder();             while (input.Length > 0)            {                // Determine if any of the replacement patterns match the input.                replacement = replacements.FirstOrDefault(r => Regex.IsMatch(input.ToString(), $"^{r.Item1}"));                 if ((object)replacement != null)                {                    // If one of the replacement patterns matches the input,                    // apply that replacement and write it to the output.                    match = Regex.Match(input.ToString(), $"^{replacement.Item1}");                    output.Append(Regex.Replace(match.Value, replacement.Item1, replacement.Item2));                    input.Remove(0, match.Length);                }                else                {                    // If not, move a single character as-is from input to the output.                    output.Append(Regex.Escape(input[0].ToString()));                    input.Remove(0, 1);                }            }             // Return the output string as the regular expression pattern.            return "^" + output + "$";        }         /// <summary>        /// Gets the directory information from the specified file path.        /// </summary>        /// <param name="filePath">The file path from which the directory information is to be obtained.</param>        /// <returns>Directory information.</returns>        /// <remarks>        /// This differs from <see cref="Path.GetDirectoryName"/> in that it will see if last name in path is        /// a directory and, if it exists, will properly treat that part of path as a directory. The .NET path        /// function always assumes last entry is a file name if path is not suffixed with a slash. For example:        ///     Path.GetDirectoryName(@"C:\Music") will return "C:\", however,         /// FilePath.GetDirectoryName(@"C:\Music") will return "C:\Music\", so long as Music directory exists.        /// </remarks>        public static string GetDirectoryName(string filePath)

Lines 1–500 of 971.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t