[ARRAY] Mehrdimensionale dynamisch Arrays erstellen

muhkuh

Erfahrenes Mitglied
Servus,

ich hab mir eine kleine Klasse geschrieben (PHP4), die einen Verzeichnisbaum rekursiv ausliest und in einem Array speichert.
PHP:
Array
(
    [0] => ./ardner_misc
    [1] => ./casetull
    [2] => ./drack
    [3] => ./drack/laste
    [4] => ./drack/tust/schumm
)
Das ganze ist schön und gut ;) Ich habe mir jetzt allerdings in den Kopf gesetzt, ob es nicht möglich wäre das Array etwas anders zu gestalten:
PHP:
Array
(
    [0] => ./
        Array
        (
            [0] => ardner_misc/
            [1] => casetull/
            [2] => drack/
                Array
                (
                    [0] => laste/
                    [1] => tuste/
                        Array
                        (
                            [0] => schumm/
                        )
                )
        )
)
Anders ausgegeben so:
PHP:
    $array[ 0 ] = './';
    $array[ 0 ][ 0 ] = 'ardner_misc/';
    $array[ 0 ][ 1 ] = 'casetull/';
    $array[ 0 ][ 2 ] = 'drack/';
    $array[ 0 ][ 2 ][ 0 ] = 'laste/';
    $array[ 0 ][ 2 ][ 1 ] = 'tuste/';
    $array[ 0 ][ 2 ][ 1 ][ 0 ] = 'schumm/';

Ich hab bisher mit meinen - bescheidenen - PHP-Kenntnissen bisher noch auf keine Lösung gekommen das Array wie oben beschrieben umzugestalten. Probleme bereitet mir vor allem das erstellen des mehrdimensionalen Arrays. Ich muss auch gestehen keinen spezielle Anwendung im Kopf zu haben, denke aber, dass das Array in der gewünschten Form wesentlich flexibler ist. Vor allem interessiert mich im Moment lediglich, wie ich das Array entsprechend umwandeln kann. Ich habe wie gesagt auch schon ein wenig herumexperimentiert, allerdings ohne auf einen grünen Zweig zu kommen. Vielleicht gibt es sogar eine Möglichkeit, die mir das Array in gewünschter Form gleich beim rekursiven Auslesen des Verzeichnisbaum erstellt. Anbei die Klasse
PHP:
class listdir
{
	/**
	 *	Variablen
	 */
	var $_arrDirs;
	var $_arrFiles;
	var $_root;
	
	/**
	 *	Konstruktor
	 */
	function listdir( $dirname = null )
	{
		$this->_arrDirs = array();
		$this->_arrFiles = array();
		$this->_root = is_dir( $dirname )
					? $dirname
					: '.'; 
		
		$this->runDirs( $this->_root );
	}
	
	/**
	 *	Methoden
	 */
	// Dateien und Verzeichnisse einlesen (rekursiv)
	function runDirs( $root )
	{
		$handle_dir = opendir( $root );
		
		while( false !== ( $item = readdir( $handle_dir ) ) )
		{			
			if( $item != '.' && $item != '..' )
			{
				if( is_dir( $root . '/' . $item ) )
				{					
					$this->_arrDirs[] =  $root . '/' . $item;
					
					$this->runDirs( $root . '/' . $item );
				}
				else
				{
					$this->_arrFiles[ $root ][] = $item;
				}
			}
		}
		
		closedir( $handle_dir );
		clearstatcache();
		
		ksort( $this->_arrDirs );
		ksort( $this->_arrFiles );
	}
	
	// _root neu setzen und Array mit Verzeichnissen neu erstellen
	function setNewRoot( $root )
	{
		if( is_dir( $root ) )
		{
			die( '$this->setNewRoot(): Das angegebene Verzeichnis ist nicht vorhanden.' );
		}
		
		$this->_root = $root;
		
		$this->_runDirs( $this->_root );
	}
	
	// Dirs ausgeben
	function printDirs()
	{
		return $this->_arrDirs;
	}
	
	// Files ausgeben
	function printFiles()
	{
		return $this->_arrFiles;
	}
}
 
Probiers mal so:
PHP:
    // Dateien und Verzeichnisse einlesen (rekursiv)
    function runDirs( $root, $recursive = true, $recursion = false )
    {
				$this->_arrDirs = $this->runRecursiveDir( $root, $recursive, $recursion);
    }
    
    function runRecursiveDir( $root, $recursive = true, $recursion = false )
    {
        $handle_dir = opendir( $root );
        
        $dir = '';
        
        while( false !== ( $item = readdir( $handle_dir ) ) )
        {            
            if( $item != '.' && $item != '..' )
            {
                if( is_dir( $root . '/' . $item ) )
                {           
										if( $recursive === true )    
										{     
                    		$dir =  $this->runRecursiveDirs( $root . '/' . $item, true, true );
                    }
                    else
                    {
												$dir = $root . '/' . $item;
										}
                }
                else
                {
                    $this->_arrFiles[ $root ][] = $item;
                }
            }
        }
        
        closedir( $handle_dir );
        clearstatcache();
        
        ksort( $this->_arrDirs );
        ksort( $this->_arrFiles );
        return ($recursion === true ? $dir : true);			
		}
 
Zuletzt bearbeitet:
So eine Klasse hab ich auch mal geschrieben, dort wird jedes Unterverzeichnis wiederum als Instanz der selben Klasse angelegt.
Dadurch ist die Rekursion kein Problem mehr und der Verzeichnisbaum kann quasi originalgetreu im Objekt wiedergegeben werden.

Ich hatte auch erst an ein Array wie bei Dir gedacht, fand dann aber dass die Variante die ich jetzt nutze (eben wiederum Objekte im Array zu haben) weitaus flexibler und einfacher war.

Am besten schaust Du Dir einfach mal mein Tutorial dazu an.
 
Ich hab sowas letztens auch gemacht , zwar nicht mit Objekten wie Dennis Wronka sondern mit einen netten Array.

Der Trick ist Rekursion wobei die Funktion ansich selber ein Array zurück gibt.

Der Kern der ganzen Klasse sah so aus um das Array zu erstellen.

PHP:
    private function parseDir ($path) {
            
        $fileArray = array();
             
        $fileStream = opendir($path);
        
        while ($file = readdir($fileStream)) {
            if($file != '.' && $file != '..') {
                if(is_dir($path.'/'.$file) ) {
                    
                    // war eine Einstellung für Tiefenrekursion
                    if($this->current_deep < $this->deep || $this->deep == -1) {
                            
                        $this->current_deep++;
                        $returnArray =  $this->parseDir($path.'/'.$file);
                          
                        /* hier wurde nur geschaut ob der Rückgabe Wert vom Array
                         leer war da der Filter schon gegriffen hat dann wurde das
                         Element nicht mehr reingespeichert
                         würde man normal so speichern dann das gleiche dann 
                         weiter unten
                         $fileArray[$path.'/'.$file] = returnArray */                          
                        if(is_array($returnArray) && count($returnArray) > 0) {
                            $fileArray[($this->fullPath)?$path.'/'.$file:$file] = $returnArray;
                        }                                                 
                        $this->current_deep--;
                    }
                        
                } else {
                     // war für nen Filter
                    foreach ($this->filterValues as $value) {
                        if(preg_match_all('/'.$value.'/i',$file,$matches)) {
                            $fileArray[($this->fullPath)?$path.'/'.$file:$file] = 'file';
                        } 
                    }
                }
            }
        }
       return $fileArray;
    }
 
Wow, danke für die Antworten soweit schon mal! Habe auch gleich ein wenig rumprobiert und kann sagen, dass die Lösung von mAu leider nicht funktioniert. Ich bekomme da lediglich "true" zurück. Die Klasse von Dennis kenne ich auch, hab die hier sogar irgendwo als Code-Schnipsel zum nachgucken rumfliegen ;) Ist im Prinzip genau das, was ich mir auch vorstelle, nur eben mit verschachtelten Objekten. Die Funktion von FlagTheThief werde ich mir heute definitiv auch noch angucken, und mich dann ggf. noch mal melden!
 
Hm... Ich sitz zwar grad in Bio, aber is nich so wichtig... Aender mal folgende Zeile:
PHP:
return ($recursion === true ? $dir : true); 
// in
return $dir;

// und
$dir =  $this->runRecursiveDirs( $root . '/' . $item, true );
// in
$dir =  $this->runRecursiveDirs( $root . '/' . $item, true, true );
Vielleicht gehts dann :)
 
Hat leider keinen Effekt was die Ausgabe angeht. Mir ist aber eben aufgefallen, dass deine Lösung in der rekursiven Funktionen keinen Array, sondern einen String zurückgibt. Ich hab daher die beiden $dir in $dir[] gewechselt. Bringt zwar im Prinzip auch nichts, jetzt bekomme ich aber einen - wenn auch leeres - mehrdimensionales Array zurück. Die Tiefe des Arrays entspricht aber der Tiefe des Verzeichnisbaums ;) Ich bin noch nicht ganz genau durchgestiegen, was die Funktionen genau machen, aber der Weg scheint definitiv der richtige zu sein xD
 
Keine Ahnung, ob das noch aktuell ist ...
Aber da Du sicher nicht der einzige bist, der sowas machen will, hier mal eine - recht simple - Lösung ;-)

PHP:
function rekdir($path = ".") { 
	$dir = dir($path);  
	while(false !== ($file = $dir->read())) {    
		if(("."  == $file) || (".." == $file))
			continue;      
		if(is_dir($path."/".$file)) { 
			$x[$file] = rekdir($path."/".$file);     //Statt $x[$file] kannst Du auch $x[] schreiben ... dann kennst Du den VZ-Namen halt ned.
		} else { 
			$x[] = $file;
		}   
	}	
	$dir->close();  
    return $x;
}

$dirarray = rekdir(".");

