2009-06-18 19 views
10

Necesito ayuda para analizar la respuesta de ListDirectoryDetails en C#.Analizando FtpWebRequest ListDirectoryDetails línea

Solo necesito los siguientes campos.

  • Nombre de archivo/Nombre del directorio
  • Fecha de creación
  • y el tamaño del archivo.

Esto es lo que algunas de las líneas se ven como cuando corro ListDirectoryDetails:

d--x--x--x 2 ftp  ftp   4096 Mar 07 2002 bin 
-rw-r--r-- 1 ftp  ftp  659450 Jun 15 05:07 TEST.TXT 
-rw-r--r-- 1 ftp  ftp  101786380 Sep 08 2008 TEST03-05.TXT 
drwxrwxr-x 2 ftp  ftp   4096 May 06 12:24 dropoff 

Gracias de antemano.

Respuesta

22

No estoy seguro de si todavía necesita esto, pero esta es la solución que se me ocurrió:

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); 

partido Grupos:

  1. tipo de objeto:
    • directorio d:
    • -: archivo
  2. Matriz [3] de permisos (rwx-)
  3. Tamaño del archivo
  4. Fecha de la última actualización
  5. Última hora de modificación del archivo
  6. /Nombre del directorio
+0

Gran Regex, añadido los nombres de toda la capturar grupos para hacerlo más difícil de entender al analizar ... ¿Cómo muestra el ftpd que usa este formato años en la fecha de modificación? – ullmark

+0

si el año de la fecha de modificación es el año actual, entonces muestra solo el dm de MMM y hh: mm, pero si es de un año anterior, muestra el año real, pero no el tiempo. –

+2

