2009-06-18 21 views
10

Ho bisogno di aiuto per analizzare la risposta da ListDirectoryDetails in C#.Parsing FtpWebRequest ListDirectoryDettagli linea

Ho solo bisogno dei seguenti campi.

  • Nome file/Nome directory
  • Data di Creazione
  • e della dimensione dei file.

Ecco quello che alcune delle linee simile a quando 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 

Grazie in anticipo.

risposta

22

Non sono sicuro se hai ancora bisogno di questo, ma questa è la soluzione che si avvicinò con:

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

Incontro Gruppi:

  1. tipo di oggetto:
    • d: directory
    • -: file
  2. Array [3] dei permessi (rwx-)
  3. File Size
  4. Ultima modifica Data
  5. Last Time modifica
  6. File/Nome directory
+0

Grande Regex, nomi aggiunto per tutto il catturare gruppi per renderlo più inespugnabile durante l'analisi ... Come fa il ftpd che usa questo formato per mostrare anni nella data di modifica? – ullmark

+0

se l'anno della data di modifica è l'anno corrente, quindi mostra solo il MMM dd e hh: mm, ma se è relativo a un anno precedente, quindi mostra l'anno effettivo, ma non il tempo. –

+2

Con i gruppi:?? ^ (? [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? $ Se l'anno è lo stesso, allora mostrerà il tempo, altrimenti mostrerà l'anno. Questo è di design. Se è necessario un timestamp accurato, utilizzare WebRequestMethods.Ftp.GetDateTimestamp. – adzm

1

Questo è il mio algoritmo per accedere al file/Nome Dir, Data creazione, Attributo (File/Dir), Dimensione. Spero che questo aiuti ...

 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

Per questa offerta specifica, il seguente codice farà:

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

otterrete (come del 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 

Ma, in realtà, cercare di analizzare l'elenco restituito dal ListDirectoryDetails non è la strada giusta da percorrere.

Si desidera utilizzare un client FTP che supporta il moderno comando MLSD che restituisce un elenco di directory in un formato leggibile dalla macchina specificato nello RFC 3659. L'analisi del formato leggibile dall'uomo restituito dal comando antico LIST (utilizzato internamente dal FtpWebRequest per il suo metodo ListDirectoryDetails) deve essere utilizzato come ultima opzione, quando si parla di server FTP obsoleti, che non supportano il comando MLSD (come Microsoft Server FTP IIS).

Molti server utilizzano un formato diverso per la risposta del comando LIST. In particolare, IIS può utilizzare il formato DOS. Vedi C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response.


Ad esempio, con WinSCP .NET assembly, è possibile utilizzare le sue Session.ListDirectory o Session.EnumerateRemoteFiles metodi.

Essi utilizzano internamente il comando MLSD, ma possono ricorrere al comando LIST e supportare dozzine di diversi formati di elenchi leggibili dall'uomo.

L'elenco restituito viene presentata come insieme di RemoteFileInfo instances con immobili come:

  • Name
  • LastWriteTime (con il corretto fuso orario)
  • Length
  • FilePermissions (analizzato in diritti individuali)
  • Group
  • Owner
  • IsDirectory
  • IsParentDirectory
  • IsThisDirectory

(io sono l'autore di WinSCP)


La maggior parte delle altre biblioteche 3rd party faranno lo stesso. L'utilizzo di FtpWebRequest class non è affidabile per questo scopo. Sfortunatamente, non ci sono altri client FTP integrati nel framework .NET.

0

costruzione sull'idea regex di Ryan Conrad, questo è il mio codice di lettura finale:

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

E la classe FtpFileInfo che è pieno:

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