Как проверить, существует ли файл по заданному пути в C? - PullRequest
0 голосов
/ 19 сентября 2019

Я пытаюсь найти файл (скажем marks.txt) по определенному пути, переданному в качестве аргумента функции.Можно ли дать имя файла и путь в качестве аргументов функции, которая проверяет, существует ли файл, и выводит путь?

Следующая функция принимает в качестве аргумента только путь.

int fileexists(const char *path){                                 
    File *ptr = fopen(path, "r");                                             
    if (fptr == NULL)                                                                                                                                                                                                      
        return 0;
    fclose(fptr);
    return 1;
}        

Требуемый прототип функции:

int fileexists(const char *path, const char *filename)

Ответы [ 3 ]

2 голосов
/ 19 сентября 2019

Функция, которая у вас есть, проверяет, можно ли открыть файл, но не работает для некоторых файлов, которые существуют, но у вас нет прав на открытие.Я бы использовал stat вместо этого.Чтобы объединить путь и имя файла, вы можете использовать строковые функции.

Обычные API Unix C мрачны.Требуется много усилий, чтобы правильно выполнить простейшие вещи - и даже тогда я не уверен, что не забыл какой-то Unix-изм, такой как обработка сигналов или некоторые непонятные случаи ошибок.То есть вещи, которые довольно просты для понимания в современном C ++.

Я бы хотел, чтобы кто-то разработал современный API системы C и реализовал его как минимум для Linux, чтобы наши страдания закончились ...

Обычно для конкатенации строк требуется API более высокого уровня при сохранении некоторого здравого смысла.Таким образом, в приведенном ниже примере для построения строки используется класс strbuilder.Это делает вещи расплывчатыми и позволяет избежать наиболее распространенных ошибок.

#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

struct strbuilder {
    unsigned items, item;
    size_t length, *lengths;
    char *str, *dst;
};

bool strbuilder_pass(struct strbuilder *builder, int *rc);
void strcat_str(struct strbuilder *builder, const char *src);
void strcat_c_ifnone(struct strbuilder *builder, char c);
bool strbuilder_is_freed(const struct strbuilder *builder);

int fileExists(const char *path, const char *filename)
{
    const char pathSep = '/';
    int rc;
    struct strbuilder bld = {0};
    while (strbuilder_pass(&bld, &rc))
    {
        strcat_str(&bld, path);
        strcat_c_ifnone(&bld, pathSep);
        strcat_str(&bld, filename);
        if (!rc)
        {
            struct stat statbuf;
            printf("path = %s\n", bld.str);
            rc = stat(bld.str, &statbuf);
        }
    }
    assert(strbuilder_is_freed(&bld));
    return rc;
}

int main()
{
    int rc = fileExists("/", "dev");
    assert(rc == 0);
    return 0;
}

Создание строки контролируется функцией strbuilder_pass, которая продвигает состояние построителя строки через пять проходов операции:

  1. Определите количество элементов, ширина которых должна быть сохранена (избегает необходимости вызывать strlen дважды).
  2. Подготовьте вектор хранения длины.Определите необходимую длину буфера.
  3. Подготовьте буфер выходной строки.Объединить элементы в буфер.
  4. Использовать буфер выходной строки.
  5. Освободить буфер выходной строки.

Этот API не особенно особенный, но подходит для этоговариант использования.Некоторый другой специальный подход тоже подойдет, но это ИМХО немного более элегантно.

void strbuilder_free(struct strbuilder *builder)
{
    free(builder->lengths);
    free(builder->str);
    memset(builder, 0, sizeof(*builder));
}