Con los grupos:.?? ^ (? [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? $ Si el año es el mismo, se mostrará la hora, de lo contrario, se mostrará el año. Eso es por diseño. Si necesita una marca de tiempo precisa, use WebRequestMethods.Ftp.GetDateTimestamp. – adzm

1

Ésta es mi algoritmo para obtener el archivo/Nombre de la dirección, Fecha de creación, Atributo (Archivo/Dir), Tamaño. Espero que esto ayude ...

 FtpWebRequest _fwr = FtpWebRequest.Create(uri) as FtpWebRequest  
     _fwr.Credentials = cred; 
     _fwr.UseBinary = true; 
     _fwr.UsePassive = true; 
     _fwr.KeepAlive = true; 
     _fwr.Method = WebRequestMethods.Ftp.ListDirectoryDetails; 
     StreamReader _sr = new StreamReader(_fwr.GetResponse().GetResponseStream()); 

     List<object> _dirlist = new List<object>(); 
     List<object> _attlist = new List<object>(); 
     List<object> _datelist = new List<object>(); 
     List<long> _szlist = new List<long>(); 
     while (!_sr.EndOfStream) 
     { 
      string[] buf = _sr.ReadLine().Split(' '); 
      //string Att, Dir; 
      int numcnt = 0, offset = 4; ; 
      long sz = 0; 
      for (int i = 0; i < buf.Length; i++) 
      { 
       //Count the number value markers, first before the ftp markers and second 
       //the file size. 
       if (long.TryParse(buf[i], out sz)) numcnt++; 
       if (numcnt == 2) 
       { 
        //Get the attribute 
        string cbuf = "", dbuf = "", abuf = ""; 
        if (buf[0][0] == 'd') abuf = "Dir"; else abuf = "File"; 
        //Get the Date 
        if (!buf[i+3].Contains(':')) offset++; 
        for (int j = i + 1; j < i + offset; j++) 
        { 
         dbuf += buf[j]; 
         if (j < buf.Length - 1) dbuf += " "; 
        } 
        //Get the File/Dir name 
        for (int j = i + offset; j < buf.Length; j++) 
        { 
         cbuf += buf[j]; 
         if (j < buf.Length - 1) cbuf += " "; 
        } 
        //Store to a list. 
        _dirlist.Add(cbuf); 
        _attlist.Add(abuf); 
        _datelist.Add(dbuf); 
        _szlist.Add(sz); 

        offset = 0; 
        break; 
       } 
      } 
     } 
4

Por este listado específico, el siguiente código hará:

FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://ftp.example.com/"); 
request.Credentials = new NetworkCredential("user", "password"); 
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails; 
StreamReader 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); 
    DateTime modified; 
    string s = Regex.Replace(match.Groups[6].Value, @"\s+", " "); 
    if (s.IndexOf(':') >= 0) 
    { 
     modified = DateTime.ParseExact(s, hourMinFormats, culture, DateTimeStyles.None); 
    } 
    else 
    { 
     modified = DateTime.ParseExact(s, yearFormats, 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")); 
} 

obtendrá (a partir del año 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 

Pero, en realidad, intentar analizar la lista devuelta por ListDirectoryDetails no es el camino correcto.

Desea utilizar un cliente FTP que admita el comando MLSD moderno que devuelve un listado de directorios en un formato legible por máquina especificado en el RFC 3659. El análisis del formato legible por humanos devuelto por el antiguo comando LIST (utilizado internamente por FtpWebRequest para su método ListDirectoryDetails) debe utilizarse como la opción de último recurso, cuando se habla con servidores FTP obsoletos, que no admiten el comando MLSD (como Microsoft Servidor FTP de IIS).

Muchos servidores utilizan un formato diferente para la respuesta del comando LIST. Particularmente, IIS puede usar el formato DOS. Ver C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response.


Por ejemplo, con WinSCP .NET assembly, puede utilizar sus Session.ListDirectory o Session.EnumerateRemoteFiles métodos.

Utilizan internamente el comando MLSD, pero pueden recurrir al comando LIST y admiten docenas de diferentes formatos de listas legibles por humanos.

El listado devuelto se presenta como colección de RemoteFileInfo instances con propiedades como:

  • Name
  • LastWriteTime (con zona horaria correcta)
  • Length
  • FilePermissions (analizado en los derechos individuales)
  • Group
  • Owner
  • IsDirectory
  • IsParentDirectory
  • IsThisDirectory

(yo soy el autor de WinSCP)


mayoría de las otras bibliotecas 3 ª parte harán lo mismo. El uso del FtpWebRequest class no es confiable para este propósito. Desafortunadamente, no hay otro cliente FTP incorporado en .NET Framework.

0

Basándose en la idea de expresiones regulares de Ryan Conrad, este es mi código de lectura final:

protected static Regex m_FtpListingRegex = new Regex(@"^([d-])((?:[rwxt-]{3}){3})\s+(\d{1,})\s+(\w+)?\s+(\w+)?\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); 
protected static readonly String Timeformat = "MMM dd yyyy HH:mm"; 

/// <summary> 
/// Handles file info given in the form of a string in standard unix ls output format. 
/// </summary> 
/// <param name="filesListing">The file listing string.</param> 
/// <returns>A list of FtpFileInfo objects</returns> 
public static List<FtpFileInfo> GetFilesListFromFtpListingUnix(String filesListing) 
{ 
    List<FtpFileInfo> files = new List<FtpFileInfo>(); 
    MatchCollection matches = m_FtpListingRegex.Matches(filesListing); 
    if (matches.Count == 0 && filesListing.Trim('\r','\n','\t',' ').Length != 0) 
     return null; // parse error. Could throw some kind of exception here too. 
    foreach (Match match in matches) 
    { 
     FtpFileInfo fileInfo = new FtpFileInfo(); 
     Char dirchar = match.Groups[1].Value.ToLowerInvariant()[0]; 
     fileInfo.IsDirectory = dirchar == 'd'; 
     fileInfo.Permissions = match.Groups[2].Value.ToCharArray(); 
     // No clue what "inodes" actually means... 
     Int32 inodes; 
     fileInfo.NrOfInodes = Int32.TryParse(match.Groups[3].Value, out inodes) ? inodes : 1; 
     fileInfo.User = match.Groups[4].Success ? match.Groups[4].Value : null; 
     fileInfo.Group = match.Groups[5].Success ? match.Groups[5].Value : null; 
     Int64 fileSize; 
     Int64.TryParse(match.Groups[6].Value, out fileSize); 
     fileInfo.FileSize = fileSize; 
     String month = match.Groups[7].Value; 
     String day = match.Groups[8].Value.PadLeft(2, '0'); 
     String year = match.Groups[9].Success ? match.Groups[9].Value : DateTime.Now.Year.ToString(CultureInfo.InvariantCulture); 
     String time = match.Groups[10].Success ? match.Groups[10].Value.PadLeft(5, '0') : "00:00"; 
     String timeString = month + " " + day + " " + year + " " + time; 
     DateTime lastModifiedDate; 
     if (!DateTime.TryParseExact(timeString, Timeformat, CultureInfo.InvariantCulture, DateTimeStyles.None, out lastModifiedDate)) 
      lastModifiedDate = DateTime.MinValue; 
     fileInfo.LastModifiedDate = lastModifiedDate; 
     fileInfo.FileName = match.Groups[11].Value; 
     files.Add(fileInfo); 
    } 
    return files; 
} 

Y la clase FtpFileInfo que está llena:

public class FtpFileInfo 
{ 
    public Boolean IsDirectory { get; set; } 
    public Char[] Permissions { get; set; } 
    public Int32 NrOfInodes { get; set; } 
    public String User { get; set; } 
    public String Group { get; set; } 
    public Int64 FileSize { get; set; } 
    public DateTime LastModifiedDate { get; set; } 
    public String FileName { get; set; } 
}