\chapter{Uso de cadenas de texto}

\section{Las cadenas como arreglos}

Recordando la presentación de arreglos hecha en donde las cadenas están definidas como un arreglo de caracteres o un apuntador a una porción de memoria conteniendo caracteres ASCII. Una cadena en C es una secuencia de cero o más caracteres seguidas por un carácter NULL o 

\begin{verbatim} 
\0
\end{verbatim} :

\begin{tabular}{|c|c|c|c|c|c|c|c|c|}
  \hline
F & A & C & U & L & T & A & D &  $\setminus0$ \\
  \hline
\end{tabular}

La cual se puede declarar como

\begin{verbatim}
char a[] = "FACULTAD"

char b[9] = "";

b[0] = 'F';
b[1] = 'A';
b[2] = 'C';
b[3] = 'U';
b[4] = 'L';
b[5] = 'T';
b[6] = 'A';
b[7] = 'D';
b[8] = '\0';

\end{verbatim}

Es importante preservar el carácter de terminación NULL, ya que con éste es como C define y maneja las longitudes de las cadenas. Todas las funciones de la biblioteca estándar de C lo requieren para una operación satisfactoria.

En general, aparte de algunas funciones restringidas en longitud (strncat(), strncmp() y strncpy()), al menos que se creen cadenas a mano, no se deberán encontrar problemas. Se deberán usar las funciones para manejo de cadenas y no tratar de manipular las cadenas en forma manual desmantelando y ensamblando cadenas.

\section{Implementación de algunas funciones para manejar cadenas}

En esta sección se muestran algunas funciones implementadas para ver el manejo de cadenas de caracteres y en la siguiente sección se muestra algunas funciones standard.

\subsection{Impresión de Cadenas como arreglos}

El siguiente es un ejemplo de como imprimir una cadena de caracteres con la función printf. Adicionalmente muestra el manejo de arreglos bidimensionales de caracteres.

\begin{verbatim}
/* 
 * File:   arreglos_cadenas.c
 * Author: felix
 *
 * Created on 3 de noviembre de 2011, 13:30
 */

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


void ejemplo_01() {
    char a[3][5] = {"Uno", "dos", "tres"};
    
    int i;
    
    for(i=0; i<3; i++)
        printf("%s\n", a[i]);
}

int main(int argc, char** argv) {
    int i;
    
    if(argc > 1) 
        for(i=0; i<argc; i++)
            printf("%d %s\n", i, argv[i]);
 
    return (EXIT_SUCCESS);
}
\end{verbatim}

Note que los argumentos de main contienen información del número de parámetros argc y  las cadenas de caracteres dados en argv.

\subsection{Manejo de cadenas de caracteres con apuntadores}

Dado que las cadenas de caracteres son arreglos, estos pueden manejarse por apuntadores. El siguiente ejemplo muestra como se pueden almacenar las cadenas en un arreglo y en un apuntador.

\begin{verbatim}
// Ejemplo ec082.cpp

/**
*  Este programa muestra los diferentes formatos para i
*  imprimir caracteres y cadenas de caracteres
*/
#include <stdio.h>

int main()
{
	char a = 'A';
	char cadena[] = "Esto es una cadena de texto";
	char *cadenap = "Esto tambien es una cadena de texto";

	printf("%c\n", a);
	printf("%s\n", "Esto es una cadena de texto");
	printf("%s\n", cadena);
	printf("%s\n", cadenap);

	return 0;
}
\end{verbatim}

\subsection{Longitud de una cadena de Texto}

Dado que una cadena de texto tiene un terminador de cadena dado por '\\0', podemos hacer un ciclo para contar los caracteres de una cadena. Note que la condición de terminación es que el i-esimo carácter sea '\\0' tal como se muestra en el siguiente código.

\begin{verbatim}
int longitud(char *a) {
    int i, c=0;
    for(i=0; a[i]!= '\0'; i++)
        c++;
    return c;
}
\end{verbatim}

\noindent
ver cadenas\_texto.c

\section {Uso de las funciones proporcionadas por las librerías}

Todas las funciones para manejo de cadenas tienen su prototipo en:

\begin{verbatim}
#include <string.h>
\end{verbatim}

Las funciones m·s comunes son descritas a continuación:

\noindent
 \textbf{char *strcpy(const char *dest, const char *orig)} -- Copia la cadena de caracteres apuntada por orig (incluyendo el car·cter terminador \begin{verbatim} '\0'\end{verbatim} ) al vector apuntado por dest. Las cadenas no deben solaparse, y la de destino, debe ser suficientemente grande como para alojar la copia.

\noindent 
\textbf{int strcmp(const char *s1, const char *s2)} -- Compara las dos cadenas de caracteres s1 y s2. Devuelve un entero menor, igual o mayor que cero si se encuentra que zs1 es, respectivamente, menor que, igual a, o mayor que s2.

\noindent
\textbf{char *strerror(int errnum)} -- Devuelve un mensaje de error que corresponde a un número de error.

\noindent 
\textbf{int strlen(const char *s)} -- Calcula la longitud de la cadena de caracteres.

\noindent
\textbf{char *strncat(char *s1, const char *s2, size\_t n)} -- Agrega n caracteres de s2 a s1.

\noindent 
\textbf{int strncmp(const char *s1, char *s2, size\_t n)} -- Compara los primeros n caracteres de dos cadenas.

\noindent 
\textbf{char *strncpy(const char *s1, const char *s2, size\_t n)} -- Copia los primeros n caracteres de s2 a s1.


\noindent 
\textbf{strcasecmp(const char *s1, const char *s2)} -- versión que ignora si son mayúsculas o minúsculas de strcmp().

\noindent 
\textbf{strncasecmp(const char *s1, const char *s2, size\_t n)} -- versiÛn insensible a mayúsculas o minúsculas de strncmp() que compara los primeros n caracteres de s1.

El uso de muchas funciones es directo, por ejemplo:

\begin{verbatim}
char *s1 = "Hola"; char *s2; int longitud;

longitud = strlen("Hola");  /* long = 4 */ (void) strcpy(s2,s1);
\end{verbatim}

Observar que tanto strcat() y strcopy() regresan una copia de su primer argumento, el cual es el arreglo destino. Observar también que orden de los argumentos es arreglo destino seguido por arreglo fuente lo cual a veces es una situación para hacerlo incorrectamente.

La función strcmp() compara lexicográficamente las dos cadenas y regresa:


Menor que cero -- si s1 es léxicamente menor que s2; Cero -- si s1 y s2 son léxicamente iguales; Mayor que cero -- si s1 es léxicamente mayor que s2; Las funciones de copiado strncat(), strncmp() y strncpy() son versiones m·s restringidas que sus contrapartes más generales. Realizan una tarea similar, pero solamente para los primeros n caracteres. Observar que el carácter de terminación NULL podría ser violado cuando se usa estas funciones, por ejemplo:

\begin{verbatim}
char *s1 = "Hola"; char *s2 = 2; int longitud = 2;

(void) strncpy(s2, s1, longitud); /* s2 = "Ho" */

\end{verbatim}

donde s2 no tiene el terminador NULL.


\subsubsection {Búsqueda en cadenas}
La biblioteca también proporciona varias funciones de búsqueda en cadenas

\noindent 
\textbf{char *strchr(const char *s, int c)} -- Devuelve un puntero a la primera ocurrencia del carácter c en la cadena de caracteres s.

\noindent 
\textbf{char *strrchr(const char *s, int c)} -- Encuentra la última ocurrencia del caracter c en la cadena.

\noindent 
\textbf{char *strstr(const char *s1, const char *s2)} --Localiza la primera ocurrencia de la cadena s2 en la cadena s1.

\noindent 
\textbf{char *strpbrk(const char *s1, const char *s2)}-- Regresa un apuntador a la primera ocurrencia en la cadena s1 de cualquier carácter de la cadena s2, o un apuntador nulo si no hay un carácter de s2 que exista en s1.

\noindent 
\textbf{size\_t strspn(const char *s1, const char *s2)}-- Calcula la longitud del segmento inicial de s1 que consta únicamente de caracteres en s2.

\noindent 
\textbf{size\_t strcspn(const char *s1, const char *s2)} -- Regresa el número de caracteres al principio de s1 que no coinciden con s2.

\noindent 
\textbf{char *strtok(char *s1, const char *s2)} -- Divide la cadena apuntada a s1 en una secuencia de ``tokens'', cada uno de ellos esta delimitado por uno o m·s caracteres de la cadena apuntada por s2.

Las funciones strchr() y strrchr() son las más simples de usar, por ejemplo:

\begin{verbatim}
char *s1 = "Hola";
char *resp;

resp = strchr(s1,'l');
\end{verbatim}

Después de la ejecución, resp apunta a la localidad s1 + 2.

La función strpbrk() es una función más general que busca la primera ocurrencia de cualquier grupo de caracteres, por ejemplo:

\begin{verbatim}
char *s1 = "Hola"; char *resp;

res = strpbrk(s1,"aeiou");
\end{verbatim}

En este caso, resp apunta a la localidad s1 + 1, la localidad de la primera o.

La función strstr() regresa un apuntador a la cadena de búsqueda especificada o un apuntador nulo si la cadena no es encontrada. Si s2 apunta a una cadena de longitud cero (esto es, la cadena ``''), la función regresa s1. Por ejemplo:

\begin{verbatim}
char *s1 = "Hola"; char *resp;

resp = strstr(s1,"la");
\end{verbatim}

la cual tendrá resp = s1 + 2.

La función strtok() es un poco m·s complicada en cuanto a operación. Si el primer argumento no es NULL entonces la función encuentra la posición de cualquiera de los caracteres del segundo argumento. Sin embargo, la posición es recordada y cualquier llamada subsecuente a strtok() iniciará en esa posición si en estas subsecuentes llamadas el primer argumento no es NULL. Por ejemplo, si deseamos dividir la cadena s1 usando cada espacio e
imprimir cada ``token'' en una nueva línea haríamos lo siguiente:

\begin{verbatim}
char s1[] = "Hola muchacho grande"; char *t1;

for ( t1 = strtok(s1," ");
      t1 != NULL;
      t1 = strtok(NULL, " ") )
    printf("%s\n",t1);
\end{verbatim}

Se emplea un ciclo for en una forma no regular de conteo:

En la inicialización se llama a la función strtok() con la cadena s1. Se termina cuando t1 es NULL. Se esta asignando tokens de s1 a t1 hasta la terminación llamando a strtok() con el primer argumento NULL.

\subsubsection{Prueba de clasificación de caracteres: $<$ctype.h$>$}

El argumento de las funciones siguientes es un int cuyo valor debe ser EOF o representable por un unsigned char. El valor de retorno es un int. Las funciones regresan cero si el argumento satisface la condición y un  valor diferente de cero si no lo hace.

\noindent
\begin{tabular}{|c|c|}
  \hline
  % after \\: \hline or \cline{col1-col2} \cline{col3-col4} ...
  FunciÛn & DescripciÛn\\
  \hline
islower(c) & letra minúscula \\
isupper(c) & letra mayúscula \\
isalpha(c) & letra mayúscula o minúscula \\
isdigit(c) & dígito decimal \\
isalnum(c) & letra mayúscula o minúscula o dígito decimal\\
iscntrl(c) & carácter de control \\
isgraph(c) & carácter de impresión excepto espacio \\
isprint(c) & carácter de impresión incluyendo espacio\\
ispunct(c) & carácter de impresión excepto espacio, letra o dígito \\
isspace(c) & espacio, avance de línea, nueva línea, retorno de \\
           & carro, tabulador, tabulador vertical\\
isxdigit(c) & dígito hexadecimal\\
  \hline
\end{tabular}


Funciones de conversión:

int tolower(int c)  convierte c a letra minúscula

int toupper(int c)  convierte c a letra mayúscula


\subsubsection{Conversión de cadenas}

Existen unas cuantas funciones para convertir cadenas a enteros, enteros largos y valores flotantes, las cuales se encuentran en la librerÌa $<$stdlib.h$>$. Estas son:

\noindent 
double atof(const char *cadena) : Convierte una cadena a un valor flotante.

\noindent 
int atoi(const char *cadena) :  Convierte una cadena a un valor entero.

\noindent 
int atol(const char *cadena) : Convierte una cadena a un valor entero largo.

\noindent 
double strtod(const char *cadena, char **finap) : Convierte una cadena a un valor de punto flotante.

\noindent 
double strtol(const char *cadena, char *finap, int base) : Convierte una cadena a un entero largo de acuerdo a una base dada, la cual deber· estar entre 2 y 36 inclusive. 

\noindent 
unsigned long strtoul(const char *cadena, char *finap, int base) : Convierte una cadena a un entero largo sin signo.

Varias de las funciones se pueden usar en forma directa, por ejemplo:

\begin{verbatim}
char *cad1 = "100";
char *cad2 = "55.444";
char *cad3 = "1234";
char *cad4 = "123cuatro";
char *cad5 = "invalido123";
char *cad6 = "123E23Hola";
char *cad7;

int i; float f:

i = atoi(cad1);    /* i = 100 */
f = atof(cad2);    /* f = 55.44 */
i = atoi(cad3);    /* i =1234*/
i = atoi(cad4);    /* i = 123 */
i = atoi(cad5);    /* i =0 */
f = strtod(cad6, &cad7);  /* f=1.230000E+25 y cad7=hola*/
\end{verbatim}
Nota:

Los caracteres en blanco son saltados. Caracteres ilegales son ignorados. Si la conversión no puede ser hecha se regresa cero y errno es puesta con el valor ERANGE.


