Java Linux: Krasse Sonderzeichen im Pfad, Umbenennen scheitert (Java)

CyborgBeta

Captain
Registriert
Jan. 2021
Beiträge
3.740
Hallo,

sowohl:

Java:
        System.out.println("Renaming: " + file.getName() + " ---> " + newName);
        System.out.println("Result: " + file.renameTo(new File(file.getParent() + File.separator + newName)));

als auch:

Java:
        System.out.println("Renaming: " + file.getName() + " ---> " + newName);
        System.out.println("Result: " + Files.move(file.toPath(), Path.of(file.getParent(), newName)));

scheitert, mit folgender Fehlermerldung: java.nio.file.InvalidPathException: Malformed input or input contains unmappable characters:.

Erst, wenn ich vorher in dem Verzeichnis find . -type f -exec bash -c 'mv -vn "$1" "$(iconv -f UTF8 -t ASCII//TRANSLIT <<< $1)"' -- {} \; aufrufe, geht es.

Hat jemand eine Idee, woran das liegt - oder ob das ein JRE Bug ist?

Das Verzeichnis enthält u. a. Dateien, die Leerzeichen und Symbole im Namen haben, die ich aber mit ls auflisten kann ...

Ich habe schon versucht, alles auf en_US.UTF-8 umzustellen, aber ich glaube, das sind UTF-16 Zeichen.
 
Ich bin aus dem Zeug schon etwas raus,
passiert das auch wenn du es so machst?
1. Die Dateipfade sauber zusammenbauen, via Paths (https://docs.oracle.com/javase/7/docs/api/java/nio/file/Paths.html)
2. dann sowas nutzt wie toAbsolutePath?

Im allgemeinen versucht man nicht PFade selber mit + Separator + ... zusammenzubauen, weil das nicht platformunabhängig ist.
toAbsolutePath ist dann platform-spezifisch implementiert und müsste dann Pfade korrekt berechnen
 
  • Gefällt mir
Reaktionen: ILoveShooter132 und CyborgBeta
zu 1. ja, auch

zu 2. toAbsolutePath nutze ich bereits, also es sind keine relativen Pfade
Ergänzung ()

Moment, will nichts Falsches erzählen, ich teste es noch einmal mit Paths
Ergänzung ()

Ja, auch:

Java:
          System.out.println("Renaming: " + file.getName() + " ---> " + newName);
          System.out.println("Result: " + Files.move(file.toPath(), Paths.get(file.getParent(), newName)));

Code:
Renaming: DJ BR&NU's Tech House 2024 Mix??? Pure Vibes Only ??? #TechHouse #ElectronicRevival #BeatDriven [PjYSrgKwzYI].mp3 ---> DJ BRNUs Tech House 2024 Mix Pure Vibes Only TechHouse ElectronicRevival BeatDriven.mp3
Exception in thread "main" java.nio.file.InvalidPathException: Malformed input or input contains unmappable characters:

Code:
$ ls DJ*
'DJ BR&NU'\''s Tech House 2024 Mix'$'\357\274\232'' Pure Vibes Only '$'\357\275\234'' #TechHouse #ElectronicRevival #BeatDriven [PjYSrgKwzYI].mp3'
 
Zuletzt bearbeitet:
Im Terminal lang ausführen, bei den Umgebungsvariablen die zurückkommen sollte "LANG=" definiert sein. Die Sprache ist dabei weniger wichtig als das da nach dem Punkt UTF-8 steht. Entsprechend sollte die Java JRE mittels System.getProperty("file.encoding") auch UTF-8 zurückgeben.

Ansonsten wäre mein Ansatz in der Richtung java.net.URLDecoder.decode(file.getName(), "UTF-8")
 
  • Gefällt mir
Reaktionen: BeBur und CyborgBeta
Ich glaub, hier ist einiges durcheinander: (Debian)

Code:
$ locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
 
Es hat geklappt. :)

Code:
$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=en_US.UTF-8

