Programmieren mit C++

Visual C++

Systemnahe Lösungen mit VC++

Relative Dateinamen bestimmen

Wie kann man zu einem vollständigen Dateinamen den relativen Namen in bezug auf ein Basisverzeichnis ermitteln?

Frage

Unter einem relativen Dateinamen soll nachfolgend der Name verstanden werden, der von einem gegebenen Basisverzeichnis zur Datei führt, deren Name mit vollständigem Pfad vorliegt. Im einfachsten Fall gestaltet sich dies wie folgt:

Basisverzeichnis: c:\foo\bar
Dateiname: c:\foo\bar\myfile.txt
Relativer Name: myfile.txt

Liegt die Datei außerhalb des Basisverzeichnisses, werden für das darüberliegende Verzeichnis zwei Punkte vorangestellt:

Basisverzeichnis: c:\foo\bar
Dateiname: c:\foo\myfile.txt
Relativer Name: ..\myfile.txt

Haben die Verzeichnisse nichts miteinander gemeinsam, können es auch mehrere durch „..\“ gekennzeichnete Elternverzeichnisse werden.

Basisverzeichnis: c:\aaa\bbb\eee
Dateiname: c:\bbb\ccc\myfile.txt
Relativer Name: ..\..\..\bbb\ccc\myfile.txt

Weichen die Laufwerksbuchstaben voneinander ab, stimmt der relative Dateiname mit dem vollständigen Dateinamen überein.

Die Funktion GetRelativeFilename() übergibt im Parameter currentDirectory das Basisverzeichnis und in absoluteFilename den vollständigen Dateinamen. Der resultierende relative Dateiname wird als Ergebnis zurückgeliefert.

Lösung

Über Defines kann das Verhalten der Funktion angepaßt werden. Die maximale Länge eines Dateinames definiert:

#define MAX_FILENAME_LEN 512

Maximallänge des Dateinamens

Um Unix und Windows unter einen Hut zu bringen, definiert das Define

#define ABSOLUTE_NAME_START 3

den Anfang eines Dateinamens bzw. die Anzahl der Zeichen, die am Anfang eines absoluten Dateinamens ignoriert werden. Für DOS/Windows sind dies 3 Zeichen, da Laufwerke mit „#:\“ notiert werden, wohingegen Unix nur einen Backslash „\“ benötigt, so daß hier 1 einzusetzen ist.

Sowohl currentDirectory als auch absoluteFilename können voll qualifizierter Dateinamen sein und mit dem Laufwerksbuchstaben für DOS bzw. dem Backslash unter Unix beginnen. currentDirectory ist überdies unempfindlich gegen abschließende Backslashes, die das Ergebnis nicht beeinflussen.

Startposition des Dateinamens

Das Backslash-Zeichen wird im Define

#define SLASH '\\'

vorgegeben, wobei unter DOS „\\“ und unter UNIX „/“ zum Einsatz kommt. Die Grundlogik der Funktion GetRelativeFilename() besteht darin, die Anzahl der übereinstimmenden Verzeichnisse von Basisverzeichnis und absolutem Dateinamen zu ermitteln und für die gefundene Anzahl die entsprechende Anzahl von Verweisen auf das Elternverzeichnis „..\“ einzusetzen.

Dazu durchläuft die Funktion die beiden Strings, bis ein Ergebnis gefunden ist, maximal jedoch nur über die Länge des kürzeren der beiden Strings, deren Längen gleich zu Beginn der Funktion ermittelt und in cdLen bzw. afLen abgelegt werden.

