Pregunta

Ver también C Tokenizer


Aquí está un rápido substr() para C que escribí (sí, las inicializaciones de variables debe ser trasladado al inicio de la función, etc, pero usted consigue la idea)

He visto a muchos "inteligentes" de las implementaciones de substr (), que son fáciles de uno forro llamadas strncpy()!

Todos ellos están mal (strncpy no garantiza la terminación nula y por lo tanto la llamada NO puede producir un correcto subcadena!)

Aquí hay algo que tal vez mejor?

Llevar a cabo los bichos!

char* substr(const char* text, int nStartingPos, int nRun)
{
    char* emptyString = strdup(""); /* C'mon! This cannot fail */

    if(text == NULL) return emptyString;

    int textLen = strlen(text);

    --nStartingPos;

    if((nStartingPos < 0) || (nRun <= 0) || (textLen == 0) || (textLen < nStartingPos)) return emptyString;

    char* returnString = (char *)calloc((1 + nRun), sizeof(char));

    if(returnString == NULL) return emptyString;

    strncat(returnString, (nStartingPos + text), nRun);

    /* We do not need emptyString anymore from this point onwards */

    free(emptyString);
    emptyString = NULL;

    return returnString;
}


int main()
{
    const char *text = "-2--4--6-7-8-9-10-11-";

    char *p = substr(text, -1, 2);
    printf("[*]'%s' (\")\n",  ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 1, 2);
    printf("[*]'%s' (-2)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 3, 2);
    printf("[*]'%s' (--)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 16, 2);
    printf("[*]'%s' (10)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 16, 20);
    printf("[*]'%s' (10-11-)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 100, 2);
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 1, 0);
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    return 0;
}

Salida :

[*]'' (")
[*]'-2' (-2)
[*]'--' (--)
[*]'10' (10)
[*]'10-11-' (10-11-)
[*]'' (")
[*]'' (")
¿Fue útil?

Solución

Yo diría que volver NULL si la entrada no es válida más que un malloc()ed cadena vacía.De esa manera usted puede probar si es o no la función de error o no con if(p) en lugar de if(*p == 0).

También, creo que su función de pérdidas de memoria debido a emptyString es sólo free()d en una condicional.Usted debe asegurarse de que free() es incondicional, es decir,justo antes de la return.

En cuanto a tu comentario sobre strncpy() no NUL-terminación de la cadena (lo cual es cierto), si se utiliza calloc() para asignar la cadena en lugar de malloc(), esto no será un problema si usted asigna un byte más que copiar, ya que calloc() ajusta automáticamente todos los valores (incluyendo, en este caso, el fin) a 0.

Me gustaría darle más notas, pero me gusta la lectura camelCase código.No es que haya nada malo con ella.

EDITAR:Con respecto a las actualizaciones:

Ser conscientes de que el C estándar define sizeof(char) 1 independientemente de su sistema.Si usted está utilizando una computadora que utiliza 9 bits en un byte (dios no lo quiera), sizeof(char) es todavía va a ser 1.No es que haya nada de malo en decir sizeof(char) - muestra claramente su intención y proporciona simetría con llamadas a calloc() o malloc() para otros tipos.Pero sizeof(int) es realmente útil (ints pueden ser de diferentes tamaños de 16 y 32 y estos nuevos equipos de 64 bits).Cuanto más que usted sabe.

También me gustaría reiterar que la coherencia con la mayoría de los otros C código es para volver NULL en un error en lugar de "".Sé que muchas de las funciones (como strcmp()) probablemente va a hacer las cosas mal, si les pasa NULL - esto es de esperar.Pero la biblioteca estándar de C (y muchas otras APIs C) adoptar el enfoque de "Es responsabilidad del llamador para comprobar NULL, no la función de la responsabilidad del bebé de él/ella, si (s)no." Si quieres hacerlo de la otra manera, eso es genial, pero va en contra de una de las más fuertes tendencias en C diseño de la interfaz.

También, me gustaría utilizar strncpy() (o memcpy()) en lugar de strncat().El uso de strncat() (y strcat()) oculta su intención - que hace que la persona que vea el código de pensar que usted desee agregar al final de la cadena (que se puede hacer, porque después de calloc(), el fin es el comienzo), cuando lo que se desea hacer es ajustar la cadena. strncat() hace que parezca que estamos añadiendo a una cadena, mientras que strcpy() (o cualquier otra rutina de copia) podría hacer que se vea más como lo que tu intención.Las tres líneas siguientes, todos hacen lo mismo en este contexto - escoja cualquiera que piense que se ve más bonito:

strncat(returnString, text + nStartingPos, nRun);

strncpy(returnString, text + nStartingPos, nRun);

memcpy(returnString, text + nStartingPos, nRun);

Además, strncpy() y memcpy() probablemente será un (pequeñito) poco más rápido/más eficiente que strncat().

text + nStartingPos es el mismo nStartingPos + text - Yo pondría la char * en primer lugar, como creo que es más claro, pero en cualquier orden que desee poner en usted.También, los paréntesis alrededor de ellos son innecesarios (pero agradable), ya que + tiene mayor prioridad que ,.

EDIT 2:Las tres líneas de código no hacer la misma cosa, pero en este contexto todos producen el mismo resultado.Gracias por la captura de mí en eso.

Otros consejos

Su función parece muy complicado para lo que debería ser una operación sencilla. Algunos problemas son (no todos estos son errores):

  • strdup(), y otras funciones de asignación de memoria, puede fallar, que deben permitir todos los temas posibles.
  • Sólo asignar recursos (memoria, en este caso), siempre y cuando lo necesite.
  • que debe ser capaz de distinguir entre los errores y picaduras válidos. Por el momento, no se sabe si malloc() fracaso de substr ("xxx",1,1) o una trabajadora substr ("xxx",1,0) produce una cadena vacía.
  • que no es necesario que calloc() memoria que se va a sobrescribir todos modos.
  • todos los parámetros no válidos o bien debe causar un error o ser forzado a un parámetro válido (y su API deben documentar cuál).
  • que no es necesario para establecer el emptyString local para NULL después de liberarla. - se perderá el retorno de la función
  • que no es necesario usr strncat() - que debe conocer los tamaños y la memoria que tiene disponible antes de hacer cualquier copia para que pueda utilizar el (muy probablemente) más rápido memcpy().
  • eres uso de la base-1 en lugar de basar-0 para los índices de las cadenas va a contrapelo de C.

El siguiente segmento es lo que haría (me gusta más el lenguaje Python de valores negativos a contar desde el final de la cadena, pero me he mantenido longitud en lugar de la posición final).

char *substr (const char *inpStr, int startPos, int strLen) {
    /* Cannot do anything with NULL. */

    if (inpStr == NULL) return NULL;

    /* All negative positions to go from end, and cannot
       start before start of string, force to start. */

    if (startPos < 0)
        startPos = strlen (inpStr) + startPos;
    if (startPos < 0)
        startPos = 0;

    /* Force negative lengths to zero and cannot
       start after end of string, force to end. */

    if (strLen < 0)
        strLen = 0;
    if (startPos >strlen (inpStr))
        startPos = strlen (inpStr);

    /* Adjust length if source string too short. */

    if (strLen > strlen (&inpStr[startPos]))
        strLen = strlen (&inpStr[startPos]);

    /* Get long enough string from heap, return NULL if no go. */

    if ((buff = malloc (strLen + 1)) == NULL)
        return NULL;

    /* Transfer string section and return it. */

    memcpy (buff, &(inpStr[startPos]), strLen);
    buff[strLen] = '\0';

    return buff;
}
char* emptyString = strdup(""); /* C'mon! This cannot fail? */

Es necesario comprobar para nulo. Recuerde que todavía debe asignar 1 byte para el carácter nulo.

strdup podría fallar (aunque es muy poco probable y no vale la pena mirar para, en mi humilde opinión). Se tiene sin embargo otro problema - no es una función estándar de C. Sería mejor utilizar malloc.

También puede utilizar la función memmove para devolver una subcadena de principio a longitud. Mejorando / añadir otra solución de la solución de paxdiablo:

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

    char *splitstr(char *idata, int start, int slen) {
            char ret[150];
            if(slen == NULL) {
                    slen=strlen(idata)-start;
            }
            memmove (ret,idata+start,slen);
            return ret;
    }

    /*
    Usage:
            char ostr[]="Hello World!";
            char *ores=splitstr(ostr, 0, 5);
            Outputs:
                    Hello
    */

Espero que ayuda. Probado en Windows 7 Home Premium con TCC C Compilier.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top