Listar diretório atual em C Puro [RESOLVIDO]

1. Listar diretório atual em C Puro [RESOLVIDO]

Steve
Steve

(usa Slackware)

Enviado em 10/11/2018 - 11:18h

Bom dia a todos, estou com algumas dúvidas então se alguém puder ajudar, eu agradeço!
OBS: Não sou aluno e isso não é um projeto de faculdade. Trata-se de um programa simples que quero montar para mim, e para meus testes de aprendizado da linguagem C.

OBJETIVO: Eu quero jogar pra uma variável a lista de pastas que contém no diretório do meu programa que estou executando!
AINDA MAIS EXATO: No diretório atual onde está meu programa contém o seguinte:
.
..
program.c
program.out
pasta1
pasta2
pasta3

O que desejo é pegar apenas as 3 pastas ignorando qualquer arquivo neste diretório. LEMBRANDO os nomes acima são exemplos, então não me serve filtros com nome pasta por exemplo, porque se a pasta tiver o nome JOÃO não funcionaria né? Como informei os nomes podem ser outros.

DÚVIDAS E PREOCUPAÇÕES:
Código que tentei:

#define _DEFAULT_SOURCE //Entendi que nesse código essa linha é necessária pq alphasort está Obsoleto... Que chato né?
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h> //free que usa ela

void main() {
struct dirent **namelist; //Porque 2 ** ?
int n = scandir(".", &namelist, NULL, alphasort); //alphasort é uma função, assim como scandir

while (n--) {
printf("%s\n", namelist[n]->d_name);
free(namelist[n]);
}
free(namelist); //Queria entender como funciona o free
}

O Código acima, peguei como exemplo do scandir. Porém existem coisas nesse código que não sei o que são. Esse código ele lista na tela o diretório atual. Mas fico preocupado com o seguinte.
1) Entendi que a função scandir ou alphasort não se deve mais usar. Então qual eu devo usar HOJE?
2) Não compreendi o porque de 2 ** na frente de namelist, ainda estou tentando entender ponteiros, muito confuso pra mim...
3) Eu queria saber o que é esse free e porque ele precisaria ser usado?

4) Claro que neste código está faltando implementar um filtro para não listar arquivos e nem OCULTOS... Mas entendi que preciso colocar isso no while para filtrar como eu preciso. Pensei em usar um IF para isso...
5) Ainda não sei como ao invés de um PRINT eu enviaria para uma variável essa lista, e pensei em uma variável do tipo assim: Diretorios = pasta1, pasta2, pasta3, pasta4.....

Alguém pode me ajudar?


  


2. MELHOR RESPOSTA

???
gokernel

(usa Linux Mint)

Enviado em 11/11/2018 - 06:53h

Como acordei com bom humor e com vontade de ajudar o próximo vai o código abaixo:


OBS: não li todo o texto ...


Como a dúvida era simples, peguei um código antigo meu e "zap" ;)


//-------------------------------------------------------------------
//
// Lista todas as pastas inclusive a pasta( . e .. )
//
// ARQUIVO:
// list_dir.c
//
// COMPILE:
// gcc list_dir.c -o list_dir -Wall
//
//-------------------------------------------------------------------
//
#ifdef WIN32
#include "io.h"
#else
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#endif

#include <stdio.h>
#include <string.h>

void list_dir (void ) {

#ifdef WIN32
int done;
struct _finddata_t find;

//-------------------------------------------
// Find ONLY DIRECTORY
//-------------------------------------------
done = _findfirst("*.*", &find);
do {

// if (strcmp(find.name, "."))
if (find.attrib & _A_SUBDIR) {
printf ("%s\n", find.name);
}

} while ( !_findnext(done, &find) );
_findclose(done);

#endif // WIN32

#ifdef __linux__
DIR *dir;
struct dirent *entry;
struct stat s;

//-------------------------------------------
// Find ONLY DIRECTORY
//-------------------------------------------
dir = opendir("./");
for (;;) {
entry = readdir(dir);
if (!entry) break;

// if (strcmp(entry->d_name, "."))
if ( stat(entry->d_name, &s) == 0 && S_ISDIR(s.st_mode) ) {
printf ("%s\n", entry->d_name);
}
}
closedir(dir);

#endif // __linux__

}// list_dir ()


int main (void) {
list_dir ();
return 0;
}


OBS: só testei no Windows ... mas acho que funciona no Linux !


3. Re: Listar diretório atual em C Puro [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 11/11/2018 - 16:45h

Prezado Steve,

Começando pelo título, a rigor não faz sentido falar de “C puro” para tratar diretórios, pois o padrão do C não especifica funções voltadas para essa finalidade. O que você usou no seu programa é uma função do padrão POSIX, que usa o padrão do C e de sua biblioteca padrão, mas o estende significativamente com seu próprio conjunto de funções, variáveis e tipos de dados.

Steve escreveu:

1) Entendi que a função scandir ou alphasort não se deve mais usar. Então qual eu devo usar HOJE?