bool strbuilder_pass(struct strbuilder *builder, int *rc)
{
    if (!builder->length) {// start of pass 1
        builder->length = 1; /*term*/
        *rc = EAGAIN;
        return true;
    }
    else if (!builder->lengths) // end of pass 1
    {
        builder->lengths = malloc(sizeof(*builder->lengths) * builder->items);
        if (builder->lengths)
            return true;
        *rc = ENOMEM;
    }
    else if (!builder->str) // end of pass 2
    {
        builder->dst = (builder->str = malloc(builder->length));
        builder->item = 0;
        builder->length = 0;
        if (builder->dst) {
            *builder->dst = '\0';
            return true;
        }
        *rc = ENOMEM;
    }
    else if (builder->dst) // end of pass 3
    {
        while (*builder->dst) { // include optional content
            builder->dst++; // skip
            builder->length++;
        }
        builder->dst = NULL;
        *rc = 0;
        return true;
    }
    else if (!builder->dst) // end of pass 4 (if any)
    {}
    else {
        *rc = EINVAL;
    }
    strbuilder_free(builder);
    return false;
}

void strcat_str(struct strbuilder *builder, const char *src)
{
    if (!src)
        return;
    if (!builder->lengths) // pass 1
        builder->items ++;
    else if (!builder->str) // pass 2
    {
        size_t len = strlen(src);
        builder->lengths[builder->item++] = len;
        builder->length += len;
    }
    else if (builder->dst) // pass 3
    {
        size_t len = builder->lengths[builder->item++];
        if (*builder->dst && (!len || *builder->dst != *src))
        {
            builder->dst++;
            builder->length++;
        }
        memcpy(builder->dst, src, len);
        builder->dst += len;
        builder->length += len;
        *builder->dst = '\0';
    }
}

void strcat_c_ifnone(struct strbuilder *builder, char c)
{
    if (!builder->lengths) {} // pass 1
    else if (!builder->str) // pass 2
    {
        if (c) builder->length ++;
    }
    else if (builder->dst) // pass 3
    {
        if (!builder->length || builder->dst[-1] != c)
            *(builder->dst) = c;
    }
}

bool strbuilder_is_freed(const struct strbuilder *builder)
{
    return !builder || (!builder->lengths && !builder->str);
}
2 голосов
/ 19 сентября 2019

Этот вопрос состоит из двух частей, и правильные ответы на них зависят от того, что вы пытаетесь сделать.

  1. Объедините имя каталога и имя файла, чтобы сформировать полное имя пути.
  2. Определить, существует ли файл (на который указывает полное имя пути).

Конкатенация имени каталога и имени файла проста.Ваши друзья strcpy и strcat сделают большую часть работы.Есть несколько мелких деталей, о которых следует помнить: (a) Вам понадобится достаточно большой буфер для полного пути, и вам нужно будет решить, использовать ли массив фиксированного размера (возможно, размера MAX_PATH)или malloc'ed буфер;(б) вам может понадобиться вставить явный символ '/' (и обычно вставлять его не помешает, даже если строка каталога уже оканчивается на один);(c) в Windows вы можете захотеть использовать '\\' вместо '/'.

И тогда определение того, существует ли файл с полным именем пути, уже получило хороший ответ на Какой лучший способпроверить, существует ли файл в C? .Большой вопрос, который нужно задать, заключается в том, спрашиваете ли вы, существует ли файл в процессе подготовки к работе с файлом?Если это так, у вас есть серьезная уязвимость, если вы проверяете наличие файла, но затем, прежде чем делать что-то другое, происходит что-то еще, что приводит к появлению или исчезновению файла.Поэтому вместо того, чтобы проверять и затем делать, обычно лучше просто попытаться сделать что-то другое и изящно справиться с любыми ошибками.

1 голос
/ 19 сентября 2019

Вы, вероятно, хотите что-то вроде этого (без проверки ошибок для краткости):

...
#include <string.h>   // for str* functions
#include <unistd.h>   // for access
#include <stdlib.h>   // for malloc
...
int fileexists(const char *path, const char *filename)
{
    char *name= malloc(strlen(path) + strlen(filename) + 1);
    strcpy(name, path);
    strcat(name, filename);
    int retval = access(name, F_OK) == 0;
    free(name);
    return retval;
}

Звоните так:

if (fileexists("/some/path/", "somefilename.txt")) ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...