"We are back" « oc.at

Charset Problem mit Perl Globbing auf Windows mit BSD_glob() *solved*

GrandAdmiralThrawn 09.10.2014 - 10:22 3763 17
Posts

GrandAdmiralThrawn

XP Nazi
Avatar
Registered: Aug 2000
Location: BRUCK!
Posts: 3727
Grüß euch,

Ich hätte eine Frage bezüglich der Verwendung von bsd_glob() auf Win32, weil ich da ein Problem mit Verzeichnisnamen habe. Ich kann nicht den kompletten Code posten, also muß es etwas unvollständiger "Pseudo"code tun. bsd_glob() wird statt dem Perl Builtin verwendet, damit Leerzeichen keine Pattern Delimiter darstellen können. Es wird eine Folderstruktur mit 3 Ebenen abgegrast. Ca. so:

Code: PERL
use strict;             # Strict code only, no dodgy stuff.
use File::Glob 'glob';  # Use BSD_glob() instead of builtin.

my(@tree) = glob("E:/Data/*");      # Read folders on level 2 into array ("E:/Data" is considered level 1, "E:" level 0).

foreach $branch (@tree) {           # Iterate through folder level 2.
  if (-d $branch) {                 # Check if branch folder is truly a directory.
    @leaves = glob("$branch/*");    # Read folder names on level 3 into array using BSD_glob.
    foreach $leaf (@leaves) {       # Iterate through folders on L3.
    if (-d $leaf) {                 # Check whether leaf folder is truly a directory.
      $mtime = stat($leaf)->mtime;  # If so, read modification time stamp.
      $mtimestamps{$leaf} = $mtime; # Add timestamp as a value to the hashtable for the current folder name as key.
      $leafcount++;                 # Increment total folder count.
      }
    }
  }
}
Sinn des ganzen ist die Visualisierung einer bestimmten Folderstruktur, sortiert nach Modification Timestamps. Der Code soll auch auf *nix lauffähig sein, und ich baue sowohl auf Linux als auch Windows, daher Perl.

Aber der Punkt ist, daß ich am Sonderzeichen ź hängenbleibe! glob() scheint Verzeichnisse mit diesem Zeichen im Namen einfach nicht zu expanden! Damit wird jeder betroffene Folder einfach übersprungen und fällt unter'n Tisch.

Etwas Recherche hat ergeben, daß ź kein Mitglied des ISO-8859-15 Zeichensatzes ist, den mein Windows NT5.2 aber nutzen sollte. Dennoch lassen sich Datei- und Verzeichnisnamen mit ź im Windows Explorer problemlos anzeigen und nutzen. Das scheint mir aber nur in UTF-8 zu existieren, nämlich als [U+017A]. Oder verwendet Windows in Wahrheit irgendein krankes Hybridencoding a la IRC für Dateinamen?!

Andere Zeichen, die in ISO-8859-15 existieren werden problemlos behandelt, so z.B. ø, ©, ç, ¾ und so weiter und so fort.

Was soll ich jetzt tun? Echt keine Ahnung wie ich das behandeln sollte. Das Script selbst ist übrigens als UTF-8 ohne BOM encoded, falls das eine Rolle spielen sollte.

Danke!
Bearbeitet von GrandAdmiralThrawn am 10.10.2014, 19:08

Ringding

Pilot
Avatar
Registered: Jan 2002
Location: Perchtoldsdorf/W..
Posts: 4300
Ich weiß nicht, wie Perl unter Windows das macht, aber grundsätzlich hat Windows für alle (traditionellen) API-Funktionen 2 Varianten: eine, die Strings in UTF-16 versteht, und eine mit 8bit-Strings – Windows-1252 typischerweise in unseren Breiten. Die Funktionsnamen haben jeweils ein W für die 16bit- und A für die 8bit-Variante. Sollte Perl die zweite Variante verwenden, kannst du vermutlich gar nichts machen. Vielleicht einen anderen Perl-Build verwenden?

Unter Unix gibt es dieses Problem nicht, weil Perl-Strings einfach Byte-Strings sind und das OS für alle Filenamen ebensolche liefert und versteht.

GrandAdmiralThrawn

XP Nazi
Avatar
Registered: Aug 2000
Location: BRUCK!
Posts: 3727
Bitte entschuldige meine Unwissenheit, aber "W"? "A"? Ich verstehe nicht.

Darf ich deiner Antwort entnehmen, daß Windows hier ein Hybrid Encoding (CP1252+UTF-16) nutzt, um Dateinamen zu kodieren?

Wo Perl ist, muß es doch einen Weg geben..

Ringding

Pilot
Avatar
Registered: Jan 2002
Location: Perchtoldsdorf/W..
Posts: 4300
Wenn eine API-Funktion z.B. FindFirstFile heißt, dann existiert dieser Symbolname gar nicht wirklich, sondern stattdessen gibt es FindFirstFileA und FindFirstFileW für die 8- bzw. 16bit-Version.

Und nein, Windows verwendet nie ein Hybrid-Encoding für Filenamen, sondern immer UTF-16. Nur über den Weg der API-Funktionen, die von Programmen aufgerufen werden, geschieht hier eine Verwurschtung, und das auch nur dann, wenn die A-Varianten verwendet werden – welche aus diesem Grund heutzutage einfach nicht mehr zeitgemäß sind.

Mit http://www.dependencywalker.com kann man sich anschauen, welche Symbole ein .exe oder eine .dll importiert.

GrandAdmiralThrawn

