[RISOLTO][c] Estrarre sottostringa da stringa

di il
21 risposte

[RISOLTO][c] Estrarre sottostringa da stringa

Salve, mi sono accorto che sia argv[0] sia __FILE__ contengono il persorso del file, non solo il nome.
a me servirebbe solo il nome senza estensione, quindi ho creato questa funzione.

La posto dentro un main anche se la userò come funzione a parte, cosi se volete potete provarla:

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

int main( int argc , char *argv[] )
{
    int h = 0 , l = 0;
    char * p ;
    char * w ;

    /* mi creo una variabile d'appoggio cossichè posso modificare la stringa, __FILE__ o argv[0] non sono modificabili */
    char *aa = NULL;
    aa = (char*)calloc( strlen( argv[0] ) , sizeof( char ) );
    strcpy( aa , argv[0] )    ;

    puts( arg );

    /* METTERO' CONTROLLO SU calloc E strcpy */

    p = strrchr( aa , '\\' );   /* Occorenza ultimo '\' */
    w = strrchr( aa , '.' );    /* Occorenza ultimo '.' */
    /* METTERO' CONTROLLO SULLE strrchr  */

    p++; /* passo avanti perchè non mi interessa il blackslash ma il carattere dopo */

    l = strlen( p );
    h = strlen( w );
    /* METTERO' CONTROLLO SULLE strlen  */

    p[l - h] = '\0';  /* "riduco" la stringa del valore della dimensione di punto ed estensione */

    printf( "\n\n\n%s---  %d\n" , p , h );
    return 0;      /* creando una funzione passo p che punta alla stringa che mi serve */
}
Per ora la funzione non è completa (mancano dei controlli e rinominerò le variabili con nomi consoni) ma e' funzionante,
mi chiedevo se l'algoritmo che ho usato è buono o avete dei consigli per migliorarlo.

Grazie per tutti i consigli.