Ambas são parte do padrão POSIX, então ambas devem ser suportadas.

O problema é que, dependendo de como o seu compilador seja invocado, ele pode ligar ou desligar o suporte a algumas funções que sejam definidas por padrões que podem diminuir a compatibilidade do código.

Em várias páginas da documentação on-line (manpages) do Linux sobre funções da biblioteca, existe uma seção que indica quais feature_test_macros (ou seja: macros que o preprocessador testa a fim de habilitar determinados recursos) são necessárias para que as declarações dessas funções sejam efetivamente enxergadas pelo programa. Existe até uma manpage que descreve todas as feature_test_macros suportadas pela GNU libc.

Algumas dessas macros são afetadas por opções do compilador e por testes e definições/redefinições/indefinições presentes no cabeçalho <features.h>. Seria interessante saber como você fez para compilar seu programa.

2) Não compreendi o porque de 2 ** na frente de namelist, ainda estou tentando entender ponteiros, muito confuso pra mim...


Entender bem ponteiros é fundamental para se poder programar efetivamente em C.

Explicar ponteiros aqui com todo o cuidado necessário seria muito complicado. Resumindo de modo quase absurdo, um ponteiro é um dado que representa um endereço de memória.

No caso da linguagem C (e também de C++), ponteiros possuem tipos de dados próprios: é possível saber que uma variável é um ponteiro desde o momento de sua declaração, e existem operações que só podem ser efetuadas com ponteiros ou só produzem resultados que sejam ponteiros. Além disso, cada ponteiro carrega, além do endereço que designa, informação sobre o tipo de dado que pode estar contido no endereço designado. Um ponteiro válido para dados inteiros implica um endereço de um valor inteiro; um ponteiro válido para dados do tipo double implica um endereço de um valor do tipo double; um ponteiro válido para dados do tipo struct tm implica um endereço de um valor do tipo struct tm; e assim por diante.

No meio desse “assim por diante” existem os casos de se poder ter ponteiros para dados cujos tipos são, eles próprios, também ponteiros. Você pode, portanto, ter um ponteiro para dado do tipo ponteiro para dado do tipo inteiro (“int **”), ou ponteiro para dado do tipo ponteiro para dado do tipo ponteiro para dado do tipo char (“char ***”).

O uso frequente de ponteiros em C se deve principalmente a duas situações: alocação dinâmica de memória e passagem de argumentos por referência.

Alocação dinâmica é relativamente simples: durante a execução do programa (e não durante a compilação), pede-se que o sistema reserve espaço na memória para uma determinada quantidade de dados contíguos, e o sistema entrega o endereço da primeira posição dessa memória (o qual, sendo endereço, requer uma variável de tipo ponteiro para ser guardado). Quando essa área de dados não for mais necessária, ela pode ser liberada e devolvida ao sistema (é justamente o propósito de free(), sobre o qual você pergunta abaixo).

Já a passagem de parâmetros para funções em C tem a característica de ser sempre feita por meio de cópias dos valores dos argumentos. Assim, por exemplo, quando se chama uma função como “div(a, b)”, a função div() recebe uma cópia do valor da variável a e uma cópia do valor da variável b. Isso tem algumas implicações interessantes, entre as quais se destacam as seguintes: se o dado do argumento for muito grande (por exemplo, uma estrutura complexa ou contendo vetores com algumas dezenas ou mesmo centenas de bytes), a cópia do seu valor como argumento da função pode ser uma operação dispendiosa; além disso, se a função vier a alterar internamente um dos valores recebidos dos argumentos copiados, os valores originais fora da função serão preservados.

O emprego de ponteiros pode ajudar a contornar o impacto dessa característica de cópia de valores. Quando se passa à função um endereço, por meio de um ponteiro, a quantidade de bytes a ser copiada para ser usada como parâmetro é de apenas o tamanho de um ponteiro (tipicamente 4 ou 8 bytes), independentemente do tamanho ocupado pelo dado apontado. Além disso, de posse do endereço de um objeto, a função pode usar esse endereço para chegar ao dado original, seja para consultá-lo, seja até mesmo para poder alterá-lo.

Para obter o endereço de um dado, tal dado tem de estar armazenado em uma variável (i.e. não pode ser um valor temporário), de modo a se poder obter o endereço de onde a variável está armazenada por meio do operador unário &.

No caso de ponteiros para dados que podem ser alterados, uma das formas de alterá-los, usada principalmente com argumentos que são ponteiros para ponteiros, é justamente alocar memória nova e atribuir o endereço alocado ao ponteiro para ponteiro.

