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 )
CAPITULO ANTERIOR DEL CURSO |