Ayuda con cadenas.

Iniciado por User, 16 Julio 2012, 09:31 AM

0 Miembros y 1 Visitante están viendo este tema.

do-while

#10
¡Buenas!

XD, ahora te explico el codigo, que veo que te faltan algunos conocimientos.

1- primera = strtok(cadena," ");

Si lees el enlace a la referencia sobre strtok que te han dejado, veras que esta llamada ignora los primeros caracteres que se encuentran en los delimitadores y empieza a contar el primer token (no se si esta palabra tiene traducciona al castellano...) desde el primer caracter que no este entre los delimitadores hasta el primer caracter que encuentre en los delimitadores. En este punto marca el delimitador encontrado con el caracter '\0', y devuelve un puntero al primer token (las siguientes llamadas funcionan igual, pero con el primer parametro NULL).

Asi si tenemos la cadena {'a','b',' ','c','\0'}, despues de esta llamada cadena quedaria:  {'a','b','\0','c','\0'} y primera seria {'a','b','\0'}

Si no encontrase el delimitadore devolveria NULL, y aqui viene la segunda parte:

2- if(primera): Si hemos encontrado un delimitador, primera apuntara a alguna parte de la memoria, y tendra un valor distinto de NULL (o distinto de cero). Las condiciones logicas en C son valores numericos. Cero o NULL significa falso y distinto de cero verdadero. Por lo tanto estamos diciendo que ejecute el codigo del if si primera es distinto de NULL (cero).

3- En esta parte he tenido un error. Ahora lo corregimos.

resto = &cadena[strlen(primera) + 1]

Tendria que ser:

resto = &cadena[strlen(cadena) + 1];

¿Porque?

Strlen nos devuelve la longitud en caracteres de una cadena. Siguiendo el ejemplo anterior, como hemos cortado el primer delimitador con un caracter nulo, se tendra que strlen(cadena) = strlen({'a','b','\0','c','\0'}) = strlen({'a','b','\0'}) = 2. cadena[2] = '\0', asi que por eso le sumo 1, para pasar al siguiente caracter despues de donde hemos cortado la cadena original.

Y el porque deberia de ser cadena el parametro de strlen y no primero, es por lo que he dicho en el primer punto: Si una cadena contiene al principio caracteres que esten contenidos entre los delimitadores, strtok los ignora y contara el primer token a partir del primer caracter de la cadena que no este contenido entre los delimitadores. Si utilizasemos primera como referencia para "saltar" esos primeros caracteres y la cadena comenzase con delimitadores, estariamos contando menos caracteres de los necesarios, en cambio utilizando cadena como parametro para strlen, nos aseguramos de que esos primeros caracteres que pueden ser delimitadores tambien se esten contando.

entonces en este caso la expresion

resto = &cadena[strlen(cadena) + 1];

seria lo mismo que resto = &cadena[3];

& nos da la direccion en memoria de una variable, por lo que estamos asignando a resto la posicion en memoria de cadena[3], que sera la cadena restante de quitar la primera parte de cadena: {'c','\0'}

Si algo no te ha quedado claro no dudes en preguntar.

¡Saludos!

PD: El else no sobraba, en la cadena que has puesto tu si que hay espacios, pero una cadema cualquiera puede no tenerlos.

Ademas, tendrias que comprobar que, despues de "quitar" la primera palabra de la cadena, el resto no sea el caracter '\0', o una cadena compuesta por espacios en blanco, tabuladores..., ya que tendrias una cadena, en principio, sin ninguna informacion util...
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

BlackZeroX

#11
Es mas optimo el que uso @User no da tantos giros innecesarios...

Código (cpp) [Seleccionar]


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

const char ESPACIO[] = " "; // conat char ESPACIO = ' ';
const char END[] = "";

int main ()
{
    char CADENA[] = "Hola como estas ?";
    char* ptr1 = NULL;
    char* ptr2 = NULL;

    ptr1 = strtok((char*)CADENA, ESPACIO);
    ptr2 = ptr1 ? strtok(NULL, END):NULL;
    printf ("Este es el 1 valor: %s\n", ptr1);
    printf ("Este es el 2 valor: %s\n", ptr2);
    return EXIT_SUCCESS;
}



Dulces Lunas!¡.
The Dark Shadow is my passion.

