Array nach bestimmten Schema ausgeben ?

Hallo Community,

das folgende PHP-Script sortiert Dateinamen aus einem Ordner nach dem von mir bestimmten Angabe.

Die Dateinamen haben folgendes Schema: XXXXX_JJJJ_MM.png

XXXXX = fünf Großbuchstaben
JJJJ = Jahr (vierstellig)
MM = Monat (zweistellig)

Beispiel: ABCDE_2018_07.png

In diesem Ordner kommt jeden Monat eine neue Datei. Die Dateien reichen im Augenblick bis 2010_10 zurück.

Das Script:

 <?php
$dir = "Pfad/zu_den/Bildern";
if (is_dir($dir)) {
if ($dh = opendir($dir)) {
while (($filename2 = readdir($dh)) !== false) {
if($filename2 != "." && $filename2 != ".." ) $files2[] = $filename2;
}
$aktuell=str_replace(' ','',date("m Y"));
$vergangen=str_replace(' ','',date("m Y", strtotime("-1 month -0 year")));
$oldyear=str_replace(' ','',date("Y", strtotime("-2 year")));
$oldmonth=str_replace(' ','',date("m", strtotime("-2 month")));
$aktuelljahr=str_replace(' ','',date("Y"));
$letztesjahr=str_replace(' ','',date("Y", strtotime("-1 year")));

foreach ($files2 as $con1) {
  $in[] = array(
    'rest_1' => substr ( $con1, 6, 4 ),
    'pre_1' => substr ( $con1, 11, 2 ),
    'test_1' => $con1
  );
}

foreach($in as $key => $row)
{
    $array_rest_1[$key]   = $row['rest_1'];
    $array_pre_1[$key]    = $row['pre_1'];
}

array_multisort($array_rest_1,SORT_DESC,$array_pre_1,SORT_DESC,$in);

$out = array();
reset($in);
while(list($key, $val) = each($in)){
$out[] = $val['test_1'];
#echo $val['test_1'] . "<br />"; // Gibt alle Dateinamen sortiert (JJJJ - MM) aus

// Gibt nur Dateinamen mit 2018 (aktuelljahr) aus
//if ($val['rest_1'] == $aktuelljahr)
//echo $val['test_1'] . "<br />";
// Ende Ausgabe (aktuelljahr)

// Gibt nur Dateinamen mit 2017 (letztesjahr) aus
//if ($val['rest_1'] == $letztesjahr)
//echo $val['test_1'] . "<br />";
// Ende Ausgabe (letztesjahr)

// Gibt nur Dateinamen mit 2017 (letztesjahr) und 2016 (oldyear) aus
if ($val['rest_1'] == $letztesjahr)
echo $val['test_1'] . "<br />";
if ($val['rest_1'] == $oldyear)
echo $val['test_1'] . "<br />";
// Ende Ausgabe (letztesjahr) und (oldyear)

}


echo "<br />";
echo count ($out) . "<br />";
echo "<br>";
echo $aktuell . "<br />";
echo $vergangen . "<br />";
echo $aktuelljahr . "<br />";
echo $letztesjahr . "<br />";
echo $oldyear . "<br />";
echo "<br>";

closedir($dh);
}
}
?>

Die Ausgaben Funkpioniern einzeln, wenn ich die nicht benötigten auskommentiere.

Ich möchte eine Ausgabe des sortierten Array ($out) nach folgendem Schema erreichen:

Den Dateinamen des Aktuellen Monats (im Moment XXXXX_2018_08.png)
Zwei leer Zeilen (br)
Die restlichen Dateinamen des aktuellen Jahres (im Moment 2018)
Zwei leer Zeilen (br)
Alle Dateinamen des letzten Jahres (im Moment 2017) = Aktuelles Jahr -1
Zwei leer Zeilen (br)
Alle Dateinamen des vor letzten Jahr = Aktuelles Jahr -2
Zwei leer Zeilen (br)
usw. bis zum ältesten Jahr in Array (im Moment 2010)

Wie mache ich das am einfachsten, ohne viele komplizierte Schleifen-Konstrukte ?

Das Ausgabe Schema soll natürlich erhalten bleiben wenn noch Dateien aus dem Jahre 2009 oder früher hin zu kommen, und wenn im nächstes Jahr (2019) das aktuelle ist.

Ich hoffe ich habe mich verständlich ausgedrückt.

Über eine Antwort und ausführliche Erklärung würde ich mich freuen, da ich noch nicht lange mit PHP scripte.

Vielen Dank
Andreas

Hallo,

bin zwar eigentlich Perl-Programmierer, habe aber mal die Gelegenheit genutzt, etwas PHP zu frickeln.

Ergebnis -> https://pastebin.com/HMA2H3mt

Gruß,
Steve

Vielen Dank für deine Antwort.

Beim ausführen der Datei erhalte ich zwei Fehlermeldungen:

Notice: Undefined offset: 0 in test_4.php on line 27

Notice: Undefined offset: 0 in test_4.php on line 30

