Autor Tema: Tutorial - Cadenas y arreglos de chars  (Leído 55230 veces)

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

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Tutorial - Cadenas y arreglos de chars
« en: 28 de Abril de 2009, 19:22:50 »
Gracias a que existen compiladores de C para pics es posible que los micros puedan entregar resultados en forma de texto y no en bits. Los compiladores de C normal (nada de c++, ni c#, ni java) no manejan variables tipo string pero manejan algo similar: arreglos de chars.

La variable char mide 8 bits y puede expresar datos de 0 a 255 (256 valores). Comúnmente se vincula un char a una letra o caracter del código ascii dependiendo del valor binario que éste tenga. Por ejemplo, la letra 'a' vale 0x61 en código ascii o el espacio ' ' vale 0x20.

La tabla ascii es muy popular, acá están sus valores: http://www.asciitable.com/

Un arreglo es una sucesión de variables en memoria ram. Un arreglo de chars es una sucesión de variables de 8 bits.

Si se declara lo siguiente:

char arreglo[5];

se tendrá un arreglo de 5 elementos de tipo char. El contenido de cada char es aleatorio al arrancar el pic y usando el compilador de CCS para el ejemplo anterior. Se deberá inicializar el valor de cada elemento según el uso que se le vaya a dar.

Para convertir el arreglo de chars anterior en una cadena formal, el arreglo deberá contar con lo siguiente:

- Debe tener de 0 a 4 elementos (caracteres ascii) en las primeras 4 posiciones
- El último elemento deberá ser un NULL char que equivale a 0x00

El elemento NULL marca el fin de los caracteres útiles de la cadena. Armemos una cadena sencilla con las características que se mencionaron:

arreglo[0]='c';
arreglo[1]='a';
arreglo[2]='s';
arreglo[3]='a';
arreglo[4]=0x00;

La cadena claramente contiene 'casa'. El quinto elemento dice: 'aquí terminó el texto'.

Al usar la declaración char arreglo[5]; en realidad se está declarando un apuntador a una secuencia de chars. El apuntador se llamará arreglo y será capaz de conocer la dirección ram de un char.

Por ejemplo, si yo quisiera saber cuál es la tercera letra de mi cadena puedo hacerlo de muchas formas...

Código: [Seleccionar]
//Usando un índice con arreglo (la más fácil)
if(arreglo[2]=='s')
   //Es una s

//Usando otro apuntador de chars
char *apuntador;
apuntador = arreglo+2; //copia la dirección ram del tercer elemento de la cadena (letra s)
if(*apuntador == 's')
   //Es una s

//Usando otro apuntador de chars
char *apuntador;
apuntador = &arreglo[2]; //copia la dirección ram del tercer elemento de la cadena (letra s)
if(*apuntador == 's')
   //Es una s

El siguiente ejemplo muestra una cadena que contiene el texto útil 'Calcula'.

Código: [Seleccionar]
char cadena[30]; //Una cadena que podrá almacenar hasta 29 caracteres
cadena[0]='C';
cadena[1]='a';
cadena[2]='l';
cadena[3]='c';
cadena[4]='u';
cadena[5]='l';
cadena[6]='a';
cadena[7]=0x00;
cadena[8]='d';
cadena[9]='o';
cadena[10]='r';
cadena[11]='a';

Al insertar el null char en la octava posición sabremos que ahí terminó la cadena a pesar de que hay más letras después.

Pude haberme ahorrado el esfuerzo y declarado lo anterior más rápido:

char cadena[]="Calcula";

Al escribir lo anterior el compilador cuenta cuántos chars lleva la cadena y agrega el NULL char, obteniendo 8 elementos para el arreglo cadena. Este arreglo es de tamaño fijo a pesar de que no le declaramos uno. El uso de tal instrucción solo es válido al principio del programa. Si se desea construir una cadena a mitad de un programa es mejor usar sprintf.

Para declarar cadenas hay 2 formas básicas: cadenas de tamaño constante y cadenas dinámicas. Ya se vio la declaración de las de tamaño constante, ahora se verá la dinámica:

char *cadena;

Al no tener un tamaño la cadena puede invadir memoria ram dedicada a otros propósitos y eso se debe evitar a toda costa. Para ubicar memoria ram en CCS para cadenas dinámicas (también para arreglos de cualquier otra variable dinámica) se usa malloc(). No profundizaré en este tema porque se usa poco en microcontroladores. Ejemplo:

Código: [Seleccionar]
#USE DYNAMIC_MEMORY
...

char *cadena;

...

//Búscame ram para almacenar 30 chars consecutivos
cadena=(char *)malloc(30);

...

//Ya no voy a usar la cadena, deséchala
free(cadena);

De esa forma la cadena ocupará espacio seguro y no invadirá el espacio de otros registros.

En C se usa la función sprintf para convertir variables (enteros, chars, arreglos de chars, flotantes, etc.) en texto legible para un usuario. Lo que hace la función es construir arreglos de chars y terminarlos con un NULL char.

Ejemplo 1 - Cómo almacenar 5 cadenas en una sola cadena
Código: [Seleccionar]
char cadena[50]; //cada cadena tendrá 9 chars + 1 NULL = 10 chars

//primera palabra
sprintf(cadena,"perro");
//segunda
sprintf(cadena+10,"gato");
//tercera
sprintf(cadena+20,"ratón");
//cuarta
sprintf(cadena+30,"queso");
//quinta
sprintf(cadena+40,"leche");

Pero, ¿por qué caben 5 cadenas en una sola cadena? Sprintf se encargó de insertar las letras de la palabra más un null char al final de cada una de ellas. Al usar printf para imprimirlas, buscará el null char y ahí se detendrá sin importar qué más hay después.

Ejemplo 2 - Imprimiendo las multicadenas (sigue del ejemplo 1)
Código: [Seleccionar]
//Define el tamaño de cadena en la multicadena
#define tamano 10
...

//primera
printf("%s \n", &cadena[0*tamano]);
//segunda
printf("%s \n", &cadena[1*tamano]);
//tercera
printf("%s \n", &cadena[2*tamano]);
//cuarta
printf("%s \n", &cadena[3*tamano]);
//quinta
printf("%s \n", &cadena[4*tamano]);

Lo que saldría en pantalla sería

perro
gato
ratón
queso
leche

Y un último ejemplo más difícil en el que determino la extensión del nombre de un archivo para ver si es un archivo JPEG válido (.jpg o .JPG).

Ejemplo 3 - Detectando la extensión de un archivo
Código: [Seleccionar]
    //verifica que el archivo sea un jpg válido
    //.jpg, .JPG
    char temporal[6];
    bool jpg_valido=0;

    j=0;
    for(i=(strlen(filename)-4);i<strlen(filename);i++)
        temporal[j++]=filename[i];
    temporal[j]=0;
    if(strcmp(temporal,".jpg")==0) jpg_valido=1;
    if(strcmp(temporal,".JPG")==0) jpg_valido=1;
    //.jpeg, .JPEG
    j=0;
    for(i=(strlen(filename)-5);i<strlen(filename);i++)
        temporal[j++]=filename[i];
    temporal[j]=0;
    if(strcmp(temporal,".jpeg")==0) jpg_valido=1;
    if(strcmp(temporal,".JPEG")==0) jpg_valido=1;

Primero se posiciona en el cuarto char de atrás hacia adelante de la cadena filename gracias a la función strlen() que indica cuántos chars útiles hay en la cadena. Después copia los 4 últimos chars en otra cadena llamada temporal. Por último determina con la función strcmp si la cadena temporal es idéntica a la cadena ".JPG" o ".jpg". De ser el caso, el archivo es un JPEG.

Repite el caso para .jpeg y .JPEG.

Si pudiste entender el ejemplo 3 entonces ya te sirvió este mini tutorial de cadenas.  :D

Si lo creen necesario puedo seguir explicando funciones de concatenación de strings, comparación, conteo, etc.

Actualización.

Gera continuará explicando los demás funciones de string.h aquí:

http://www.todopic.com.ar/foros/index.php?topic=25785.msg210828#msg210828
« Última modificación: 03 de Mayo de 2009, 11:46:10 por migsantiago »

Desconectado gera

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2188
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #1 en: 28 de Abril de 2009, 21:07:36 »
Excelente migsantiago!!! :-/ yo pensaba hacer algo al estilo pero me ganaste de mano jaja. Si me permitis, le voy a agregar una pequeña guia para usar la libreria string.h
En breve me pongo a escribir algo. Saludos!!!

"conozco dos cosas infinitas: el universo y la estupidez humana. Y no estoy muy seguro del primero." A.Einstein

Desconectado BrunoF

  • Administrador
  • DsPIC30
  • *******
  • Mensajes: 3865
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #2 en: 28 de Abril de 2009, 22:07:32 »
Excelenteee!!!

Agregaría una cosita: que una vez declarado un array:

char arreglo[5];

Es ilegal intentar cargarle todos los caracteres juntos:

arreglo="casa";

Por eso recurres a hacer:

arreglo[0]='c';
arreglo[1]='a';
arreglo[2]='s';
arreglo[3]='a';
arreglo[4]=0x00;
"All of the books in the world contain no more information than is broadcast as video in a single large American city in a single year. Not all bits have equal value."  -- Carl Sagan

Sólo responderé a mensajes personales, por asuntos personales. El resto de las consultas DEBEN ser escritas en el foro público. Gracias.

Desconectado gera

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2188
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #3 en: 29 de Abril de 2009, 00:33:16 »
Bueno, vamos a ver si puedo contribuir con este hilo tratando de explicar las funciones mas importantes de la biblioteca string.h q nos facilitan la vida a la hora de trabajar con strings (o bloques de memoria).
Vamos a ver cada funcion, lo que hace, su sintaxis y su uso :wink:

Funciones:
strcpy   copia una cadena
strcat   concatena cadenas
strcmp   compara dos cadenas
strchr   localiza la primera ocurrencia de un caracter en una cadena
strrchr   localiza la ultima ocurrencia de un caracter en una cadena
strstr   localiza una sub cadena
strtok   divide una cadena en elementos (tokens)
memset   llena un espacio de memoria (puede llenar una cadena con un caracter dado)
strlen   obtiene la longitud de una cadena en caracteres (bytes)

Macros
NULL   puntero nulo

Tipos
size_t   entero sin signo q se utiliza para indicar el tamaño de una cadena

Bueno, empecemos a ver funcion por funcion qué hace cada una, y como se usa.


Funcion: strcpy

Sintaxis: char *strcpy(char *destino, const char *origen);
Vemos que la función recibe dos punteros, uno que apunta a la cadena de origen, y otro al destino. El valor retornado por strcpy es un puntero al destino.

Descripción:
Esta función copia caracter a caracter el contenido de una cadena a otra, incluyendo el caracter nulo. Muchas veces he visto que alguien quiere igualar cadenas haciendo algo como esto
string1=string2; MAL
Esto es una igualación de punteros. Osea, estamos haciendo que el puntero "string2" apunte al mismo lugar que "string1". Y si éstos fueron declarados como arreglos, es imposible hacer esto, ya que son puntero estáticos y cada uno apunta al lugar de memoria que le fue asignado.
Entonces, cómo hacemos si queremos que el arreglo "string2" tenga el mismo contenido que "string1"? Simplemente con strcpy ;)

