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!
Umbenennung von mehreren Tif-Dateien
-
- 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.
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()
@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.
-
- 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.
Irgendjemand muss immer durchs Raster fallen.
Und da diese auch unbekannt bleiben möchten dürfen sie das auch.
Irgendjemand muss immer durchs Raster fallen.
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()
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
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
@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.
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.
@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:
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.
@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 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.
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]
[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]