Umbenennung von mehreren Tif-Dateien

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Jaylan
User
Beiträge: 2
Registriert: Mittwoch 11. Mai 2016, 19:18

Hallo zusammen,

ich möchte ein kleines Programm schreiben, welches mir ein Präfix an den bereits vorhandenen Dateinamen anhängt.
Die Dateiennamen haben folgendes Format:
Whitney_Houston_0001
Whitney_Houston_0002
Whitney_Houston_0003

D.h. Name_Nachname_Bildindex

Der Suffix 0001, 0002, 0003 ist der Bildindex. Hier bedeutet das, dass von Whitney Houston 3 verschiedene Bilder vorhanden sind.
Nun möchte ich, an den Dateinamen vorne, eine Personenkennung als Ziffer anfügen.
Jede Person soll eine eigene Ziffer haben und danach soll ein Unterstrich erfolgen. Der Personenkennungs-Index soll sich immer um einen Wert erhöhen, wenn sich der Name unterscheidet.

Bsp.:
000000_Whitney_Houston_0001
000000_Whitney_Houston_0002
000000_Whitney_Houston_0003
000001_Wolly_Peterson_0001
000001_Wolly_Peterson_0002
000002_Yve_Richard_0001
...

Vielleicht hat ja jemand schon sowas implementiert. Ich bin Anfängerin und würde mich über jede Hilfestellung freuen.

Dieses Programm würde erheblich die Umbennung meiner Daten erleichtern.

Vielen Dank im Voraus!
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Jaylan: was hast Du schon probiert? Wo kommst Du konkret nicht weiter?
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Der unten aufgeführte Code läuft unter Python2 .
Da bisher von dir kein eigener Code zur Verfügung steht hab ich ein kleines Hindernis eingebaut.
Das kannst du einfach beheben, wenn du den Code verstehst.

Code: Alles auswählen

#! /usr/bin/python
# -*- coding: utf-8 -*-

class Artist(object):
    def __init__(self, add_new_artist=False):
        self.musiker_id = {'Whitney_Houston': {'index': '000001', 'anzahl': 0}}
        self.zaehler = len(self.musiker_id.keys())

        self.option = {'add_new_artist': add_new_artist}

    def check(self, musiker):
        if self.option['add_new_artist']:
            if not musiker in self.musiker_id: 
                self.zaehler += 1
                self.musiker_id.update({musiker: {'index': '', 'anzahl': 0}})
                self.musiker_id[musiker]['index'] = str(self.zaehler).rjust(6,'0')
            self.musiker_id[musiker]['anzahl'] += 1

            return self.musiker_id[musiker]['index']
        else:
            try:
                self.musiker_id[musiker]['anzahl'] += 1
                return self.musiker_id[musiker]['index']
            except KeyError:
                print "Warning - Artist Unbekannt: %s" % (musiker)
                return 'Unknown'

    def rename_files(self, musik_dateien):
        neue_liste = []
        for datei in musik_dateien:
            try:
                # gesuchtes Muster: Name_Nachname_Bildindex
                name, nachname = datei.split('_')[0:2]
            except ValueError:
                print "Warning - Dateiname ist unpassend '%s'" % (datei)
                print "Info - Überspringe Datei"
                continue

            musiker = "%s_%s" % (name, nachname)
            personenkennungs_index = self.check(musiker)

            if not personenkennungs_index == 'Unknown':
                neuer_dateiname = "%s_%s" % (personenkennungs_index, datei)
            else:
                neuer_dateiname = datei

            neue_liste.append(neuer_dateiname)
        return neue_liste
    
    def show(self):
        for musiker in self.musiker_id:
            print "%s - Anzahl %s" % (musiker, str(self.musiker_id[musiker]['anzahl']))

def get_dateien():
    # hier einfach die Dateinamen ermitteln
    files = ['Whitney_Houston_0001', 'Whitney_Houston_0002', 'Whitney_Houston_0003',
             'Wolly_Peterson_0001', 'Wolly_Peterson_0002', 'Yve_Richard_0001']

    return files

def main():
    musik_dateien = get_dateien()

    artist = Artist(add_new_artist=False)
    musik_dateien = artist.rename_files(musik_dateien)

    print '\n'.join(musik_dateien)

    # artist.show()