echo "<pre>";
print_r($dirarray);
echo "</pre>";

Sicher kann man das noch verfeinern, wenn man möchte, aber ich denke, das Ergebnis ist schon recht brauchbar!

Die wichtigsten Zeilen sind dabei:

PHP:
$x[$file] = rekdir($path."/".$file);
Da es sich um ein Verzeichnis handelt, rufen wir die die Funktion rekursiv auf.
Der Einfachheit halber nehmen wir nun mal an, dass hier bereits die maximale Tiefe erreicht ist ...
Die rekursiv aufgerufene Funktion erstellt nun also ein Array mit den ganzen Files und (ganz wichtig!!)...
PHP:
return $x;
... gibt dieses Array zurück an ...
PHP:
$x[$file]

Danach läuft die Funktion ganz normal weiter und übergibt das mehrdimensionale Array - sobald sie vollständig durchgelaufen ist - an ...
PHP:
$dirarray
... womit wir die Funktion ja ursprünglich aufgerufen haben.

Wir haben dann also genau das erzeugt, was Du haben möchtest :-)

Ich habe es mal in einem Test-Verzeichnis mit Unterverzeichnis und Unter-Unterverzeichnissen und noch einem Unter-Unter-Unterverzeichnis -.- laufen lassen.
Die Unterverzeichnisse habe ich dabei als "Ebene 0" bis "Ebene 2" benannt. Die Testdateien sind einfach kopiert, daher immer dieselben (also nicht verwirren lassen).
Hier das Ergebnis:
PHP:
Array
(
    [Ebene 0] => Array
        (
            [Ebene 1.1] => Array
                (
                    [0] => Testdatei1
                    [1] => Testdatei2
                    [2] => Testdatei3
                )

            [Ebene 1.2] => Array
                (
                    [Ebene 2] => Array
                        (
                            [0] => Testdatei1
                            [1] => Testdatei2
                            [2] => Testdatei3
                        )

                    [0] => Testdatei1
                    [1] => Testdatei2
                    [2] => Testdatei3
                )

            [0] => Testdatei1
            [1] => Testdatei2
            [2] => Testdatei3
        )

    [0] => Testdatei1
    [1] => Testdatei2
    [2] => Testdatei3
    [3] => vzlist.php
)

Ich hoffe, die Erklärung war halbwegs verständlich und nicht zu chaotisch.
Wer es dennoch nicht versteht, schnappt sich einen guten Debugger und durchläuft damit den Code auf einem gut verzweigten Verzeichnisbaum (2-3 Ebenen sollten es schon sein) Schritt für Schritt.
Das sollte dann auf jeden Fall Klarheit schaffen.

Viel Spaß damit ...
 
Hallo WTB oder gern jemand anders,

ich habe zu deinem Programm Code "rekdir" folgende Frage:

Ich möchte eigentlich nur die Verzeichnisse ohne Dateien in das Array schreiben lassen. Allerdings sollen die Namen nicht als Index im Array erscheinen, sondern als Wert. Der Index soll lediglich immer ein numerischer Wert sein. Du hast ja schon drauf hingeweisen, daß man einfach $X[] statt $x[$file] schreiben muß. dann erhalte ich zwar ein Array, wie ich es mir von der Struktur her vorstelle - allerdings dann nur die Dateien. Mir ist schon klar warum, soweit verstehe ich deinen Quellcode schon, aber:
Nun bastel ich hier schon stundenlang, wie ich das array entsprechend mit den Verzeichnisnamen als Wert gefüllt bekomme.
Ich komme aber einfach nicht drauf.

Kannst du mir da bitte helfen?

Vielen Dank im Voraus
 
Verstehe nicht so ganz, wie das aussehen soll. Kannst Du mal skizzieren, wie Du Dir die Ausgabe vorstellst?

Aber mal abgesehen davon ... warum arbeitest Du denn nicht einfach mit den Schlüsseln, das geht doch auch.

Kleines Beispiel:

PHP:
function rekdir($path = "."){ 
	$dir = dir($path);  
	while(false !== ($file = $dir->read()))  {    
		if(("."  == $file) || (".." == $file))
			continue;      
		if(is_dir($path."/".$file)) {
			if(($x[$file]=rekdir($path."/".$file)) == "")
				$x[$file]=".";
		}
	}
	$dir->close(); 
	return $x;
}

function ordered_list ($array){
	foreach ($array as $key => $val) {
		$y.= "<li>".$key."</li>";
		if(is_array($val)) {
			$y.= ordered_list($val);
		}
	}
	return "<ol>".$y."</ol>";
}

$dirarray = rekdir(".");
echo "<pre>";
print_r($dirarray);
echo "</pre>";
echo ordered_list($dirarray);

Das gibt Dir die Verzeichnisse dann als geordnete Liste (html) aus.
Also die ordered_list Funktion.

Kommt aber vielleicht auch darauf an, was Du damit anstellen willst ... das kann ich mir nur eben so nicht ganz vorstellen.

Grüße, WTB.
 
Zurück