char* GetRelativeFilename(char *currentDirectory, 
                          char *absoluteFilename)
{
 int afMarker = 0;   // Index in absoluten Dateinamen
 int rfMarker = 0;   // Index in relativen Dateinamen
 int cdLen = 0;      // Länge Basisverzeichnis
 int afLen = 0;      // Länger absoluter Dateiname
 int i = 0;
 int levels = 0;     // Unterschiedsebenen
 static char relativeFilename[MAX_FILENAME_LEN+1];
 
 cdLen = strlen(currentDirectory);
 afLen = strlen(absoluteFilename);

Nachdem die Längen bekannt sind, erfolgt eine Überprüfung, um sicherzustellen, daß die Strings nicht länger als erlaubt und nicht kürzer als der kürzest mögliche Name sind.

 if (cdLen > MAX_FILENAME_LEN || cdLen < ABSOLUTE_NAME_START+1 || 
     afLen > MAX_FILENAME_LEN || afLen < ABSOLUTE_NAME_START+1)
   return NULL;

Ein Sonderfall liegt vor, wenn Basisverzeichnis und absoluter Dateiname unterschiedliche Laufwerke beinhalten. In diesem Fall entspricht der relative Dateiname dem absoluten.

 if(currentDirectory[0] != absoluteFilename[0])
 {
  strcpy(relativeFilename, absoluteFilename);
  return relativeFilename;
 }

Liegen Basisverzeichnis und absoluter Dateiname auf dem gleichen Laufwerk, ist herauszufinden, wieviel des Basisverzeichnisses im absoluten Dateinamen enthalten ist.

 i = ABSOLUTE_NAME_START;
 
 while(i < afLen &&
       i < cdLen &&
       currentDirectory[i] == absoluteFilename[i])
 {
   i++;
 }

Ist das gesamte Basisverzeichnis im absoluten Dateinamen enthalten, muß nur der verbleibende Rest abgeschnitten werden, um den relativen Dateinamen zu erhalten. Es ist sicherzustellen, daß der relative Dateiname keinen abschließenden Backslash besitzt, da dies nur für Verzeichnisse erlaubt ist.

 if(i == cdLen && (absoluteFilename[i] == SLASH || 
                   absoluteFilename[i-1] == SLASH))
 {
  if (absoluteFilename[i] == SLASH)
    i++;
  
  strcpy(relativeFilename, &absoluteFilename[i]);
  return relativeFilename;
 }

Liegt die Datei nicht in einem Unterverzeichnis des Basisverzeichnisses, muß ausgehend vom vollständigen Basisverzeichnis in Richtung Wurzel zurückgeschritten werden, indem eine entsprechende Anzahl von „..\“ eingefügt wird. Dazu gilt es zunächst herauszufinden, um wie viele Ebenen der Dateiname tiefer liegt als das tiefste gemeinsame Verzeichnis.

Backslash

 afMarker = i;
 levels   = 1;

 while(i < cdLen)
 {
  i++;

  if(currentDirectory[i] == SLASH)
  {
   i++;

   if (currentDirectory[i] != '\0')
     levels++;
  }
 }

Der Zeiger in den absoluten Dateinamen muß jetzt noch auf den Start des Verzeichnisses korrigiert werden, in dem die Levelsuche stoppte.

Levelsuche

 while(afMarker > 0 && 
       absoluteFilename[afMarker-1] != SLASH)
 {
  afMarker--;
 }

Der resultierende String darf mit den eingefügten „..\“ nicht zu lang werden.

 if(levels * 3 + afLen - afMarker > MAX_FILENAME_LEN)
   return NULL;

Jetzt kann die erforderliche Anzahl „..\“ Verweise eingefügt und am Ende ein Backslash angehängt werden.

 rfMarker = 0;
 for(i = 0; i < levels; i++)
 {
  relativeFilename[rfMarker++] = '.';
  relativeFilename[rfMarker++] = '.';
  relativeFilename[rfMarker++] = SLASH;
 }

Zu guter letzt wird der Rest des absoluten Dateinamens an den Ergebnisstring hinzukopiert und dieser als Funktionsergebnis zurückgeliefert.

 strcpy(&relativeFilename[rfMarker], 
        &absoluteFilename[afMarker]);
 
 return relativeFilename;
}

Verzeichnisanfang suchen





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 03:03:41 von textarchiv.alojado.de