13

については既に読みましrealpath()たが、シンボリックリンクを解決したり、ファイルが実際に存在するかどうかを確認したりせずに、次の結果が得られるベースディレクトリとファイル名を渡すことができる関数はありますか? または、変更されたを使用する必要がありrealpath()ますか?

"/var/", "../etc///././/passwd" => "/etc/passwd"
4

3 に答える 3

10

これが normalize_path() 関数です。

指定されたパスが相対パスの場合、関数は現在の作業ディレクトリを先頭に追加して開始します。

次に、 のような特別なパス コンポーネント...または空のコンポーネントが処理され、結果が返されます。

の場合..、最後のコンポーネントがあれば削除されます (/..単に を返し/ます)。または空のコンポーネント ( double ) の
場合、これは単にスキップされます。./

この関数は、空のパスを返さないことを保証します (/代わりに が返されます)。

#define _GNU_SOURCE /* memrchr() */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

char * normalize_path(const char * src, size_t src_len) {

        char * res;
        size_t res_len;

        const char * ptr = src;
        const char * end = &src[src_len];
        const char * next;

        if (src_len == 0 || src[0] != '/') {

                // relative path

                char pwd[PATH_MAX];
                size_t pwd_len;

                if (getcwd(pwd, sizeof(pwd)) == NULL) {
                        return NULL;
                }

                pwd_len = strlen(pwd);
                res = malloc(pwd_len + 1 + src_len + 1);
                memcpy(res, pwd, pwd_len);
                res_len = pwd_len;
        } else {
                res = malloc((src_len > 0 ? src_len : 1) + 1);
                res_len = 0;
        }

        for (ptr = src; ptr < end; ptr=next+1) {
                size_t len;
                next = memchr(ptr, '/', end-ptr);
                if (next == NULL) {
                        next = end;
                }
                len = next-ptr;
                switch(len) {
                case 2:
                        if (ptr[0] == '.' && ptr[1] == '.') {
                                const char * slash = memrchr(res, '/', res_len);
                                if (slash != NULL) {
                                        res_len = slash - res;
                                }
                                continue;
                        }
                        break;
                case 1:
                        if (ptr[0] == '.') {
                                continue;

                        }
                        break;
                case 0:
                        continue;
                }
                res[res_len++] = '/';
                memcpy(&res[res_len], ptr, len);
                res_len += len;
        }

        if (res_len == 0) {
                res[res_len++] = '/';
        }
        res[res_len] = '\0';
        return res;
}
于 2011-01-23T15:34:32.477 に答える
3
function normalize_path($path, $pwd = '/') {
        if (!isset($path[0]) || $path[0] !== '/') {
                $result = explode('/', getcwd());
        } else {
                $result = array('');
        }
        $parts = explode('/', $path);
        foreach($parts as $part) {
            if ($part === '' || $part == '.') {
                    continue;
            } if ($part == '..') {
                    array_pop($result);
            } else {
                    $result[] = $part;
            }
        }
        return implode('/', $result);
}

(私がこれを書いた時点で、質問にはPHPのタグが付けられていました。)

とにかく、ここに正規表現バージョンがあります:

function normalize_path($path, $pwd = '/') {
        if (!isset($path[0]) || $path[0] !== '/') {
                $path = "$pwd/$path";
        }
        return preg_replace('~
                ^(?P>sdotdot)?(?:(?P>sdot)*/\.\.)*
                |(?<sdotdot>(?:(?P>sdot)*/(?!\.\.)(?:[^/]+)(?P>sdotdot)?(?P>sdot)*/\.\.)+)
                |(?<sdot>/\.?(?=/|$))+
        ~sx', '', $path);
}
于 2011-01-23T13:49:35.060 に答える
1

Hardexソリューションを使用します。

#include <string.h>

char * normalizePath(char* pwd, const char * src, char* res) {
    size_t res_len;
    size_t src_len = strlen(src);

    const char * ptr = src;
    const char * end = &src[src_len];
    const char * next;

    if (src_len == 0 || src[0] != '/') {
        // relative path
        size_t pwd_len;

        pwd_len = strlen(pwd);
        memcpy(res, pwd, pwd_len);
        res_len = pwd_len;
    } else {
        res_len = 0;
    }

    for (ptr = src; ptr < end; ptr=next+1) {
        size_t len;
        next = (char*)memchr(ptr, '/', end-ptr);
        if (next == NULL) {
            next = end;
        }
        len = next-ptr;
        switch(len) {
        case 2:
            if (ptr[0] == '.' && ptr[1] == '.') {
                const char * slash = (char*)memrchr(res, '/', res_len);
                if (slash != NULL) {
                    res_len = slash - res;
                }
                continue;
            }
            break;
        case 1:
            if (ptr[0] == '.') {
                continue;
            }
            break;
        case 0:
            continue;
        }

        if (res_len != 1)
            res[res_len++] = '/';

        memcpy(&res[res_len], ptr, len);
        res_len += len;
    }

    if (res_len == 0) {
        res[res_len++] = '/';
    }
    res[res_len] = '\0';
    return res;
}

例:

#include <stdio.h>

int main(){
    char path[FILENAME_MAX+1];
    printf("\n%s\n",normalizePath((char*)"/usr/share/local/apps",(char*)"./../../../",path));
    return 0;
}

出力:

/usr


ノート:

  1. 最初の引数は、他のパスが正規化される相対ディレクトリ パス (絶対パス) です。通常、現在のディレクトリの絶対パスです。
  2. 2 番目の引数は、シンボリック リンクを解決せずに正規化する文字列です。
  3. 3 番目の引数はchar*、正規化されたパスを格納するために必要なメモリ/容量が必要な です。
于 2015-12-10T12:43:14.167 に答える