CAPITULO 8: FUNCIONES DE MANEJO DE STRINGS

 

1. INTRODUCCION
Si bien ya hemos realizado variadas operaciones de manejo de string , dada su importancia, pues son cuando menos el medio de comunicación de los programas con el operador, trataremos acá de sintetizar los conceptos relativos a los mismos, y resumir aquellas funciones ya vistas, con el agregado de otras nuevas.
La mayoría de las que veremos a continuación, responden a la norma ANSI C, por lo que serán independientes del compilador que usemos. Estas tienen sus prototipos definidos en los archivos de encabezamiento stdio.h, stdlib.h, string.h y ctype.h.
Agregaremos tambien algunas que caen fuera de la norma, por lo que su portabilidad a otros compiladores distintos al que fueron extraidas, no es segura. Seran aquellas declaradas en Headers no citados arriba. Sin embargo, hoy en día practicamente todos los compiladores las traen ó tienen otras similares, con nombres parecidos. De cualquier forma, antes de compilar los ejemplos aquí suministrados, en caso de encontrarse alguna de estas, verifique con su manual de Libreria la existencia y compatibilidad de la misma.
Refresquemos, antes de comenzar, algunas de las características básicas de los strings. Estos pueden aparecer en un programa como una constante simbólica, de la forma siguiente:

#define TITULO "Capitulo 9"

en este caso, en cada lugar donde aparece TITULO se reemplazará esta constante simbólica por la DIRECCION de la C del texto con que fué definida .
Así, será correcto escribir:

char *p = TITULO ;

Recordemos tambien que en la memoria, el string se guardará de la siguiente forma:

 

Donde los números son el código ASCII que representa a cada caracter del string , en particular , note que 20 corresponde al espacio , terminandose con un NULL (código 0 ) .
A los efecttos prácticos, para las funciones de manejo de los mismos, es como si en realidad hubieramos memorizados directamente los caracteres:

 

El código ASCII de los caracteres imprimibles vá entre el 31 y el 127 , reservándose los códigos entre el 0 y 30 , para los caracteres de control (retorno de carro, avance de linea, tabulador, etc).
Si en cambio , hubieramos escrito el string de una manera ortográficamente más correcta :

#define TITULO "Capítulo 9"

(con la i acentuada) estaríamos introduciendo un caracter del conjunto ASCII Extendido , ya que su código supera a 127 y está representado por 173 .
Lo correcto en este caso sería definir , aunque muchos compiladores ya lo presuponen por omisión, para asegurar la portabilidad :

unsigned char *p = TITULO ;

de esta forma se garantiza que el alcance de la variable sea de 255 , ó en su defecto :

int *p = TITULO ;

Es correcto tambien declarar el puntero , y asignarlo posteriormente

char *p ;
p = TITULO ;

Esta asignación solo dá , al contenido del puntero la dirección del string global predefinido .
Sin embargo , si en lugar de un puntero deseamos usar un array , en este caso es correcta la inicialización del mismo , pero no así su asignación posterior:

char nombre[] = TITULO ;         /* Correcto */
.................
char nombre[11] ;
nombre = TITULO ;                  /* Incorrecto */

Ya que si bien, el nombre de un array es un puntero , es de índole constante , negándose el compilador a cambiar su dirección.
Si estuvieramos en el caso de ingresar un string variable , por ejemplo leyendolo desde el teclado , podríamos utilizar un array, de la siguiente forma :

char nombre[128] ;
scanf("%s" , nombre ) ;

en este caso la única precaución es que , el array tenga suficiente longitud para albergar a cualquier string escrito . En el caso de trabajar bajo DOS, basta con darle 128 caracteres, ya que el buffer de lectura de ese sistema operativo no supera dicha cantidad .
Hay que hacer notar que la longitud de un string puede ser mayor que la del texto válido contenido , ya que este termina donde se encuentra el NULL , quedando los bytes sobrantes desaprovechados .
Seria incorrecto leer este string mediante un puntero declarado , pero al que no se le ha reservado memoria:

char *p ;
scanf("%s" , p )      /* Incorrecto */

ya que la dirección contenida por p no ha sido inicializada aún con ningun valor válido . Lo correcto en éste caso es:

char *p ;
p = (char *)malloc(128 * sizeof(char)) ;
scanf("%s" , p )                           /* Correcto */

reservando memoria previamente a cargar el string.
Otro punto sobre el que quiero volver, a fín de evitar confusiones, es el sentido de la constante NULL , y el de variables nulas.
Segun éste se aplique a caracteres, strings ó punteros, su significado varia:

 

Hay que recalcar que, practicamente todas las funciones que describiremos a continuación , basan su operatoria en la suposición que los strings que se le pasan como argumento , terminan en el caracter NULL , si por error esto no fuera así , los resultados son catastróficos , produciendose generalmente la destrucción de los datos y el aborto del programa .

2. FUNCIONES DE IMPRESION DE STRINGS
Daremos un análisis de las funciones que permiten la impresión en pantalla de strings , muchas de ellas pueden obviamente , utilizarse para imprimir otro tipo de variable , pero aquí sólo describiremos su aplicación particular sobre el tema de nuestro interes.

PRINTF()


              ...........
              p = "Lenguaje C" ;    /* 10 caracteres */
              ...........
              printf("|%15s|" , p )     ;    /* imprime : |     Lenguaje C|  */
              printf("|%15.8s|" , p )   ;    /*    "    : |       Lenguaje|  */
              printf("|%-15s|" , p )    ;    /*    "    : |Lenguaje C     |  */
              printf("|%-15.8s|" , p )  ;    /*    "    : |Lenguaje       |  */
              printf("|%.6s|" , p )     ;    /*    "    : |Lengua|           */
              ancho = printf("|%15s|" , p ); /* imprime : |     Lenguaje C|  */
              printf("|%*.8s|" , p , ancho); /*    "    : |       Lenguaje|  */

Existe otra función más específica que la anterior , aunque más restrigida , puts() .

PUTS()


              #include <stdio.h>
              main()
              {
              char p[] = "Uno" , s[] = "Dos" ;
              puts(p) ;
              puts(s) ;
              }
              /* imprime :    Uno
                              Dos       */

3. FUNCIONES DE ADQUISICION DE STRING
Cuando se necesita leer un string enviado desde el teclado , se utilizará alguna de las funciones abajo citadas , debiendose tener los recaudos descriptos antes , ya que la longitud del mismo es desconocida.

SCANF()



De la misma manera que para printf(), hay funciones menos generales, dedicadas expresamente a la lectura de strings, como gets(), que veremos a continuación .

GETS()



4. FUNCIONES DE CONVERSION ENTRE STRING Y VARIABLES NUMERICAS
Puede darse el caso que la información a ingresarse a un programa ejecutable , por el operador pueda ser en algunos caso un valor numérico y en otros un string de caracteres . Un problema típico enfrentamos en el ejemplo en que ingresabamos a nuestra base de datos un articulo , ya sea por su nombre ó por su número de código .
Más cómodo que escribir dos instancias del programa , una para cada una de las opciones , es dejar que el operador escriba lo que se le venga en ganas , leyendolo como un string , luego verificar si éste está compuesto exclusivamente por números ó posee algun caracter nó numérico , y actuar en consecuencia .
Para evaluar si un string cae dentro de una categoría dada , es decir si está compuesto exclusivamente por números , letras, mayúsculas , minúsculas , caracteres alfanuméricos , etc existen una serie de funciones , algunas de las cuales ya hemos usado, que describimos a continuación . Estas deben ser usadas con los strings , analizando caracter a caracter de los mismos, dentro de un FOR ó un WHILE:

for(i=0 ; palabra[i] != NULL ; i++) {
 if( isalnum(palabra[i] )
  .........................
 }

 

IS.....()


             int isalnum( int  c )
             int isalpha( int  c )
             int isascii( int  c )
             int iscntrl( int  c )
             int isdigit( int  c )
             int islower( int  c )
             int isupper( int  c )
             int ispunct( int  c )
             int isspace( int  c )
             int isxdigit( int  c )

 

La Función Retorna CIERTO si c es :
isalnum(c) Alfanumérico ( letras ó números )
isalpha(c) Alfabetico , mayúscula ó minúscula
isascii(c) Si su valor está entre 0 y 126
iscntrl(c) Si es un caracter de control cuyo ASCII está comprendido entre 0 y 31 ó si es el código de "delete" , 127 .
islower(c) Si es un caracter alfabético minuscula.
isupper(c) Si es un caracter alfabético mayúscula
isdigit(c) Si es un número comprendido entre 0 y 9
ispunct(c) Si es un caracter de puntuación
isspace(c) Si es el caracter espacio, tabulador, avance de línea, retorno de carro, etc.
isxdigit(c) Si es código correspondiente a un número hexadecimal, es decir entre 0 - 9 ó A - F ó a - f .

 


Una vez que sabemos que un string está compuesto por números , podemos convertirlo en una variable numérica , de cualquier tipo , para poder realizar con ella los cálculos que correspondan .

ATOI() , ATOL() , ATOF()


            int    atoi( const char *s )
            long   atol( const char *s )
            double atof( const char *s )
[espacios , blancos , tabulador , etc] [signo] xxx

donde xxx son caracteres entre 0 y 9 , para atoi() y atol() . Para atof() en cambio , se aceptan :

[espacios , etc] [signo] xxx [.] [ xxx]   ó
[espacios , etc] [signo] xxx [.] [ xxx] [ e ó E [signo] xxx ]

según se desee usar la convención de punto flotante ó cientifica.


Es posible tambien , aunque menos frecuente , realizar la operación inversa, es decir, convertir un número en un string.

ITOA() , ULTOA()


               char  *itoa( int numero , char *s , int base )
               char *ultoa( unsigned long numero , char *s , int base )

 

5. DETERMINACION DE LA LONGITUD DE UN STRING
Hemos aplicado anteriormente esta función, damos aquí entonces , sólo una ampliación de sus caracteristicas.

STRLEN() , _FSTRLEN


          size_t  strlen( const char *s )
          size_t far _fstrlen( const char far *s )
           ..............
           char s[128] ;
           gets(s) ;
           p = (char *)malloc( sizeof( strlen(s) + 1 ) ;

6. COPIA Y DUPLICACION DE STRINGS
Vimos que el operador de asignación no está definido para strings , es decir que hacer p = q , donde p y q son dos arrays , no produce la copia de q en p y directamente la expresión no es compilada . Si en cambio p y q son dos punteros a caracteres , la expresión es compilada , pero no produce el efecto de copia , simplemente , cambia el valor de p , haciendo que apunte al MISMO string que q . Es decir que no se genera uno nuevo , por lo que todo lo operado sobre p afectará al original , apuntado por q .
Para generar entonces , una copia de un string en otro lugar de la memoria , se deben utilizar alguna de las funciones abajo descriptas . Hay que diferenciar la copia de la duplicacion : la primera copia un string sobre un lugar PREVIAMENTE reservado de memoria ( mediante malloc() , calloc() ó alguna otra función función de alocación ) , en cambio la duplicación GENERA el espacio para guardar al nuevo string así creado.

STRCPY()



Existe tambien una función para realizar la copia PARCIAL . Por lo general las funciones que realizan acciones sobre una parte solamente , de los strings , llevan el mismo nombre de las que los afectan totalmente , pero con la adición de la letra "n".

STRNCPY()


               #include <string.h>
               main()
               {
               char strvacio[11] ;
               char strorigen[] =  "0123456789" ;
               char strdestino[] = "ABCDEFGHIJ" ;
               ..................
               strncpy( strdestino , strorigen , 5 ) ;
               strncpy( strvacio , strorigen , 5 ) ;
               strvacio[5] = '\0' ;
               .........................
               }

Los strings quedarían , luego de la copia :

      strdestino[] == 0 , 1 , 2 , 3 , 4 , F , G , H , I , J , \0
      strvacio[]   == 0 , 1 , 2 , 3 , 4 , \0 , indefinidos

Note que en el caso de strdestino no hizo falta agregar el NULL , ya que éste se generó en la incialización del mismo , en cambio strvacio no fué inicializado , por lo que para terminar el string , luego de la copia , se deberá forzosamente agregar al final del mismo.


La función siguiente permite la duplicación de strings :

STRDUP()


              #include <string.h>
              main()
              {
              char *p ;
              char q[] = "Duplicación de strings" ;
              p = strdup( q ) ;
              ..................
              }

Note que el retorno de la función debe ser siempre asignado a un dado puntero .


7. CONCATENACION DE STRINGS
Se puede, mediante la concatenación de dos ó más strings , crear otro , cuyo contenido es el agregado del de todos los anteriores .
La concatenación de varios strings puede anidarse , de la siguiente manera :

strcat( strcat(x , w) , z ) ;

en la cual al x se le agrega a continuación el w , y luego el z . Por supuesto x tiene que tener suficiente longitud como para albergarlos .

STRCAT()


              #include <string.h>
              char z[20] ;
              main()
              {
              char p[20] ;
              char q[] =   "123456789" ;
              char w[] =   "AB" ;
              char y[20] = "AB" ;
              strcat( y , q ) ;      /* Correcto , el contenido de y[] será:
                                         y[] == A,B,1,2,3,4,5,6,7,8,9,\0  */
              strcat( z , q ) ;      /* Correcto , por ser global z[] quedó 
                                         inicializado con 20 NULLS por lo que
                                         luego de la operación quedará:
                                         z[] == 1,2,3,4,5,6,7,8,9,\0
              strcat( p , q ) ;      /* Error ! p no ha sido inicializado por
                                         lo que la función no encuentra el NULL
                                         para empezar a agregar , por lo que
                                         barre la memoria hasta encontrar 
                                         alguno, y ahí escribe con resultados,
                                         generalmente catastróficos.
              strcat( w , q ) ;      /* Error ! w solo tiene 3 caracteres, 
                                         por lo el resultado final será:
                                         w[] == A,B,1  sin la terminación
                                         del NULL por lo que cualquier
                                         próxima operación que se haga
                                         utilizando este array, como string,
                                         fallará rotundamente .
               {

STRNCAT()



8. COMPARACION DE STRINGS
No debe confundirse la comparación de strings , con la de punteros , es decir

if(p == q) {
............

sólo dará CIERTO cuando ambos apunten al MISMO string , siempre y cuando dichos punteros sean " near " ó " huge " . El caso que acá nos ocupa es más general , ya que involucra a dos ó más strings ubicados en distintos puntos de la memoria ( abarca el caso anterior , como situación particular).

STRCMP()


              <  0 si s1 es menor que s2
              == 0 si s1 es igual a s2
              >  0 si s1 es mayor que s2

La comparación se realiza caracter a caracter , y devuelve el resultado de la realizada entre los primeros dos que sean distintos.
La misma se efectua tomando en cuenta el código ASCII de los caracteres así será por ejemplo '9' < 'A' , 'A' < 'Z y 'Z' < 'a' .



STRCMPI()




STRNCMP() , STRNCMPI()



9. BUSQUEDA DENTRO DE UN STRING
Muchas veces dentro de un programa , se necesita ubicar dentro de un string , a un determinado caracter ó conjunto ordenado de ellos . Para simplificarnos la tarea existen una serie de funciones de Librería , que toman por su cuenta la resolución de este problema :

STRCHR() Y STRRCHR()


                       char *strchr( const char *s1 , int c )
                       char *strrchr( const char *s1 , int c )

STRBRK()



STRSTR()



STRTOK()


              puts("escriba una frase , separando las palabras con espacios") ;
              gets(s) ;
              p = strtok(s , " ") ;
              while(p) {
                puts(p) ;
                p = strtok( NULL , " ") ;
               }

10. FUNCIONES DE MODIFICACION DE STRING
Resulta conveniente a veces uniformizar los string leidos de teclado, antes de usarlos, un caso típico es tratar de independizarse de la posibilidad de que el operador escriba, algunas veces con mayúscula y otras con minúscula.

STRLWR() Y STRUPR()


                       char *strlwr( char *s1 )
                       char *strupr( char *s1 )

FIN DE CURSO

CAPITULO ANTERIOR DEL CURSO


Other links: Nature Posters and art prints