if __name__ == '__main__':
    main()
BlackJack

@sebastian0202: Was passiert wenn es einen Künstler gibt der sich 'Unknown' nennt? Statt spezieller Rückgabewerte für Ausnahmefälle, könnte man Ausnahmen verwenden. Dafür sind die erfunden worden. :-)
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Es gibt zahlreiche Interpreten die Unknown sind. Die können dann eh nicht eindeutig unterschieden werden.
Und da diese auch unbekannt bleiben möchten dürfen sie das auch. :P
Irgendjemand muss immer durchs Raster fallen.
BlackJack

Ein etwas weniger unnötig umständlicher Ansatz (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function
import os
import re
from collections import defaultdict

SOURCE_NAME_RE = re.compile(r'(?P<name>.+)_\d{4}')


def main():
    name2filenames = defaultdict(list)
    for filename in os.listdir(os.curdir):
        match = SOURCE_NAME_RE.match(filename)
        if match:
            name2filenames[match.group('name')].append(filename)

    for i, (_, filenames) in enumerate(sorted(name2filenames.iteritems())):
        for filename in filenames:
            new_filename = '{0:6d}{1}'.format(i, filename)
            print('rename {0} -> {1}'.format(filename, new_filename))
            #os.rename(filename, new_filename)


if __name__ == '__main__':
    main()
Jaylan
User
Beiträge: 2
Registriert: Mittwoch 11. Mai 2016, 19:18

Erstmal vielen Dank für die Lösungsvorschläge. Tut mir leid, dass ich spät antworte.

Vielleicht noch eine paar wichtige Hinweise zu meinem Vorhaben.
Ich habe einen Ordner mit ca. 4500 TIFF-Dateien, also Bilddateien. Nicht alle Personen sind Musiker, es sind auch Personen aus verschiedenen Branchen vorhanden :) Das ändert natürlich nicht viel am Skript.
Für mich sind folgende Punkte wichtig.
- Die Dateien sind bereits sortiert in dem Ordner enthalten.
- Manche Personen haben auch Zwei Vornamen, wie z.B. Vorname_Vorname_Name_Bildindex.
- Es sollte der Pfad des Ordners, wo sich die Dateien befinden im Skript angegeben werden, damit diese eingelesen werden - gibt es denn generell Probleme bei so vielen Dateien? Wie kann man das am besten lösen?
- Die Umbenennung erfolgt auf den originalen Dateien

Bin leider Anfängerin in Python, aber mir wurde gesagt, dass dieses Problem in Python besser und einfacher zu lösen wäre, als z.B. in C++ oder in Java.

@BlackJack: Wo muss ich denn Änderungen vornehmen? Wo kann ich meinen Pfad angeben? Wo den Typ der Datei? Oder ist das egal?
@sebastian: Scheint mir bisschen kompliziert, aufgrund fehlender Erfahrung :)

LG,

Jaylan
BlackJack

@Jaylan: Der letzte Punkt ist, äh, mutig. Gerade wenn man selber das erste mal programmiert, sollte man so eine potentiell gefährliche Operation besser nicht auf den originalen Daten durchführen.

Du müsstest halt anfangen zu verstehen was da passiert, dann lernst Du auch wo man Änderungen vornehmen muss. :-) Fang am besten an erst einmal Python zu lernen. Denn in Python programmieren, ohne Python zu können, geht nicht.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Jaylan: wenn Du den Code verstanden hast, erklären sich die meisten Deiner Fragen von selbst und bis auf Punkt 3 sind auch alle schon bedacht. Bei konkreten Verständnisproblemen kannst Du natürlich gerne noch Fragen. Wenn Du C++ oder Java schon beherrschst, ist das Lösen des Problems in der bekannten Sprache einfacher als in einer unbekannten, auch wenn es mehr als 25 Zeilen werden.