Erreicht durch:

.bashrc:

Code:
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8

Und vorheriger Generierung von en_US.UTF-8 mit sudo dpkg-reconfigure locales ...

Ergebnis:

Renaming: Martin Garrix, David Guetta, Ti��sto, Hardwell, Avicii, Calvin Harris (Dyglex Mix) [ApzwBvVWISM].mp3 ---> Martin Garrix, David Guetta, Tisto, Hardwell, Avicii, Calvin Harris (Dyglex Mix).mp3

Nur etwas seltsam, dass das im Allgemeinen notwendig ist.
 
CyborgBeta schrieb:
Nur etwas seltsam, dass das im Allgemeinen notwendig ist.
Man kann im Allgemeinen nicht definitiv ableiten, welches encoding korrekt ist. Daher wird das in der Regel nicht gemacht. Irgendwas wird einfach als default genommen und wenn das nicht passt, dann hat man Pech, bzw. das muss man schlicht wissen beim Einlesen.
 
  • Gefällt mir
Reaktionen: CyborgBeta
Heißt das im Umkehrschluss, man sollte darauf achten, möglichst keine Dateinamen zu haben, die Sonderzeichen/Umlaute enthalten?
 
Du kannst kaum gewährleisten, 100% Kontrolle über die Pfade zu haben, von daher halte ich das nicht für einen guten Weg. Fast die Hälfte der Menscnheit verwendet Schriftzeichen jenseits von ASCII und haben z.B. Namen mit Zeichen jenseits von ASCII, von daher erschöpft sich das Problem nicht in Sonderzeichen und Umlauten.
Man muss halt die Mechanismen nehmen, die das Betriebssystem anbietet, wobei ein Nutzer immer was kaputt machen kann. Texteditoren nutzen Heuristiken um zu raten, da gibt es auch entsprechende Bibliotheken. Aber jenseits von Fließtext kann man sich vermutlich nur sehr begrenzt darauf verlassen.
 
  • Gefällt mir
Reaktionen: CyborgBeta
Ok, das klingt einleuchtend ... dann eben Linux (also die Sprache) korrekt konfigurieren, und Java somit einen Tipp geben, dass nicht alle Zeichen im Pfad ASCII (POSIX)-konform sind. Das ist prinzipiell auch nicht schlimm, nur man muss es halt (wie fast immer auch ...) vorher wissen.
 
  • Gefällt mir
Reaktionen: BeBur
Glück hast du, wenn du selber auch der Nutzer bist.. ich musste mal einem IT-Laien erklären, wieso seine YAML-Datei nicht richtig verarbeitet wird und was er machen muss damit das funktioniert. Hatte er die Datei doch "ganz normal wie immer mit Wordpad gespeichert".... Ich hatte gar nicht gewusst, dass es Wordpad überhaupt noch gibt.
Dabei fällt mir auch ein, dass noch nichtmal gewährleistet ist, dass wenn man Text aus Dateien extrahiert, die mit solchen Programmen erstellt wurden, dass die dann nur ein einzelnes encoding haben. Soweit ich mich erinnere geht Wordpad hin und codiert zeichen auch gerne mal anders als den Rest des Textes (wenn du die Zeichen von woanders kopierst). Würde mich nicht wundern, wenn andere Programme das zum Teil auch so machen.
 
  • Gefällt mir
Reaktionen: CyborgBeta
😬 Ja, Textdateienencodings, Lineendings usw. Aber das Fass will ich jetzt nicht aufmachen.
 
  • Gefällt mir
Reaktionen: dasBaum_CH und BeBur
CyborgBeta schrieb:
Heißt das im Umkehrschluss, man sollte darauf achten, möglichst keine Dateinamen zu haben, die Sonderzeichen/Umlaute enthalten?
Für einen selbst nicht so von Relevanz.
Sollten Dateien aber über verschiedene Systeme verteilt werden, speziell auch über Sprachgrenzen hinaus, dann sollte schon etwas Zurückhaltung ausgeübt werden. Ist im geschäftlichen Umeld eher von Bedeutung.