21 Risposte

  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Due consigli:
    1) pensa in maniera modulare: scrivi una funzione che riceve in input una stringa con il nome di file con path, un puntatore per la stringa da caricare con il nome di file senza path e un intero che indica la dimensione allocata per tale stringa (per evitare buffer overflow)
    2) non complicarti la vita con calloc e malloc: definisci una stringa locale di 80 caratteri e la carichi usando la funzione scritta al punto 1
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    candaluar ha scritto:


    Due consigli:
    1) pensa in maniera modulare: scrivi una funzione che riceve in input una stringa con il nome di file con path, un puntatore per la stringa da caricare con il nome di file senza path e un intero che indica la dimensione allocata per tale stringa (per evitare buffer overflow)
    2) non complicarti la vita con calloc e malloc: definisci una stringa locale di 80 caratteri e la carichi usando la funzione scritta al punto 1
    Grazie dei suggerimenti.

    1)
    Si, la mia intenzione finale e' di fare una funzione a parte che riceva la stringa con tutto il pepercorso. In sostanza sarà una funzione di una libreria personale che deve dirmi che programma si sta eseguendo, per una questione puramente di gusti preferisco avere il nome senza path (e nel frattenpo mi esercito con la prorammazione in c)
    Ricevere in input anche la stringa da caricare mi sta bene. Come mai non restituire il puntatore a fine funzione, ma passarlo come input? che differenze ci sono?
    La dimensione della stringa , non so quanto possa essere grande la stringa senza il path , non credo più di 30 caratteri massimo (essendo il nome di un programma, non credo che si superino i 20 caratteri nella maggior parte dei casi)

    2) Vedi ultimo punto sopra.

    Volevo apportare delle modifiche:
    in windows tutti i programmi finiscono con .exe , quindi saranno sempre 4 caratteri. Logicamente quindi, è un errore anticipare semplicemente il carattere '\0' di 4 caratteri invece di crearmi il puntatore all'ultima occorenza di '.' ?
    P[l-4] invece di p[l - h]
    semplificherei il codice.
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Si, la mia intenzione finale e' di fare una funzione a parte che riceva la stringa con tutto il pepercorso. In sostanza sarà una funzione di una libreria personale che deve dirmi che programma si sta eseguendo, per una questione puramente di gusti preferisco avere il nome senza path (e nel frattenpo mi esercito con la prorammazione in c)
    Intanto scrivi una funzione *generica* che dato un nome di file con o senza path ti carica una stringa con il nome di file senza path; questa funzione verrà poi richiamata dalla funzione che vuoi scrivere ma potrà anche essere utilizzata per altre esigenze.
    Ricevere in input anche la stringa da caricare mi sta bene. Come mai non restituire il puntatore a fine funzione, ma passarlo come input? che differenze ci sono?
    Se vuoi restituire un puntatore devi allocare dentro la funzione e quindi poi il chiamante deve ricordarsi/sapere che poi tale puntatore dovrà essere deallocato (free). Se invece ti fai passare il puntatore da caricare:
    a) semplifichi la vita a te che implementi la funzione perchè non ti devi preoccupare di allocare
    b) permetti al chiamante di passarti quello che vuole, memoria allocata oppure anche semplicemente una stringa dallo stack.
    La dimensione della stringa , non so quanto possa essere grande la stringa senza il path , non credo più di 30 caratteri massimo (essendo il nome di un programma, non credo che si superino i 20 caratteri nella maggior parte dei casi)
    Datti un valore massimo ragionevole, es.40 perchè difficilmente avrai nomi di file più lunghi. Se invece vuoi essere preciso, il chiamante può allocare la dimensione della stringa di input + 1 per il terminatore.
    in windows tutti i programmi finiscono con .exe , quindi saranno sempre 4 caratteri. Logicamente quindi, è un errore anticipare semplicemente il carattere '\0' di 4 caratteri invece di crearmi il puntatore all'ultima occorenza di '.' ?
    P[l-4] invece di p[l - h]
    semplificherei il codice.
    Sulla falsa riga della funzione che estrae il nome di file, scrivi una funzione che estrae il nome senza path. Ugualmente, per esercizio, puoi scrivere una funzione che ritorna l'estensione.
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Ecco il programma sistemato
    
    #include <stdio.h>
    #include <string.h>
    
    int estraiStringa( char *pathProg , char *nomeProg )
    {
        /* ======================  DEFINIZIONE VARIABILI ======================= */
        char *ptrStart = NULL ;               /* Puntatore su carattere start    */
        char *ptrClose = NULL ;               /* Puntatore su carattere start    */
        char start = '\\';                    /* Precarattere iniziale nome      */
        char close = '.';                     /* Postcarattere finale nome       */
        size_t dI = 0;                        /* Dimensione stringa da '\\'      */
        size_t dF = 0;                        /* Dimensione stringa da '.'       */
        /* ===================================================================== */
    
        /* Controllo path  */
        if( NULL == pathProg ) {
            fprintf( stderr , "%s" , "Input non valido!\n" );
            return 0;
        }
    
        /* Occorenza ultimo '\' */
        ptrStart = strrchr( pathProg , start );
        if( NULL == ptrStart ) {
            fprintf( stderr , "Carattere \" %c \" non trovato\n" , start );
            return 0;
        }
        ptrStart++; /* passo avanti perchè non mi interessa partire dal blackslash ma dal carattere dopo */
    
        /* Occorenza ultimo '.' per l'estensione */
        ptrClose = strrchr( pathProg , close );
        if( NULL == ptrStart ) {
            fprintf( stderr , "Carattere \" %c \" non trovato\n" , close );
            return 0;
        }
    
        /* Dimensione stringa con estensione */
        dI = strlen( ptrStart );
        if( !dI ) {
            fprintf( stderr , "Dimensione stringa con estensione non corretta \n" );
            return 0;
        }
    
        /* Dimensione estensione */
        dF = strlen( ptrClose );
        if( !dF ) {
            fprintf( stderr , "Dimensione estensione stringa non corretta \n" );
            return 0;
        }
    
        /* Copia in nomeProg la parte di path dal puntatore ptrStart */
        strcpy( nomeProg , ptrStart );
        printf( "\n\n\nNome Programma con estensione: %s\n" , nomeProg );
    
        /* "riduco" la stringa del valore della dimensione di punto ed estensione */
        nomeProg[dI - dF] = '\0';
        printf( "Nome Programma senza estensione: %s\n" , nomeProg );
    
        return strlen( nomeProg );
    }
    
    int main( int argc , char *argv[] )
    {
        char stringa[40] = "" ;
        int dim = 0;
    
        dim = estraiStringa( argv[0] , stringa );
        puts( "" );
        printf( "Nome Programma = %s\n" , stringa );
        printf( "dimensione nome Programma = %d\n" , dim );
    
        return 0;
    }
    
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Alla EstraiStringa, che chiamerei EstraiNomefile, dovresti passare anche la dimensione massima della stringa.
    Al posto di strcpy quindi usi strncpy.
    Non stampare messaggi dalla funzione ma fai in modo che se ad esempio non trovi lo slash copi la stringa così com'è
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Ecco le modifiche
    Ho tenuto i printf come sorta di debug per il momento, appena ho la versione finale li leverò
    per le fprintf(stderr) credo che alla fine le devierò in un file.log

    
    #include <stdio.h>
    #include <string.h>
    
    int estraiNomeFile( char *pathProg , char *nomeProg , size_t maxDim )
    {
        /* ======================  DEFINIZIONE VARIABILI ======================= */
        char *ptrStart = NULL ;               /* Puntatore su carattere start    */
        char *ptrClose = NULL ;               /* Puntatore su carattere start    */
        char start = '\\';                    /* Precarattere iniziale nome      */
        char close = '.';                     /* Postcarattere finale nome       */
        size_t dI = 0;                        /* Dimensione stringa da '\\'      */
        size_t dF = 0;                        /* Dimensione stringa da '.'       */
        /* ===================================================================== */
    
        /* Controllo Input  */
        if( NULL == pathProg ) {
            fprintf( stderr , "%s" , "Input path non valido!\n" );
            return 0;
        }
        if( 1 > maxDim ) {
            fprintf( stderr , "%s" , "Input path non valido!\n" );
            return 0;
        }
    
        printf( "Path Programma = %s\n" , pathProg );
    
        /* Occorenza ultimo '\' */
        ptrStart = strrchr( pathProg , start );
        if( NULL == ptrStart ) {
            fprintf( stderr , "Carattere \" %c \" non trovato\n" , start );
            /* Se la stringa non è un path copia tutta la stringa di input*/
            strncpy( nomeProg , pathProg , ( maxDim - 1 ) );
            nomeProg[maxDim - 1] = '\0';
            return strlen( nomeProg );
        }
        /* passo avanti perchè non mi interessa partire  dal carattere dopo il '\' */
        ptrStart++;
    
        /* Occorenza ultimo '.' per l'estensione */
        ptrClose = strrchr( pathProg , close );
        if( NULL == ptrStart ) {
            fprintf( stderr , "Carattere \" %c \" non trovato\n" , close );
            return 0;
        }
    
        /* Dimensione stringa con estensione */
        dI = strlen( ptrStart );
        if( !dI ) {
            fprintf( stderr , "Dimensione stringa con estensione non corretta \n" );
            return 0;
        }
    
        /* Dimensione estensione */
        dF = strlen( ptrClose );
        if( !dF ) {
            fprintf( stderr , "Dimensione estensione stringa non corretta \n" );
            return 0;
        }
    
        if( ( strlen( ptrStart ) - dF ) > maxDim - 1 )
    
        {
            fprintf( stderr , "%s%s" , "La dimensione del nome del programma eccede " ,
                     "la dimensione massima consentita\n" );
        }
        /* Copia in nomeProg la parte di path dal puntatore ptrStart */
        strncpy( nomeProg , ptrStart , maxDim );
    
        printf( "\n\n\nNome Programma con estensione: %s\n" , nomeProg );
    
        /* "riduco" la stringa del valore della dimensione di punto ed estensione */
        nomeProg[dI - dF] = '\0';
        printf( "Nome Programma senza estensione: %s\n" , nomeProg );
    
        return strlen( nomeProg );
    }
    
    int main( int argc , char *argv[] )
    {
        char stringa[40] = "" ;
        int dim = 0;
    
        dim = estraiNomeFile( argv[0] , stringa , 40 );
        puts( "" );
        printf( "Nome Programma = %s\n" , stringa );
        printf( "dimensione nome Programma = %d\n" , dim );
    
        return 0;
    }
    
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Il seguente codice ha passato l'analisi di Clang con -Weverything -Werror attivi e ha anche superato il controllo di SPLint (ovviamente non è perfetto). Io farei così per Linux(ci vuole poco a modificarlo per MS Windows):
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define DEBUG
    #define PATH_MAX (256)
    /* Tipo definito per compatibilità con il C89 */
    typedef enum{FALSE,TRUE} boolean_t;
    /* Prototipi di funzione */
    static boolean_t get_filename(char *path,char *cleared,size_t csize);
    /* Signori e signore,eccolo! Non ha bisogno di presentazioni: è il main!*/
    int main(void)
    {
            boolean_t success;
            char source[PATH_MAX];
            char dest[PATH_MAX] = "";
            char *p;
            size_t dim;
            printf("Inserire un percorso: ");
            if(NULL == fgets(source,PATH_MAX,stdin))
            {
                    printf("\nImpossibile leggere il percorso\n");
                    return EXIT_FAILURE;
            }
            p = strrchr(source,'\n');
            if(NULL != p)
            {
                    *p = '\0';
            }
            success = get_filename(source,dest,PATH_MAX);
            if(FALSE == success)
            {
                    printf("\nErrore: get_filename ha fallito\n");
                    return EXIT_FAILURE;
            }
            dim = strlen(dest);
            if(0 != dim)
            {
                    printf("Il nome del file è %s\n",dest);
            }
            else
            {
                    printf("File non valido,forse è una directory?\n");
            }
            return EXIT_SUCCESS;
    }
    /* Estrae il nome di un file da un percorso Unix-like */
    static boolean_t get_filename(char *path,char *cleared,size_t csize)
    {
            size_t pathlen = strlen(path);
            char *toslash;
            char *start;
            if(NULL == path || NULL == cleared || csize < (pathlen + 1))
            {
    #ifdef DEBUG
                    printf("Errore: file: extract.c func: get_filename()\n");
                    if(NULL == path)
                    {
                            printf("Causa: path è NULL\n");
                            printf("static boolean_t get_filename(char *path,char *cleared,size_t csize);\n");
                            printf("                              ~~~~~^~~~~\n");
                    } 
                    if(NULL == cleared)
                    {
                            printf("Causa: cleared è NULL\n");
                            printf("static boolean_t get_filename(char *path,char *cleared,size_t csize);\n");
                            printf("                                         ~~~~~~^~~~\n");
                    }
                    if(csize < (pathlen + 1))
                    {
                            printf("Causa: spazio non sufficiente in cleared\n");
                            printf("static boolean_t get_filename(char *path,char *cleared,size_t csize);\n");
                            printf("                                         ~~~~~~^~~~~~~ ~~~~~~~^~~~~~\n");
                    }
                   
    #endif
                    return FALSE;
            }
            
            toslash = strrchr(path,'/');
            if(NULL == toslash)
            {
                    start = path;
            }
            else
            {
                    start = toslash + 1;
            }
    
            while('.' != *start && '\0' != *start)
            {
                    *cleared = *start;
                    ++start;
                    ++cleared;
            }
            *cleared = '\0';
            return TRUE;
    }
    
    
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Però il test su csize dovresti farlo quando conosci la lunghezza effettivamente necessaria...
    Potresti avere dei percorsi lunghissimi ma dei nomi di file molto corti (ai tempi del dos erano possibili 8 caratteri al massimo).
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Dal momento che non si sa a priori il risultato ottenuto con get_filename() è obbligatorio passare un secondo array di dimensione pari,includendo '\0' nel conteggio,a quella della stringa originaria. Se in input avessi già il nome del file con una lunghezza di 256 caratteri?
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Se vuoi fare una funzione generica devi lasciare la possibilità di scelta al chiamante. Nel nostro caso useremo questa funzione per estrarre il nome dell'eseguibile (binario per te che sei in Linux) che tipicamente sarà un nome corto, magari 20 caratteri possono bastare.
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    Facendo come dici tu si deve calcolare la quantità di memoria a runtime e poi la passarla come parametro a get_filename(); ciò implica l'uso di malloc e VLA che sono fortemente sconsigliati(tra i tanti standard che lo vietano c'è il più noto MISRA/C) e non vanno assolutamente usati Di sicuro con la mia implementazione si spreca qualche manciata di byte,ma il comportamento è piuttosto deterministico rispetto alla controparte meno seria e rigorosa.Tra l'altro immagina di essere l'utente di una libreria che include la funzione da me scritta: senza usare malloc,come fai sapere che ti bastano esattamente 8 caratteri,per esempio? Sai già che il percorso non è già il nome del file stesso e che quindi non è necessario copiare tutta la stringa?
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    loopunrolling ha scritto:


    Il seguente codice ha passato l'analisi di Clang con -Weverything -Werror attivi e ha anche superato il controllo di SPLint (ovviamente non è perfetto). Io farei così per Linux(ci vuole poco a modificarlo per MS Windows):

    Grazie mille!
    Sicuramente prenderò spunto da questo codice, almeno in alcune sue parti.
    2 domande:

    1° hai definito il simbolo DEBUG (scusate se non si chiama simbolo la scritta dopo un #define) tra le direttive del precompilatore all'inizio e dentro la funzione inserito un #ifndef per in cludere quel segmento di codice solo se il simbolo DEBUG è definito.
    Ho chiarissimo il procedimento e tutto. mi chiedevo, se io uso il debug di Visula Studio, Code::block o qualsiasi altro debugger , il simbolo DEBUG è definito in automatico o devo commentare nella versione finale e decommentare ogni volta che voglio fare un debug del programma? (credo sia l'ultima, ma chiedere non costa nulla)



    l'ultima parte dove c'è il while(),, esso serve a cercale il . prima dell'estensione, ma se il nome del programma è del tipo " Programma.Prova.Ciao_a_Tuuti.xxx mi cancella dal primo punto, giusto?
    Non sarebbe meglio un strrchr per cercare l'ultimo punto e porre quel puntatore uguale a '\0' direttamente? (io ho usato un modo più lungo e macchinoso con quella differenza, ma ho già aggiornato il programma, la modifica mi è venuta in mente stamattina)
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    1) DEBUG è semplicemente una MACRO vuota e non ha nulla a che vedere con i vari SO/editor/IDE/compilatore etc in uso. L'ho definita io per poterla usare come switch per poter passare velocemente tra versione di sviluppo e versione finale. Nella versione di sviluppo fa comodo vedere dove e come si sono verificati gli errori e quindi si inseriscono messaggi utili ai fini di debugging,mentre nella versione finale,quella che riceverebbe l'utente,non servono a niente i messaggi di debug: basta solo gestire gli errori adeguatamente. Commentando la linea #define DEBUG tutto il codice di debug viene scartato dal preprocessore C,decommentandola si ritorna alla versione di sviluppo senza dover togliere i commenti intorno a tutto il codice. Non ho usato nessun #ifndef,magari ti sei confuso con #endif.

    2) La tua osservazione è condivisibile e corretta. Ho scritto la funzione facendo l'assunzione che non ci siano punti prima dell'estensione,ma come dici tu è più sensato usare strrchr(come avevo fatto inizialmente).

    EDIT: stavo a pensando a come evitare la copia di stringhe. Avevo scritto qui come fare,ma prima ci devo pensare bene onde scansare la possibilità di scrivere fesserie colossali.
  • Re: [RISOLTO][c] Estrarre sottostringa da stringa

    loopunrolling ha scritto:


    1) DEBUG è semplicemente una MACRO vuota e non ha nulla a che vedere con i vari SO/editor/IDE/compilatore etc in uso. L'ho definita io per poterla usare come switch per poter passare velocemente tra versione di sviluppo e versione finale. Nella versione di sviluppo fa comodo vedere dove e come si sono verificati gli errori e quindi si inseriscono messaggi utili ai fini di debugging,mentre nella versione finale,quella che riceverebbe l'utente,non servono a niente i messaggi di debug: basta solo gestire gli errori adeguatamente. Commentando la linea #define DEBUG tutto il codice di debug viene scartato dal preprocessore C,decommentandola si ritorna alla versione di sviluppo senza dover togliere i commenti intorno a tutto il codice. Non ho usato nessun #ifndef,magari ti sei confuso con #endif.

    2) La tua osservazione è condivisibile e corretta. Ho scritto la funzione facendo l'assunzione che non ci siano punti prima dell'estensione,ma come dici tu è più sensato usare strrchr(come avevo fatto inizialmente).

    EDIT: stavo a pensando a come evitare la copia di stringhe. Avevo scritto qui come fare,ma prima ci devo pensare bene onde scansare la possibilità di scrivere fesserie colossali.

    mi son confuso col #ifdef dentro la funzione get_filename().. bene o male ho capito come usare le direttive del preprocessore, mi chiedevo solamente se utilizzando i vari debugger in qualche modo ci fosse una macro che si definiva in automatico per questo genere di cose. Ho capito che non c'è e che devo farlo da sorgente. ok nulla di complicato.
    _Per evitare la copia,, non so se sia fattibile oppure utile evitarla, come parametro passerò quasi sicuramente __FILE__ o argv[0] che non sono modificabili, quindi averne una copia modificabile potrebbe essere utile, anche solo per semplificare il tutto.
Devi accedere o registrarti per scrivere nel forum
21 risposte