@sebastian0202: noch ein paar Anmerkungen:
Zeile 7: Warum zählst Du die Länge von musiker_id händisch mit, wo Du doch die Länge jederzeit neu bestimmen kannst? Es reicht übrigens `len(self.musiker_id)`, das spart das unnötige Erzeugen einer Liste aller Keys, nur um deren Länge dann zu nehmen.
Zeile 9: Was soll das option-Wörterbuch. Wenn es Einstellungen für ein Objekt gibt, dann sollten das direkt Attribute des Objekts sein.
Zeile 11: check sollte besser get_musiker_index, o.ä. heißen. Mit check verbindet man ein True/False Ergebnis und keinen String.
Zeile 13: besser `a not in b` statt `not a in b`
Zeile 15: Um **einen** neuen Eintrag in ein Wörterbuch zu schreiben nimmt man nicht `update` sondern Indexzugriff. Erst einen Dummywert in ein Wörterbuch einzutragen, um ihn dann in der nächsten Zeile zu überschreiben ist umständlich, erzeug doch gleich ein Wörterbuch mit den richtigen Werten.
Zeile 16: Für führende Nullen und allgemein zum Formatieren von Zahlen gibt es str.format: "{0:06d}".format(len(self.musiker_id))
Zeile 22f: Diese Zeilen sind identisch im if-Block. Das sollte es nur einmal geben.
Zeile 25,35: Die Klammern sind überflüssig

Alles in allem sähe check besser so aus:

Code: Alles auswählen

    def get_musiker_index(self, musiker):
        try:
            entry = self.musiker_id[musiker]
        except KeyError:
            if self.add_new_artist:
                entry = self.musiker_id[musiker] = {
                    'index': '{0:06d}'.format(len(self.musiker_id)+1),
                    'anzahl': 0
                }
            else:
                print "Warning - Artist Unbekannt: {0}".format(musiker)
                raise
        entry['anzahl'] += 1
        return entry['index']
Zeile 28: die Funktion rename_files nennt keine Dateien um: falscher Funktionsname
Zeile 37: continue vermeiden. Hier böte sich der else-Block des try-except an.
Zeile 42: statt not a==b besser a!=b, noch besser mit Ausnahmen arbeiten
Zeile 51: es gibt iteritems wenn sowohl der Key als auch der Wert gebraucht wird.
Zeile 52: das `str` ist überflüssig

Allgemein: Da Du viel mit Leerzeilen arbeitest erhöhen zwei Leerzeilen vor Funktionsdefinitionen die Lesbarkeit enorm.
BlackJack

Mal aus Neugier wie gut die anomymen Funktionen dabei helfen das in Java nicht allzu umständlich zu formulieren (ungetestet):
[codebox=java file=Unbenannt.java]import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Predicate;
import java.util.regex.Pattern;

class Filename {
private static final Pattern NAME_PATTERN =
Pattern.compile("(?<name>.+)_\\d+");
private static final Predicate<String> NAME_PREDICATE =
NAME_PATTERN.asPredicate();

private final Path path;

public Filename(Path path) {
this.path = path.normalize();
if (!isValid(path)) {
throw new IllegalArgumentException("Path does not follow pattern");
}
}

public String getFilename() {
return path.getFileName().toString();
}

public String getArtistName() {
return NAME_PATTERN.matcher(path.getFileName().toString())
.group("name");
}

public static boolean isValid(Path path) {
return NAME_PREDICATE.test(path.getFileName().toString());
}

public void rename(int i) {
final Path newPath =
path.getParent()
.resolve(String.format("%06d_%s", i, getFilename()));
System.out.printf("%s -> %s", path, newPath);
try {
Files.move(path, newPath);
} catch (FileAlreadyExistsException e) {
System.out.printf("%s already exists!", newPath);
} catch (IOException e) {
e.printStackTrace();
}
}
}[/code]
[codebox=java file=Unbenannt.java]import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class Main {

public static void main(String[] args) throws IOException {
final Map<String, List<Filename>> artistName2Filenames = Files.find(
Paths.get(""),
1,
(path, attributes) ->
!attributes.isDirectory() && Filename.isValid(path)
)
.map(Filename::new)
.collect(Collectors.groupingBy(Filename::getArtistName));

final AtomicInteger serial = new AtomicInteger();
artistName2Filenames
.keySet()
.stream()
.sorted()
.sequential()
.forEach(artistName -> {
final int i = serial.incrementAndGet();
artistName2Filenames.get(artistName).forEach(filename ->
filename.rename(i));
});
}
}[/code]
Antworten