Wobei BeBur natürlich Recht hat, dass man sich speziell bei Drittpersonen nicht darauf verlassen kann.
 
  • Gefällt mir
Reaktionen: CyborgBeta
CyborgBeta schrieb:
Ok, das klingt einleuchtend ... dann eben Linux (also die Sprache) korrekt konfigurieren, und Java somit einen Tipp geben, dass nicht alle Zeichen im Pfad ASCII (POSIX)-konform sind. Das ist prinzipiell auch nicht schlimm, nur man muss es halt (wie fast immer auch ...) vorher wissen.
Ähhhhh
Also Linux ist ein Kernel und Bash ist eine Shell/Scripsprache die sich versucht ein Posix/ISO Standards zu halten, es gibt aber auch andere Shells mit leicht unterschiedlichen Verhalten. Das ist in etwa so verwirrend wie unter Windows mit CMD, Powershell5, Powershell7 und Aliases auf Unixbefehle, die nicht Parameterkompatibel sind..

Genug des Rants, weiter Klugschiss:
Linux akzeptiert als Dateinamen (Ordner sind auch Dateien, nur anders) alles und behandelt das auch nur als Abfolge von Bytes. Wie diese Bytes zu interpretieren sind steht in der Umgebungsvariable $LANG. Die einzige sonstige Randbedinung ist, dass Bitfolgen, die als \0 [1] interpretiert werden können unzulässig sind. Naja und / wird im Dateinamen etwas haarig, ansonsten kannst du Sonderzeichen nutzen wie du lustig bist. Theoretisch kannst du sogar Bitfolgen setzen, für die UTF-8 keine Entsprechung kennt [2].
Java sollte intern mit UTF-8 arbeiten, sich bei Zugriffen aufs Betriebssystem (Dateisystemzugriffe zum Beispiel) die $LANG besorgen um davon abzuleiten wie die Pfade zu interpretieren sind. Was bei leerer $LANG nicht funktioniert. Wenn dein Anspruch ist, dass dein Programm auf verschiedenen Betriebssystem läuft kannst du jetzt anfangen das Standardverhalten von Java herauszufinden unter allen Betriebssystemen, wenn die Umgebungsvariablen leer sind um dann die entsprechenden Fehlerbehandlungen zu implementieren..

Ansonsten sollte Windows (auf NTFS) Dateinamen als UTF-16 kodieren, kommt aber mit /\:*"?<>| und \0 nicht klar und unterscheidet nicht zwischen Groß-/Kleinschreibung (Linux/Unix aber wieder alles bis auf / und \ und unterscheidet Groß/Klein[3]).
Also soweit so simpel, bis fopen ins Spiel kommt welches Dateinamen auch mal als ANSI liest. Naja viel Spaß: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170


[1] https://en.wikipedia.org/wiki/Null_character
[2] Du und andere Programmierer/Admins werden so schnell aller aller beste Freunde!
[3] Noch eine Möglichkeit um Freundschaften zu schließen.
 
  • Gefällt mir
Reaktionen: andy_m4
Ehm, das war doch schon geklärt...

Dennoch etwas seltsam, dass Java keine Dateien mit Sonderzeichen umbenennen kann, wenn die Sprache vorher nicht konfiguriert wurde, obwohl mv damit keinerlei Probleme hat. Für mich fühlt sich das ein wenig wie ein Bug an.

Und dann noch was, die esoterischen Dateinamen kommen eigentlich nicht von mir, ich muss damit nur umgehen können.
 
CyborgBeta schrieb:
.bashrc:

Code:
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8

CyborgBeta schrieb:
Renaming: Martin Garrix, David Guetta, Ti��sto, Hardwell, Avicii, Calvin Harris (Dyglex Mix) [ApzwBvVWISM].mp3 ---> Martin Garrix, David Guetta, Tisto, Hardwell, Avicii, Calvin Harris (Dyglex Mix).mp3