Ejemplo:
Código: C
  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. void main()
  5. {
  6.         char string1[]="ejemplo de string"; //son 17 caracteres mas el nulo
  7.         char string2[20];               //tiene q ser de tama&#241;o mayor o igual a string1
  8.         strcpy(string2,string1);
  9.         printf("string2: %s\n",string2);
  10. }

Comentarios:
Es importante que el destino tenga más posiciones de memoria reservadas que el origen, ya que podemos salirnos del espacio de memoria del destino y pisar otra variable. También conviene limpiar la cadena de destino antes de hacer la copia (seria análogo a inicializar una variable), esto se hace con la función memset() que vamos a ver más adelante.


Funcion: strcat

Sintaxis: char *strcat(char *destino, const char *origen);
Esta función recibe dos punteros. El primero apunta a la cadena original a la cual le queremos añadir otra, y el segundo apunta a la cadena que queremos añadir. Retorna el puntero

Descripción:
La función strcat, concatena dos cadenas. O sea, añade una cadena al final de otra, suprimiendo el caracter nulo de la última. Hay que tener cuidado de no excedernos de los limites de memoria de la cadena de destino.

Ejemplo:
Código: C
  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. void main()
  5. {
  6.         char string[20]="hola ";
  7.         strcat(string,"amigos!");
  8.         printf("string: %s\n",string);
  9. }

Comentarios:
Como ya dijimos, la cadena afectada tiene que tener suficiente memoria reservada como para contener el total de los caracteres. De lo contrario podría producirse un error.
Además vemos que a la función le podemos pasar como parámetro una cadena directamente entre comillas. Esto se puede hacer con todas las funciones que reciban como parámetro un puntero a una cadena. Obviamente, estas cadenas se encuentran en la memoria de programa y no pueden ser modificadas ni utilizadas como variables.

Funcion: strcmp

Sintaxis: int strcmp(char *s1, char *s2);
Esta función recibe dos cadenas como parámetros y devuelve 0 si son iguales, un negativo si s1<s2 y un positivo si s1>s2.

Descripción:
Bueno, en la sintaxis ya expliqué bastante. La función básicamente sirve para saber si una cadena es igual a otra. Devolviendo 0 en este caso, y distinto de 0 si son distintas. Otro error común, es usar el operador == para saber si las cadenas son iguales:
if(string1==string2) MAL

Ejemplo:
Código: C
  1. #include<stdio.h>
  2. #include<string.h>
  3.  
  4. void main()
  5. {
  6.         char s1[]="hola mundo!"; //cuanta originalidad jaja
  7.         if( strcmp(s1,"chau mundo!") == 0);
  8.               printf("las cadenas son iguales");
  9.         else
  10.               printf("las cadenas son distintas");
  11. }