A função scandir() recebe como segundo parâmetro um valor do tipo struct dirent ***, ou seja: um “ponteiro para dado do tipo ponteiro para dado do tipo ponteiro para dado do tipo struct dirent”. E por que isso? Porque a ideia da função é que ela aloque dinamicamente (ponteiro!) um vetor de elementos do tipo struct dirent que também são individualmente alocados dinamicamente (ponteiros!), e que ainda possa gravar esse dado no argumento recebido (ponteiro!).

Para que isso seja possível você tem de declarar uma variável compatível: ela tem de poder representar o vetor dinamicamente alocado (ponteiro) para elementos dinamicamente alocados (ponteiros) de dados do tipo struct dirent, resultando numa declaração do tipo “struct durent **”, e tem de passar o endereço, e não o valor, dessa variável para a função, a fim de que ela possa ser modificada dentro da função, que a razão pela qual você passa “&namelist” em vez de apenas “namelist”.

3) Eu queria saber o que é esse free e porque ele precisaria ser usado?


Para devolver ao sistema memória que tenha sido alocada dinamicamente.

Como não apenas o vetor, mas também cada um de seus elementos é alocado dinamicamente, quando você não precisar mais do resultado da função, você tem de liberar toda a memória alocada. Isso significa que você tem primeiro de liberar cada elemento, e depois liberar também o vetor. Nesse aspecto, o código que você mostrou está totalmente correto.

O problema de não liberar a memória é que, especialmente em programas que permanecem em execução durante muito tempo, toda memória que não for liberada vai se acumulando, potencialmente sem uso, e aos poucos exaurindo recursos do sistema, podendo chegar ao ponto de faltar recurso para alguma aplicação mais tarde.

(Em sistemas operacionais modernos, quando um programa termina, os recursos que ele alocou são tipicamente devolvidos automaticamente ao sistema e, por isso, para programas de execução rápida, nem sempre os programadores se preocupam com a devolução explícita de tais recursos. Isso, contudo, é uma faca de dois gumes: quem tem o hábito de não se preocupar com recursos em programas pequenos tem maior tendência de se esquecer de cuidar deles em programas de maior porte e de execução mais longa. Além do mais, nem sempre é verdade que os recursos alocados serão devolvidos ao sistema ao final da execução do programa — mesmo memória, em certos casos (como memória compartilhada) pode permanecer em uso até que seja explicitamente devolvida.)

4) Claro que neste código está faltando implementar um filtro para não listar arquivos e nem OCULTOS... Mas entendi que preciso colocar isso no while para filtrar como eu preciso. Pensei em usar um IF para isso...


Poderia, mas você poderia fazer através da própria scandir(), alterando o terceiro argumento dela para um ponteiro de função que selecione quais nomes você quer colocar na lista retornada.

O terceiro e quarto argumentos são ponteiros para funções que a própria scandir() vai chamar antes de lhe entregar o resultado da varredura do diretório. O terceiro argumento deve ser uma função que avalia se o nome deve ou não entrar na lista, e o quarto argumento é usado para reordenar a lista de entradas encontradas pela pesquisa antes de lha entregar.

Se o terceiro argumento for nulo, a função assume que todos os nós encontrados no diretório devem ser postos na lista. Contudo, você pode definir uma função que receba um ponteiro constante para dado do tipo struct dirent, e avalie, usando os campos dessa estrutura, se você quer incluir os dados na lista ou não.

Como você falou que tem interesse apenas nos diretórios, talvez possa usar algo como o que vai abaixo.
int select_dirs(const struct dirent *entry){
// Retorna verdadeiro se for diretório e não for nem “.” nem “..”.
return
entry->d_type==DT_DIR &&
strcmp(entry->d_name, ".")!=0 &&
strcmp(entry->d_name, "..")!=0
;
}

(Com a ressalve de que nem todo sistema possui ou preenche corretamente o valor do campo d_type, e mesmo sistemas que a entendem podem estar sujeitos a tipos de sistemas de arquivo que não dispõem da informação Pode ser, portanto, que seu filtro tenha de ser um pouco mais elaborado.)

5) Ainda não sei como ao invés de um PRINT eu enviaria para uma variável essa lista, e pensei em uma variável do tipo assim: Diretorios = pasta1, pasta2, pasta3, pasta4.....


A lista já estará pronta quando a função retornar. Se você se refere à operação de filtrar seus elementos, o melhor a fazer seria mesmo implementar um filtro e passá-lo à própria scandir(), para que a lista devolvida já saia na forma que você desejar. Caso contrário, você acabará tendo de duplicar parte do esforço já feito e da memória já usada.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts