2010-12-09 44 views
2

Titre original était algorithme pour lister les répertoires/fichiers uniquement à partir d'un chemin d'objet particulier (S3, Google Storage)équivalent à glob() qui travaillerait avec un tableau à la place du système de fichiers

https://gist.github.com/9a353e1589ff3ce84c02

Quelqu'un peut-il suggérer un algorithme pour lister les répertoires/fichiers uniquement dans un chemin d'objet particulier? Par exemple ahostel.lt/img/ devrait énumérer seulement des directoies languages and psd, and files background.png, [..]. Mon algorithme est long et utilise trois cycles foreach, ce qui est mauvais pour la performance mais peut-être que quelqu'un a une meilleure idée de la façon d'y parvenir en utilisant regex ou d'autres alternatives.

Mon système s'exécute sur PHP mais le logarithme général ferait l'affaire tant qu'il est possible de le convertir en PHP. En d'autres termes, je cherche un algorithme comme glob() qui fonctionnerait avec un tableau au lieu du système de fichiers.

liste répertoire simplifié: https://gist.github.com/d0c3fa12d4b894938ba5

+0

https://gist.github.com/14405d3fa83eb50c172b – Gajus

Répondre

0
$already_included  = array(); 

    foreach($list as $key => $object) 
    { 
     $clean_key = substr($key, strlen($uri)); 
     $explode = explode('/', $clean_key); 

     if(count($explode) >= 1 && !in_array($explode[0], $already_included)) 
     { 
      $already_included[] = $explode[0]; 

      $files['directories'][] = array 
      (
       'path'   => $uri . $explode[0] . '/', 
       'name'   => $explode[0], 
       'last_modified' => $object['last_modified'], 
      ); 

     } 

     if(substr_count($key, '/', $path_str_length) === 0) 
     { 
      $basename = pathinfo($key, PATHINFO_BASENAME); 

      if(strpos($basename, '.') !== FALSE) 
      { 
       $files['files'][] = array 
       (
        'path'   => $key, 
        'name'   => $basename, 
        'size'   => $object['size'], 
        'last_modified' => $object['last_modified'], 
       ); 
      } 
      elseif(strrpos($basename, '_$folder$') !== FALSE) 
      { 
       $files['directories'][] = array 
       (
        'path'   => $key, 
        'name'   => substr($basename, 0, -9), 
        'last_modified' => $object['last_modified'], 
       ); 
      } 

     } 
    } 
1

En d'autres termes, je suis à la recherche d'un algorithme comme glob() qui travaillerait avec un tableau à la place du système de fichiers.

Vous pouvez utiliser un ArrayIterator et l'envelopper dans une coutume FilterIterator

class CustomFilterIterator extends FilterIterator 
{ 
    public function accept() 
    { 
     return strpos($this->key(), 'ahostel.lt/img/') === 0 && 
      pathinfo($this->key(), PATHINFO_EXTENSION) === 'png'; 
    } 
} 

La méthode accept doit retourner une valeur booléenne. Si le booléen est TRUE, l'élément actuellement itéré sera pris en compte pour l'inclusion dans l'itération. Dans l'exemple ci-dessus, tout ce qui ne commence pas par 'ahostel.lt/img/' et qui se termine par une extension png sera ignoré. Vous pouvez ajouter des critères de filtre supplémentaires si vous le souhaitez. Pour accéder à la clé, utilisez $this->key(). Pour la valeur, utilisez $this->current().

Utilisation (codepad)

$iterator = new CustomFilterIterator(new ArrayIterator($yourArray)); 

// to create a subset of the original array use 
$filteredArray = iterator_to_array($iterator); 

// or use good old foreach 
foreach ($iterator as $path => $fileProperties) { 
    var_dump($path, $fileProperties); 
} 

Comme alternative ou plus, vous pouvez utiliser un RegexIterator.

Les deux principaux avantages lors de l'utilisation des itérateurs est la réutilisation et la testabilité: itérateurs peuvent être empilés, de sorte que le CustomFilterIterator ci-dessus pourrait être divisé en deux itérateurs, comme un PathFilter et un ExtensionFilter. Ensuite, il vous suffit d'envelopper le ArrayIterator dans les deux itérateurs de filtre pour créer une chaîne de filtre flexible sur le dessus. Comme les itérateurs sont des classes, ils peuvent facilement être testés et mockés dans des classes dont l'itérateur est une dépendance, ce que vous ne pouvez pas faire lorsque vous placez la logique de filtrage dans la boucle foreach.

ressources supplémentaires sur itérateurs et SPL en général:

4

On dirait que vous avez un simple tableau, voici donc une alternative qui filtre le tableau à l'aide une expression régulière sur les touches.

// Matches only immediate files of ahostel.lt/img/ 
$pattern = '#^ahostel\.lt/img/[^/]+\.[^/]+$#D'; 
$keys = preg_grep($pattern, array_keys($array)); 
$items = array_intersect_key($array, array_flip($keys)); 

Une autre façon, depuis itérateurs sont impressionnants, sans écrire un sur mesure, serait d'utiliser un RegexIterator pour faire le travail de filtrage des clés. Vous devez ensuite simplement boucler sur l'itérateur filtré ou utiliser iterator_to_array() pour obtenir un tableau contenant uniquement les valeurs filtrées.

$items = new RegexIterator(
    new ArrayIterator($array), 
    '#^ahostel\.lt/img/[^/]+\.[^/]+$#D', 
    RegexIterator::MATCH, 
    RegexIterator::USE_KEY 
); 

Il y a bazillion façons différentes que vous pouvez utiliser ou créer un filtrage iterator, même en utilisant quelque chose comme fnmatch() dans la méthode accept() d'un FilterIterator d'utiliser les modèles génériques comme avec glob().

class GlobKeyFilterIterator extends FilterIterator 
{ 
    protected $pattern; 
    public function __construct(Iterator $it, $pattern) 
    { 
     $this->pattern = $pattern; 
     parent::__construct($it); 
    } 
    public function accept() 
    { 
     return fnmatch($this->pattern, $this->key(), FNM_PATHNAME); 
    } 
} 

$items = new GlobKeyFilterIterator(
    new ArrayIterator($array), 
    'ahostel.lt/img/*.*' 
); 
+0

@Gordon, vous prêchez à la chorale là-bas. – salathe

+1

@Gordon, je sais que vous ne prêchiez pas, c'est juste le tour de la phrase. :) J'ai ajouté un exemple d'utilisation de 'fnmatch()' avec un 'FilterIterator' qui se sent le plus proche de" equivalent to glob() "dans la question. – salathe

+1

@stereofrog, tout ce qui flotte votre bateau. ;) – salathe

1

De nombreux programmeurs PHP ont tendance à trop compliquer les choses. Un problème simple a toujours une solution simple. Non seulement cela est plus lisible que tout code smart-alec alambiqué, mais aussi beaucoup plus rapide.

+1

+1 pour une solution procédurale simple, -.3 pour mettre la vitesse sur la maintenabilité, -.3 pour le manque de testabilité, -.2 pour le formatage "non conventionnel", -.1 chacun pour '$ k' et' $ v 'en réclamant la lisibilité;) – Gordon