Comentarios:
Este ejemplo siempre va a dar negativo, salvo que pongamos como segundo parámetro de la función "hola mundo!". La única precaución que hay que tener al llamar a esta función, es que las cadenas tengan un terminador (NULL), si no puede haber un overflow.

---------------------------------------EN CONSTRUCCIÓN----------------------------------------
« Última modificación: 21 de Julio de 2009, 20:10:57 por gera »

"conozco dos cosas infinitas: el universo y la estupidez humana. Y no estoy muy seguro del primero." A.Einstein

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #4 en: 29 de Abril de 2009, 11:08:39 »
Excelenteee!!!

Agregaría una cosita: que una vez declarado un array:

char arreglo[5];

Es ilegal intentar cargarle todos los caracteres juntos:

arreglo="casa";

Por eso recurres a hacer:

arreglo[0]='c';
arreglo[1]='a';
arreglo[2]='s';
arreglo[3]='a';
arreglo[4]=0x00;


Sí Bruno, ya está mencionado en el tutorial. En esos casos habrá que recurrir al armado manual de la cadena o al uso de sprintf.  :mrgreen: En compiladores como c++, c# o java el uso de arreglo="casa"; a medio programa sí es posible, pero en c a secas es ilegal.

Gera, puedes seguir sin problemas con el uso de strings.h.  :-/

Ah y por cierto, hay un error en el ejemplo 3. El que lo encuentre se llevará una estrellita.  :D

Desconectado gera

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2188
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #5 en: 29 de Abril de 2009, 12:49:39 »
Bueno, ya largué con una de las funciones, de a poco voy a ir explicando todas. Cualquier cosa q se me escape o me haya equivocado me lo hacen saber por favor.
Santi, creo q tu error esta acá:
for(i=(strlen(filename)-4);i<strlen(filename);i++)
deberia ser -3 no?
saludos!!!

"conozco dos cosas infinitas: el universo y la estupidez humana. Y no estoy muy seguro del primero." A.Einstein

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #6 en: 29 de Abril de 2009, 13:11:47 »
Bueno, ya largué con una de las funciones, de a poco voy a ir explicando todas. Cualquier cosa q se me escape o me haya equivocado me lo hacen saber por favor.
Santi, creo q tu error esta acá:
for(i=(strlen(filename)-4);i<strlen(filename);i++)
deberia ser -3 no?
saludos!!!

Nop. Esa función está bien. Debe ser -4 porque cuento con todo y el punto: ".JPG". Y es -5 para el caso de ".JPEG".

Por ahí anda el error todavía, dale otra checada.  :D

Me costó trabajo pero ya conseguí la estrellita   ¿Quién la quiere?
« Última modificación: 29 de Abril de 2009, 13:43:57 por migsantiago »

Desconectado gera

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2188
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #7 en: 29 de Abril de 2009, 13:56:54 »
Tenes razón, se me escapó el hecho de q filename[0] es el primer caracter jeje. Creo q el error es la definición de "temporal"
char temporal[4];
Deberia ser de tamaño 5, ya que ".jpg" son 5 caracteres incluyendo el nulo.
A todo esto, no es mas corto hacer un strstr(filename,".jpg");? jejeje. Lo voy a poner como ejemplo cuando haga en analisis de esa funcion ;)
saludos!

"conozco dos cosas infinitas: el universo y la estupidez humana. Y no estoy muy seguro del primero." A.Einstein