Die PHP-Version die auf dem Server läuft: PHP 7.2.7

Grüße aus dem Ruhrgebiet
Andreas

1 Like

Mein Lösungsansatz: https://pastebin.com/fQDFg9rM

Gruß
Andreas

Meine Version hat praktisch keine Fehlerbehandlung.

Diese Meldung zeigt, dass du keine aktuelle Datei (2018_08) in dem Verzeichnis hast.

Gruß,
Steve

du machst es dir viel zu kompliziert. Schau dir mal glob() an. Und warum du diese ganzen String-operationen nach date() machst, ist mir auch nicht klar. Sowas deutet immer darauf hin, dass du die liefernde Funktion (in dem Fall date()) falsch verwendest.

Hier mein Vorschlag in unter 25 Zeilen (ohne Kommentare):

$directory = "Pfad/zu_den/Bildern";
$currentYear=date("Y");
$currentMonth=date("m");

//Den Dateinamen des Aktuellen Monats (im Moment XXXXX_2018_08.png)
foreach (glob("$directory/*$currentYear_$currentMonth.png") as $filename) 
{
    echo "$filename<br><br>";
}

//Die restlichen Dateinamen des aktuellen Jahres (im Moment 2018)
for($i=$currentMonth+1;$i<=12;$i++)
{
    //füge führende Nullen an (9 wird zu 09)
    $monthToCheck = sprintf('%02d', $i);
    
    foreach (glob("$directory/*$currentYear_$monthToCheck.png") as $filename) 
    {
        echo "$filename<br><br>";
    }
}

//Alle Dateinamen des letzten Jahres (im Moment 2017) = Aktuelles Jahr -1
//Zwei leer Zeilen (br)
//Alle Dateinamen des vor letzten Jahr = Aktuelles Jahr -2
//Zwei leer Zeilen (br)
//usw. bis zum ältesten Jahr in Array (im Moment 2010)

//Hinweis: ich gehe bis ins Jahr 2000 zurück. Was es nicht gibt, gibt es nicht. Performancseitig spielt es auch keine Rolle.
for($i=$currentYear-1;$i>=2000;$i--)
{
    foreach (glob("$directory/*$i_*.png") as $filename) 
    {
        echo "$filename<br><br>";
    }
}

Ihr beide solltet euch mal ganz dringend mit dem KISS-Prinzip beschäftigen. Dieses Arraygeschiebe ist unnötig kompliziert. Prüfungen sollten so viele wie nötig aber so wenig wie nötig drin sein. is_dir()?? wozu? Regex überall, wo ein paar wildcards reichen.

$while() ohne Abbruchbedingung in der Klammer? Seriously?

1 Like

Sorry, Leute…

Ich möchte dem Frager noch an die Hand geben, dass sprechende Variablennamen und der eine oder andere Quelltextkommentar ungemein zur Lesbarkeit des Codes beitragen. Mach lowerCamelCase.

yearNumberOfTenYearsAgo ist viel (!) einfacher zu lesen als yearnumberoftenyearsago oder gar solche kruden Abkürzungen wie yrnum10yrsago.

Und mit kruden Abkürzungen meine ich auch sowas hier:

foreach ($files2 as $con1) {

Warum werden Files zu Connections? Oder was soll $con für eine Abkürzung sein? Und warum $files2? Was ist der Unterschied zu $files1 oder gibts das gar nicht und das heißt $files? Schreib keine Zahlen in die Variablennamen, das ist immer unintuitiv zu verstehen. Die Länge von Variablennamen ist in keiner modernen Programmiersprache begrenzt. Beschreibe in den Variablennamen den Inhalt. Eindeutig! Ohne Abkürzungen!
Ausnahme hiervon ist $i als Zählvariable bei einer einfachen for-Schleife. Das verstehen alle Programmierer sofort. Aber auch hier gilt: sobald es kompliziert wird (etwa mehrere verschachtelte Schleifen), nimm sprechende Variablen.

Natürlich könnte ich jetzt bei jeder Variable suche, wo sie definiert ist und das dann nachvollziehen. Aber das ist viel komplizierte, als wenn deine Variable mir gleich sagt, was sie tut.

Und gerade bei foreach() würde ich immer den Ansatz fahren:

foreach($files as $file){}

$files (Plural) hält viele Dateien, $file (Singular) immer nur genau eine.

Zu den Quelltextkommentaren möchte ich beispielhafte Zeile nennen:
array_multisort($array_rest_1,SORT_DESC,$array_pre_1,SORT_DESC,$in);

Das ist so eine unintuitive Funktion, dass hier ein paar erklärende Worte zum gewünschten Ziel und zur konkreten Verwendung hilfreich wären.

Oder auch

$in[] = array(
    'rest_1' => substr ( $con1, 6, 4 ),
    'pre_1' => substr ( $con1, 11, 2 ),
    'test_1' => $con1
  );

Warum nimmst du nur Substrings? Warum gerade von 6-4 und 11-2? Warum haben alle Variablen eine _1 als Suffix? Gibt es auch eine _2? Ein paar Kommentare wären so hilfreich.

Und als letztes… Einmal heißt es $oldyear, einmal $aktuelljahr. Einige dich auf eine Sprache (hust englisch hust) und mach keine Abkürzungen!

3 Like

Danke für die Rückmeldung. Jetzt wo die Datei im Ordner liegt erscheint auch keine Fehlermeldung mehr. :smile:

Vielen Dank für deine Antworten, Hinweise, und für das kurze Script. Werde deine Ratschläge beherzigen.

Da ich noch nicht lange mit PHP-Scripte war dieses Arraygeschiebe war auch zu Übungszwecken, wie auch RegEx.

Meinen HTML- und CSS-Code kommentiere ich, und nutze sprechende Variablen. Da sich das ganze aus einer Übung von mir entwickelt hat habe ich dummerweise nicht so sehr darauf geachtet.

Werde in Zukunft stärker auf Kommentare, Sprechende Variablen und KISS achten.

Vielen Dank für deine Hilfe.

OK die Abbruchbedingung in der While-Schleife habe ich Hust Vergessen Hust .

Und glob() habe ich nicht genutzt da es später sein kann das die Bilddateien auf einen anderen Server als das Script liegen.

Und auf dieser Seite: https://secure.php.net/manual/de/function.glob.php

steht:

Hinweis: Diese Funktion kann nicht mit entfernten Dateien arbeiten, da der Zugriff auf die Datei, die bearbeitet werden soll, über das Dateisystem des Servers möglich sein muss.

Davon war aber 1. nicht die Rede und 2. widerspricht das dem YAGNI-Prinzip, irgendwas zu programmieren, was man eventuell später mal brauchen könnte. Wobei auch YAGNI nur das Ergebnis von KISS ist.

und

ging nicht nur an dich, sondern auch an den zweiten Antworter.

Viel Erfolg noch.

1 Like

Oh da hab ich mich wohl falsch ausgedrückt. Brauche das Script auf jeden Fall.

Nur die Bilder sollen eventuell mal auf einem anderen Server liegen, und das Script soll diese dann von dort einlesen. Ich denke aber das alles auf dem selben bleibt, Deshalb habe ich es nicht erwähnt.

Auch folgendes habe ich offensichtlich unglücklich formuliert:

Die restlichen Dateinamen des aktuellen Jahres (im Moment 2018)

Werden durch die folgende Zeile aus deinem Script, im August nur die Monate 09 - 12 aus 2018 ausgegeben ? Von diesen existieren noch gar keine Bilder. Oder habe ich da einen Denk- bzw. Verständnis-Fehler ?

//Die restlichen Dateinamen des aktuellen Jahres (im Moment 2018)
for($i=$currentMonth+1;$i<=12;$i++)

Ich meinte die Monate 01 - 07 aus 2018 . Und wenn im September die Datei XXXXX_2018_09.png hinzukommt. Soll diese als aktueller Monat ausgegeben werden. Und die 2018_08 mit den anderen Dateien ( 01 - 08 dann) aus 2018 ausgegeben werden. Das meinte ich mit den restlichen Dateinamen aus 2018.

Sorry das ich mich nicht Verständlich genug ausgedrückt habe.

Deine Anmerkungen zu meinem Quelltext (Kommentare, Sprechende Variablen, KISS) und die anderen Hinweise finde gut und richtig. Denn nur so kann ich als Anfänger auch was lernen und durch Struktur Fehler vermeiden.

Dafür nochmal vielen Dank

Ergänzung: Werden durch die folgende Zeile aus deinem Script, im August nur die Monate 09 - 12 aus 2018 -2000 ausgegeben ?

Meinte ich.

Sorry passiert halt wenn man vor dem Absenden nicht noch einmal liest was man geschrieben hat !

Das meine ich ja nicht mit YAGNI.

DAS meine ich mit YAGNI.

Und wir müssen das auch unterscheiden… Ein anderer Server, der im internen Netz ist und auf den du per Freigabe zugreifst, für den gilt mein Script trotzdem. Glob() kann nur nicht direkt mit externen Servern umgehen, die du nur per HTTP erreichst. Aber dafür würde auch dein Arraygeschiebe nicht taugen.

Wenn das ein Linux-Rechner ist, kannst du dir aber eine FTP-Freigabe oder eine WebDAV-Freigabe auf einen lokalen Ordner mounten. Dann geht darauf auch wieder glob().

ja, das ist so. Du wolltest die restlichen Dateien des aktuellen Jahres.

dann stricke es halt um. Ist doch nicht so schwer.

for($i=1;$i<$currentMonth;$i++)

Welche Zeile meinst du wohl?

Andreas, du verstehst mich irgendwie falsch. Mein Script ist als Startpunkt für dich gedacht. Von da aus kannst du weitermachen. Hast du mein Script mal probiert? Warum versuchst du nicht, deine Anpassungen selbst umzusetzen? Kannst ja dein neues Ergebnis dann mal vorzeigen.

1 Like