XP Nazi
Avatar
Registered: Aug 2000
Location: BRUCK!
Posts: 3727
Ich glaub ich geb's auf. Es gibt Module, die können z.B. UTF-16LE Dateinamen bzw. Metadaten durch Nutzung der richtigen API Calls einlesen (Win32API::File) und selbst das Core readdir() kanns, aber selbst so simple Core-Geschichten wie ein "Ist das Element ein Verzeichnis?" failen..

Also z.B. if (-d $leaf) geht auch schon nicht mehr...

Das is echt ein Saudreck..
Bearbeitet von GrandAdmiralThrawn am 10.10.2014, 10:53

murcielago

Dr. Doom
Avatar
Registered: Oct 2002
Location: *
Posts: 2697
Und wenn du nicht die Perl-Funktion für "sag mir ob das ein Verzeichnis ist" aufrufst, sondern unter Windows eine Weiche hast, welche direkt die Windows-API funktionen aufruft? Ich weiß nicht ob das mit Perl möglich ist, aber das wäre mein Ansatz bei so einem PRoblem

GrandAdmiralThrawn

XP Nazi
Avatar
Registered: Aug 2000
Location: BRUCK!
Posts: 3727
Ich verstehe nicht was du meinst. Wo soll die Weiche implementiert sein? So wie ich das verstehe, sitzt im Perl Core (oder im Perl Modul, wenn man ein solches verwendet) der API Call drin. Man müßte schon das Modul oder den Core umschreiben und rekompilieren fürchte ich?!

Ich glaub kaum, daß ich allen Perl Modulen und dem Core einfach aufzwingen kann, global nur mehr Wide Calls aufs NTFS abzusetzen..
Bearbeitet von GrandAdmiralThrawn am 10.10.2014, 10:59

murcielago

Dr. Doom
Avatar
Registered: Oct 2002
Location: *
Posts: 2697
Sagen wir so: in jeder PRogrammiersprache die ich bis jetzt verwendet habe, war es möglich nicht den eigenen Wrapper (was dieser Perl-API-Call ja ist) zu verwenden, sondern einen System-Call abzusetzen auf das darunterliegende OS.

PseudoCode: "if (WIN32APICall(checkmirdasverzeichnis) == true)"...

Verstehst du worauf ich hinauswill?

GrandAdmiralThrawn

XP Nazi
Avatar
Registered: Aug 2000
Location: BRUCK!
Posts: 3727
Selbst den API Call absetzen. Ok. Verstehe. Mal schaun ob das geht, und wie.

murcielago

Dr. Doom
Avatar
Registered: Oct 2002
Location: *
Posts: 2697

GrandAdmiralThrawn

XP Nazi
Avatar
Registered: Aug 2000
Location: BRUCK!
Posts: 3727
Hmm, eher [hier], so scheint es. Aber auf einen grünen Zweig mußt da auch erst Mal kommen. Neben Dirchecking brauchts ja auch noch das Auslesen von Timestamps. Die Ordner listen und in einen Array reinholen geht z.B. schon Mal mit readdir(). Habs im :raw Modus gelesen, damit da nur ja keine automatischen Konversionen passieren.

Dennoch scheint das was ich bekomme irgendwie kein UTF-16le zu sein, sondern immer was anderes, wirkt eher wie UCS-2?!

Scheinbar ist das En-/Decoding von Dateinamen generell eine Schwachstelle in Perl (meinen zumindest einige Leute im Netz).

Langsam treibt mich das jedenfalls in den Wahnsinn! :rolleyes:

Edit: Ok, es scheint, als würde readdir() besagte Folder zwar einlesen, aber als CP-1252 statt UCS-2le. Geh bitte...
Bearbeitet von GrandAdmiralThrawn am 10.10.2014, 13:16

murcielago

Dr. Doom
Avatar
Registered: Oct 2002
Location: *
Posts: 2697
Ja, das ist leider oft ein Graus... ich hab mal Plugins programmiert, welche auf OSX und Win für Adobe Indesign laufen sollten... da drehst durch :D

GrandAdmiralThrawn

XP Nazi
Avatar
Registered: Aug 2000
Location: BRUCK!
Posts: 3727
Ok, hier kommt die Saugeilheit! Und du hast mich zumindest teilweise drauf gebracht!

In Perl 5.8.1 wurde ein Switch disabled ("-C"), mit dem man Perl erklären kann, bitte nur WIDE API Calls aufs Filesystem abzusetzen. Angeblich wurde er entfernt, weil das eh keiner benutzt haben soll (he, super Grund!!).

Also hab ich echt Mal ein steinaltes Perl 5.6 installiert, das Skript mit Parameter -C für Perl gestartet und siehe da, es funktioniert..

Wie DUMM ist das bitte?!

Edit: Die API Calls selbst aus der kernel32.dll aufzurufen [geht übrigens auch], aber das will glaube ich wirklich keiner, wenn ich mir das so anschau.

Nico

former person of interest
Registered: Sep 2006
Location: -
Posts: 4082
kannst ja den verantwortlichen "reporten", vielleicht kommt das "feature" dann zurück.

murcielago

Dr. Doom
Avatar
Registered: Oct 2002
Location: *
Posts: 2697
Zitat von GrandAdmiralThrawn
Edit: Die API Calls selbst aus der kernel32.dll aufzurufen [geht übrigens auch], aber das will glaube ich wirklich keiner, wenn ich mir das so anschau.

Aber vielleicht gibts dafür wieder selbst gecodete Wrapper irgendwo in den Weiten des Netz :D
Kontakt | Unser Forum | Über overclockers.at | Impressum | Datenschutz