Desconectado AKENAFAB

  • Colaborador
  • DsPIC30
  • *****
  • Mensajes: 3227
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #8 en: 29 de Abril de 2009, 14:11:16 »

A la!

 :-/

Que buen temazo , hasta ahora vengo entendiendo  xD

No se donde esra el error xD asi que diremos entendo 2 de 4 xD

Asi que me quedo perfecto para un programa que desarrollo y se porque va el null y ese rollo del sprintf , que siempre he sido piedra con el C , sino es por los amigos.


Un Saludo y gracias!

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #9 en: 29 de Abril de 2009, 16:54:48 »
Tenes razón, se me escapó el hecho de q filename[0] es el primer caracter jeje. Creo q el error es la definición de "temporal"
char temporal[4];
Deberia ser de tamaño 5, ya que ".jpg" son 5 caracteres incluyendo el nulo.
A todo esto, no es mas corto hacer un strstr(filename,".jpg");? jejeje. Lo voy a poner como ejemplo cuando haga en analisis de esa funcion ;)
saludos!

¿Qué pasó Gera? Noooo.

El error está ahí pero temporal no debe medir 5. Debe ser:

char temporal[6];

Para que quepa ".JPEG" + NULL.

Bueno, me guardo la estrellita  :D

Sobre las mejorías que se harían al algoritmo, tienes razón se pueden hacer, pero hace 1 año que lo escribí apenas entendía esto de las cadenas y no me aventuraba a usar las funciones de string.h.  :P

Desconectado gera

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2188
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #10 en: 29 de Abril de 2009, 20:09:22 »
De una, es muy buen ejercicio hacer este tipo de funciones. Cuando yo vi arreglos en C, mi profe nos hizo reescribir toda la libreria string.h como ejercicio jeje.
en fin, mas tarde sigo con la explicacion. Saludos!!!

"conozco dos cosas infinitas: el universo y la estupidez humana. Y no estoy muy seguro del primero." A.Einstein

Desconectado Cryn

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4169
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #11 en: 29 de Abril de 2009, 23:47:57 »
geniales explicaciones vi una libreria string.h pero para usarlo en la universidad cuando programaba en c++

y siempre tuve algunas dudas con las cadenas en CCS, gracias por el Tutorial

 :-/ :-/
.

Desconectado El_Guitre

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1046
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #12 en: 30 de Abril de 2009, 00:45:11 »
Migsantiago, respecto al error del ejemplo 3, al finalizar el bucle for cuando pones temporal[j]=0; para agregarle el NULL al final de la cadena, no debería ser temporal[j++]=0; para no sobrescribir el ultimo caracter que cargaste en el for? O estoy muy errado? Saludos.

Desconectado gera

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2188
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #13 en: 30 de Abril de 2009, 01:17:26 »
Guitre, esa parte del programa esta bien. El j++ significa q j se tiene q incrementar al terminar de ejecutar esa linea. Por lo tanto si el ultimo caracter a copiar era el quinto, este se iba a copiar, y ademas j iba a quedar en 6. Por lo tanto el caracter nulo iba a quedar en la sexta posicion sin pisar nada. Caso contrario si ponemos ++j. Esto primero incrementa j, y luego ejecuta la linea.
Saludos!!

"conozco dos cosas infinitas: el universo y la estupidez humana. Y no estoy muy seguro del primero." A.Einstein

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Tutorial - Cadenas y arreglos de chars
« Respuesta #14 en: 30 de Abril de 2009, 12:44:46 »
Ampliando la explicación de Gera, explico la enorme diferencia entre ++i y i++.

A muchos nos dicen erróneamente en la escuela que ambos son iguales pero no es cierto.

++i
Al ser invocado se incrementa el contenido de i y luego se usa su valor. Ejemplo:

Código: [Seleccionar]
int i=0;

printf("i vale %u",i);
printf("i vale %u",++i);
printf("i vale %u",i);

La salida del código es

Código: [Seleccionar]
i vale 0
i vale 1
i vale 1

i++
Al ser invocado primero se usa el valor actual de i y luego se incrementa. Ejemplo:

Código: [Seleccionar]
int i=0;

printf("i vale %u",i);
printf("i vale %u",i++);
printf("i vale %u",i);

La salida es:

Código: [Seleccionar]
i vale 0
i vale 0
i vale 1

El caso aplica de la misma forma con --i y i--.
« Última modificación: 30 de Abril de 2009, 12:51:27 por migsantiago »


 

anything