Parsing FtpWebRequest ListDirectoryDetails line
Not sure if you still need this, but this is the solution i came up with:
Regex regex = new Regex ( @"^([d-])([rwxt-]{3}){3}\s+\d{1,}\s+.*?(\d{1,})\s+(\w+\s+\d{1,2}\s+(?:\d{4})?)(\d{1,2}:\d{2})?\s+(.+?)\s?$",
RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace );
Match Groups:
- object type:
- d : directory
- - : file
- Array[3] of permissions (rwx-)
- File Size
- Last Modified Date
- Last Modified Time
- File/Directory Name
For this specific listing, the following code will do:
var request = (FtpWebRequest)WebRequest.Create("ftp://ftp.example.com/");
request.Credentials = new NetworkCredential("user", "password");
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
var reader = new StreamReader(request.GetResponse().GetResponseStream());
string pattern =
@"^([\w-]+)\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+" +
@"(\w+\s+\d+\s+\d+|\w+\s+\d+\s+\d+:\d+)\s+(.+)$";
Regex regex = new Regex(pattern);
IFormatProvider culture = CultureInfo.GetCultureInfo("en-us");
string[] hourMinFormats =
new[] { "MMM dd HH:mm", "MMM dd H:mm", "MMM d HH:mm", "MMM d H:mm" };
string[] yearFormats =
new[] { "MMM dd yyyy", "MMM d yyyy" };
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
Match match = regex.Match(line);
string permissions = match.Groups[1].Value;
int inode = int.Parse(match.Groups[2].Value, culture);
string owner = match.Groups[3].Value;
string group = match.Groups[4].Value;
long size = long.Parse(match.Groups[5].Value, culture);
string s = Regex.Replace(match.Groups[6].Value, @"\s+", " ");
string[] formats = (s.IndexOf(':') >= 0) ? hourMinFormats : yearFormats;
var modified = DateTime.ParseExact(s, formats, culture, DateTimeStyles.None);
string name = match.Groups[7].Value;
Console.WriteLine(
"{0,-16} permissions = {1} size = {2, 9} modified = {3}",
name, permissions, size, modified.ToString("yyyy-MM-dd HH:mm"));
}
You will get (as of year 2016):
bin permissions = d--x--x--x size = 4096 modified = 2002-03-07 00:00
TEST.TXT permissions = -rw-r--r-- size = 659450 modified = 2016-06-15 05:07
TEST03-05.TXT permissions = -rw-r--r-- size = 101786380 modified = 2008-09-08 00:00
dropoff permissions = drwxrwxr-x size = 4096 modified = 2016-05-06 12:24
But, actually trying to parse the listing returned by the ListDirectoryDetails
is not the right way to go.
You want to use an FTP client that supports the modern MLSD
command that returns a directory listing in a machine-readable format specified in the RFC 3659. Parsing the human-readable format returned by the ancient LIST
command (used internally by the FtpWebRequest
for its ListDirectoryDetails
method) should be used as the last resort option, when talking to obsolete FTP servers, that do not support the MLSD
command (like the Microsoft IIS FTP server).
Many servers use a different format for the LIST
command response. Particularly IIS can use DOS format. See C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response.
For example with WinSCP .NET assembly, you can use its Session.ListDirectory
or Session.EnumerateRemoteFiles
methods.
They internally use the MLSD
command, but can fall back to the LIST
command and support dozens of different human-readable listing formats.
The returned listing is presented as collection of RemoteFileInfo
instances with properties like:
Name
-
LastWriteTime
(with correct timezone) Length
-
FilePermissions
(parsed into individual rights) Group
Owner
IsDirectory
IsParentDirectory
IsThisDirectory
(I'm the author of WinSCP)
Most other 3rd party libraries will do the same. Using the FtpWebRequest
class is not reliable for this purpose. Unfortunately, there's no other built-in FTP client in the .NET framework.