Nur etwas seltsam, dass das im Allgemeinen notwendig ist.
Das ist keine Lösung, sonder ein Workaround. Du limitierst damit den Zeichensatz, so dass nicht alle Zeichen mehr unterstützt und in deinem Fall ersetzt werden. Das sieht du an den Zeichen ��.

In deinem oberen Beispiel hattest du Fragezeichen im Dateinamen drin. Die werden aber nicht von allen Systemen unterstützt, weshalb hier gerne zu Unicodezeichen gegriffen wird, die dann genau so aussehen wie ein normales Fragezeichen. Java scheint das nicht zu mögen, hat aber bestimmt Möglichkeiten das zu beheben.
 
Um das zu konkretisieren: Aus Ti��sto sollte Tiësto und nicht Tisto werden.
 
jb_alvarado schrieb:
Das ist keine Lösung, sonder ein Workaround.
Ne, Ziel der Programmlogik ist es gerade, bestimmte Sonderzeichen aus den Dateinamen (Plural) zu entfernen, bzw. nur bestimmte Zeichen zuzulassen ...

Ich kann das auch gerne einmal zeigen:

Java:
  private static void normalize(File dir) {
    call(dir, "find . -type f -exec bash -c 'mv -vn \"$1\" \"$(iconv -f UTF8 -t ASCII//TRANSLIT <<< $1)\"' -- {} \\;");
  }

  private static void rename(File dir) {
    File[] files = dir.listFiles();
    assert files != null;
    for (File file : files) {
      String newName = file.getName();
      newName = newName.replaceAll(" \\[[A-Za-z0-9_-]+]\\.", ".");
      newName = newName.replaceAll("[^0-9A-Za-z ()\\[\\],.-]", "");
      newName = newName.replaceAll("\\[", "(");
      newName = newName.replaceAll("]", ")");
      newName = newName.replaceAll(" {2,}", " ");
      newName = newName.replaceAll(" +,", ",");
      newName = newName.replaceAll("\\( +", "(");
      newName = newName.replaceAll(" +\\)", ")");
      if (!file.getName().equals(newName)) {
        try {
          System.out.println("Renaming: " + file.getName() + " ---> " + newName);
          System.out.println("Result: " + Files.move(file.toPath(), Path.of(file.getParent(), newName)));
        } catch (IOException e) {
          System.out.println("Error: " + e.getMessage());
          throw new RuntimeException(e);
        }
      }
    }
  }

Rename macht Folgendes:
  • schmeiße das Suffix weg (die id)
  • entferne ungültige Zeichen
  • ersetze [ und ] durch ( und )
  • trimme doppelte Leerzeichen
  • trimme Leerzeichen vor ,
  • trimme Leerzeichen nach (
  • trimme Leerzeichen vor )
  • benenne die Datei um
 
Fürs Umbenennen würde ich ja schlicht sowas wie Musicbrainz Picard verwendet. Das kennt für die Kompatabilität auch so Dinge wie "ASCII only" (für dumme Mediaplayer), und "Windowskompatibilität" (für Windows). MusicBrainz hätte auch den Vorteil, dass du ID3 Tags abrufen kannst und darauf dann korrekte Dateinamen inkl. dem e mit Pünkten herstellen kannst fürs "Tiësto".

@marcOcram @jb_alvarado
Wenn ich den TE richtig verstanden habe, ist der Ausgang ja bereits, dass irgendeine Software vorher die Platzhalter so gespeichert hat und eben nicht den UTF-8 Modifier + Buchstabe. Das ist halbwegs aussichtslos daraus wieder korrekte Informationen herzustellen. Denn Linux und alle gängigen grafischen Überflächen wie auch Java hätten gar kein Problem damit das zu encodieren.
 
  • Gefällt mir
Reaktionen: CyborgBeta
Zurück
Oben