do-while

#12
Cita de: BlackZeroX (Astaroth) en 17 Julio 2012, 01:47 AM
Es mas optimo el que uso @User no da tantos giros innecesarios...

Dulces Lunas!¡.

??????

En lugar de tres asignaciones, una llamada a funcion (que posiblemente acceda mediante un bucle a las posiciones de la cadena para rastrear delimitadores) y una salida forzada de un bucle, estoy realizando una llamada a funcion, un desplazamiento de un puntero (y solo uno), el operador de direccion y una asignacion...

De todas maneras, voy a meter los dos codigos en bucles de 1000 repeticiones (o mas, para que tarde lo suyo) y a ver cual de los dos termina antes... Ahora pongo el codigo que utilizare y los resultados.

MODIFICADO:

El codigo:
#include <stdio.h>
#include <string.h>
#include <time.h>

#define ITER 134217728

int main ()
{
  char str[] ="Hola c";
  char str2[] = "Hola c"; /* para sobreescribir str en cada iteracion */
  char * pch;
  char * valor1;
  char * valor2;
  int i,inicio;

    inicio = time(NULL);

    for(i = 0 ; i < ITER ; i++)
    {
        strcpy(str,str2);

        pch = strtok (str," "); // Empiezo a buscar la 1 coincidencia del espacio en blanco
        while (pch != NULL)
        {
            valor1 = (pch); // Aqui se almacena la 1 palabra de la frase separa por el espacio en blanco
            pch = strtok (NULL, ""); // Aqui ya no le pongo el espacio en blanco porque deseo el resto de la frase completa
            valor2 = (pch); // Almaceno el resto de la frase en la variable
            break; // Interrumpo el bucle pues como dije solo queria obtener la 1 palabra con el espacio como delimitador
        }
    }

    printf("Metodo 1: %d segundos.\n",time(NULL) - inicio);

    inicio = time(NULL);

    for(i = 0 ; i < ITER ; i++)
    {
        strcpy(str,str2);

        pch = strtok(str," ");

        if(pch)
            valor2 = &str[strlen(str) + 1];
    }

    printf("Metodo 2: %d segundos.\n",time(NULL) - inicio);

    inicio = time(NULL);

    for(i = 0 ; i < ITER ; i++)
    {
        strcpy(str,str2);

        pch = strtok(str," ");

        if (pch)
            valor2 = strtok(NULL, "");
    }

    printf("Metodo 3: %d segundos.\n",time(NULL) - inicio);

    return 0;
}


Y los resultados:

Metodo 1: 19 segundos.
Metodo 2: 10 segundos.

Process returned 0 (0x0)   execution time : 29.150 s
Press any key to continue.


Compilado con gcc y ejecutado en un Dual-Core T4300 a 2.1 Ghz

Ejecutandolo varias veces el metodo de User ronda los 18 - 19 segundos y el mio esta entre los 10 y los 11 (en la maquina que he dejado indicada)

¡Saludos!
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

User

Bueno creo que esto esta yendo mas alla de mis nulos conocimientos en C :

Ahora se me vienen mas preguntas a la cabeza BlackZeroX dice que el codigo que use es mas optimo y me gustaria saber el porque ? tambien veo que no usa while en el ejemplo que puso y creo que el while es innecesario porque no requiero  recorrer toda la frase buscando los espacios en blanco, solo el primero, entonces con el if que usaste es suficiente verdad y pregunto nuevamente tambien porque el else ? .. el else en caso no encuentre espacios en blanco, pero donde lo usare siempre habra espacios en blanco, entonces el else no estaria de mas  ? corriganme si ando mal.

En cuando ejecucion de codigo, un codigo que haga determinada accion en menos tiempo que otro es mas efectivo ? siempre es asi ? velocidad = efectividad y estabilidad ?


No pense que este tema  llegaria a este punto y me pacere interesante ya que creo que esos puntos tambien son importantes. Gracias




do-while

#14
¡Buenas!

La cuestion es que si un codigo se ejecuta en menos tiempo que otro, significa que el coste (en tiempo) de las operaciones que realiza es menor. Por otra parte hay algo que me extraña y es que luego he hecho la prueba con el codigo de BlackZeroX:

for(i = 0 ; i < ITER ; i++)
{
   strcpy(CADENA, str2);

   ptr1 = strtok((char*)CADENA, ESPACIO);
   if (ptr1)
       ptr2 = strtok(NULL, END);
}


y me sigue saliendo que le cuesta tanto como al tuyo, por lo que resulta que es mas rapido valor2 = &str[strlen(str) + 1];  que ptr2 = strtok(NULL, END);. Y esto es lo que me resulta extraño, ya que con su codigo solamente se hace una llamada a funcion y una asignacion, por lo que la cuestion tiene que ser que strtok es mas lento que realizar todas las operaciones de &str[strlen(str) + 1]... :o

Que me lo expliquen... Si alguien de ingenieria inversa le puede hechar un vistazo a los binarios y aclararnoslo...

¡Saludos!

Acabo de dejar el codigo en el que he incluido el metodo de BlackZeroX por si quereis comprobar los tiempos.

¡Saludos!

Corrigiendo: Acabo de comprobar que si en una primera llamada no encuentra los delimitadores, devuelve la cadena completa, y si en alguna de las siguientes llamadas no encuentra delimitadores, tambien devuelve el resto de la cadena. Al parecer los unicos casos en los que strtok devuelve NULL es si se vuelve a llamar a la funcion cuando no se han encontrado delimitadores o si en algun momento solo quedan delimitadores en la cadena.

Pero si, el else estaba de mas, ya que si falla if(ptr1), es porque ptr1 ya es NULL, por lo que asignaerle NULL en el else es redundante.
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

BlackZeroX

#15
Quiero creer que strtok() hace una llamada a realloc() aumentando en +1 su tamaño ya que de lo contrario ese '\0' que agrega para dividir con varias llamadas a strtok() a larga crearía un error de escritura en regiones invalidas...

Código (vb) [Seleccionar]


... solo es otra manera de escribir: &str[strlen(ptr1) + 1];

    ptr1 = strtok((char*)CADENA, ESPACIO);
    ptr2 = ptr1 ? (char*)(cadena + strlen(ptr1) + 1): NULL;

...



Dulces Lunas!¡.
The Dark Shadow is my passion.

do-while

#16
Cita de: BlackZeroX (Astaroth) en 17 Julio 2012, 05:15 AM

   ptr2 = ptr1 ? (char*)(cadena + strlen(ptr1) + 1):


Esto tampoco es correcto. A parte de que User ha dicho que todavia no ha estudiado la aritmetica de punteros, desplazar cadena stlen(ptr1) posiciones no te asegura pasar a la siguiente posicion despues del primer token (¿lees los post anteriores o solo les echas un vistazo por encima y luego posteas sin saber lo que se ha dicho anteriormente?)

Si cadena = "---hu-ha"; la primera llamada a strtok dejara las variables asi:

ptr1 = strtok(cadena,"-");

ptr1 = "hu";
cadena = "---hu'\0'ha"

Asi que cadena + strlen(ptr1) + 1 apuntara otra vez a "hu'\0'ha". En algun post anterior, ya he comentado que tiene que volver a utilizarse la misma cadena que se le ha pasado a strtok. Si miras como ha quedado cadena despues de la primera llamada a strtok veras el porque.

¡Saludos!
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

BlackZeroX

#17
Cita de: do-while en 17 Julio 2012, 13:30 PM
Esto tampoco es correcto. A parte de que User ha dicho que todavia no ha estudiado la aritmetica de punteros, desplazar cadena stlen(ptr1) posiciones no te asegura pasar a la siguiente posicion despues del primer token (¿lees los post anteriores o solo les echas un vistazo por encima y luego posteas sin saber lo que se ha dicho anteriormente?)


Si cadena = "---hu-ha"; la primera llamada a strtok dejara las variables asi:

ptr1 = strtok(cadena,"-");

ptr1 = "hu";
cadena = "---hu'\0'ha"

Asi que cadena + strlen(ptr1) + 1 apuntara otra vez a "hu'\0'ha"
. En algun post anterior, ya he comentado que tiene que volver a utilizarse la misma cadena que se le ha pasado a strtok. Si miras como ha quedado cadena despues de la primera llamada a strtok veras el porque.

¡Saludos!

Esa afirmación es mentira... la aritmética dice todo lo contrario, ya que ambos métodos son equitativos.



...
char cadena[] = "Necesito repasar matematicas basicas"
ptr1 = strtok(cadena, " "); // ptr1 = Necesito\0repasar matematicas basicas
cadena + strlen(ptr1) + 1; // repasar matematicas basicas
cadena[strlen(ptr1) + 1]; // repasar matematicas basicas
...



http://foro.elhacker.net/programacion_cc/ayuda_con_cadenas-t367020.0.html;msg1766536#msg1766536

Edito:

Nunca mire tu post me centre en la duda.

Dulces Lunas!¡.
The Dark Shadow is my passion.

do-while

#18
Si vas a criticar algo, lee lo que se postea primero y luego comentas, ¿ok?

Bien, estaba nervioso porque no entrabas en razon y he utilizado palabras que no deberia. Lo siento. Con este codigo puedes comprobar que no estas en lo cierto:

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

int main(int argc, char *argv[])
{
   char cadena[] = "    Necesito repasar matematicas basicas";
   char *token,*resto,lector;

   token = strtok(cadena," ");

   printf("token: %s\ncadena + strlen(token) + 1: %s\ncadena + strlen(cadena) + 1: %s\n",
           token, cadena + strlen(token) + 1, cadena + strlen(cadena) + 1);

   while((lector = getchar()) != '\n' && lector != EOF);

   return 0;
}


Por lo menos tomate la molestia de comprobar si lo que dices es cierto o no.

¡Saludos!

PD: Esto si que es un error que he cometido yo. No he comprobado si realmente queda cadena o no despues de la primera llamada a strtok. Vosotros al utilizar strtok de nuevo, podeis saberlo mirando si el puntero devuelto es NULL o no. Aqui va el nuevo codigo y los resultados de los tiempos.


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

#define ITER 134217728

int main ()
{
  char str[] ="Hola como estas ?";
  char str2[] = "Hola como estas ?";
  char * pch;
  char * valor1;
  char * valor2;
  int i,inicio,longitud;

    inicio = time(NULL);

    for(i = 0 ; i < ITER ; i++)
    {
        strcpy(str,str2);

        pch = strtok (str," "); // Empiezo a buscar la 1 coincidencia del espacio en blanco
        while (pch != NULL)
        {
            valor1 = (pch); // Aqui se almacena la 1 palabra de la frase separa por el espacio en blanco
            pch = strtok (NULL, ""); // Aqui ya no le pongo el espacio en blanco porque deseo el resto de la frase completa
            valor2 = (pch); // Almaceno el resto de la frase en la variable
            break; // Interrumpo el bucle pues como dije solo queria obtener la 1 palabra con el espacio como delimitador
        }
    }

    printf("Metodo 1: %d segundos.\n",time(NULL) - inicio);

    inicio = time(NULL);

    for(i = 0 ; i < ITER ; i++)
    {
        strcpy(str,str2);

        longitud = strlen(str);

        pch = strtok(str," ");

        if(pch)
            if(strlen(str) != longitud)
                valor2 = &str[strlen(str) + 1];
    }

    printf("Metodo 2: %d segundos.\n",time(NULL) - inicio);

    inicio = time(NULL);

    for(i = 0 ; i < ITER ; i++)
    {
        strcpy(str,str2);

        pch = strtok(str," ");

        if (pch)
            valor2 = strtok(NULL, "");
    }

    printf("Metodo 3: %d segundos.\n",time(NULL) - inicio);

    return 0;
}


El tiempo del segundo metodo a subido algo...


Metodo 1: 19 segundos.
Metodo 2: 13 segundos.
Metodo 3: 19 segundos.

Process returned 0 (0x0)   execution time : 51.184 s
Press any key to continue.
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

User

Hola nuevamente  gracias a ambos por sus aclaraciones, ya quisiera tener sus conocimientos
que se nota que saben mucho pues ya hay cosas que no entiendo como dije soy novato en esto pues los unicos lenguajes que domino es vb 6.0 y php, y bueno ya se nota un poco de tension en los posts, muchachos ambos son buenos no entren en esa onda, ambos son buenos, no era mi intencion llegar a esto, gracias nuevamente a ambos y ya les podre otra duda no se si en otro hilo o lo continuo aqui.