elwear
Usuario (Argentina)
![[Programación C/C++] Funciones para cadenas o cstring](https://storage.posteamelo.com/assets-adonis/assets/2017/05/02/grin-9-vCNe3_RzO.webp)
Cadenas char, también conocidas como "arreglos de char", "cadenas", o "cstring". Detesto trabajar con ellas y supongo que muchos de ustedes también . Para hacernos la vida un poco más fácil les traigo este post para que les sirva como referencia, donde voy a listar y explicar las funciones que vienen incluídas en la biblioteca string.h (que viene de C) que nos van a simplificar bastante el trabajo a la hora de trabajar con cadenas de texto. La primera parte del post es una lista simple con las funciones, y en la segunda parte se explican a fondo. La parte de las referencias es algo extensa, así que les recomiendo ubicar la función que necesitan en la lista simple, apretar Ctrl+F para abrir el panel de búsqueda de su navegador y buscar la referencia fácilmente a través del nombre de la función. LISTA DE FUNCIONES: DE COPIA: strcpy() Copiar una cadena y pegarla sobre otra. strncpy() Copia los primeros "n" caracteres de una cadena a otra. DE CONCATENACIÓN: strcat() Concatena dos cadenas. strncat() Concatena los primeros "n" caracteres de una cadena al final de otra. DE COMPARACIÓN: strcmp() Compara si dos cadenas son iguales. strncmp() Compara si los primeros "n" caracteres de una cadena son iguales a los primeros "n" caracteres de otra. DE BÚSQUEDA: strchr() Busca en la cadena la primera ocurrencia de un caracter (char) indicado. strrchr() Busca en la cadena la última ocurrencia de un caracter (char) indicado. strcspn() Busca en una cadena la primera ocurrencia de cualquiera de los caracteres de otra cadena y retorna la posición de la misma. strpbrk() Busca en una cadena la primera ocurrencia de cualquiera de los caracteres de otra cadena y retorna un puntero a char con la subcadena formada desde esa posición. strspn() Retorna el largo de la porción inicial de una cadena que está formada solamente por los caracteres presentes en otra. strstr() Busca una cadena dentro de otra cadena, retorna un puntero a la subcadena. strtok() Divide una cadena en segmentos (o tokens) delimitados por los caracteres char que indiquemos. OTROS: strerror() Retorna una cadena que describe un error producido en el programa (pura magia). strlen() Retorna el largo de una cadena. DETALLES DE CADA FUNCIÓN: size_t strlen ( const char * cadena ); Devuelve el largo de "cadena" (el tipo size_t es como un unsigned int). El largo de la cadena es determinado por el caracter nulo o de fin de cadena (barra invertida cero): una cadena o cstring es tan largo como caracteres haya antes del caracter de fin de cadena (sin incluirlo). Esto no debe confundirse con el tamaño de la cadena como arreglo. Por ejemplo: char micadena[100] = "probando cadena"; define un arreglo de char con un tamaño de 100 chars, pero la cadena con la cual micadena ha sido inicializada tiene un tamaño de 15 caracteres. Por lo tanto, mientras que sizeof(micadena) devuelve 100, strlen(micadena) retorna 15. Valor de retorno: el largo de la cadena. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char entrada[256]; printf("Introduzca una frase: "); gets( entrada ); printf( "La frase ingresada tiene %d caracteres de largo.\n", strlen( entrada ) ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char entrada[256]; cout<<"Introduzca una frase: "; // El método getline() de cin sirve para leer cadenas con espacios. // sus argumentos son: el arreglo de char donde almacenar la entrada y // el tamaño de dicho arreglo (obtenido con sizeof). cin.getline( entrada, sizeof( entrada ) ); cout<<"La frase ingresada tiene "<<strlen( entrada )<<" caracteres de largo.\n"; return 0; } Salida: Introduzca una frase: Hola mundo! La frase ingresada tiene 11 caracteres de largo. char * strcpy ( char * destino, const char * fuente ); Copia la cadena apuntada por "fuente" al arreglo apuntado por "destino", insertando por su cuenta el caracter de fin de cadena (barra invertida cero). Hay que destacar que para que el programa no explote al realizar esta operación, el arreglo "destino" tiene que tener un tamaño de por lo menos la cantidad de caracteres que haya en "fuente" + 1 (espacio para el caracter de fin de cadena) o dicho de otra forma: tamaño mínimo de destino debe ser >= strlen( fuente ) + 1. Valor de retorno: puntero a "destino" . Ejemplos de uso: /* strcpy ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char cad1[] = "cstring de ejemplo"; char cad2[40]; char cad3[40]; strcpy( cad2, cad1 ); strcpy( cad3,"copia exitosa" ); printf( "cad1: %s\ncad2: %s\ncad3: %s\n", cad1, cad2, cad3 ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char cad1[] = "cstring de ejemplo"; char cad2[40]; char cad3[40]; strcpy( cad2, cad1 ); strcpy( cad3,"copia exitosa" ); cout<<"cad1: "<<cad1<<"\ncad2: "<<cad2<<"\ncad3: "<<cad3; return 0; } Salida: cad1: cstring de ejemplo cad2: cstring de ejemplo cad3: copia exitosa char * strncpy ( char * destino, const char * fuente, size_t cuantos ); Copia los primeros "cuantos" caracteres de "fuente" a "destino". Si el final de la cadena "fuente" (indicado por el caracter de fin de cadena 'barra invertida cero' ) es alcanzado antes de que se copien la cantidad de caracteres indicados por "cuantos", el arreglo "destino" se llena con ceros hasta que una cantidad de "cuantos" caracteres hayan sido escritos en él. Valor de retorno: puntero a "destino" . Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char cad1[]= "Ser o no ser"; char cad2[40]; char cad3[40]; /* Copiar la cantidad máxima permitida por cad2 */ strncpy( cad2, cad1, 40 ); /* copia parcial (solamente 5 chars): */ strncpy( cad3, cad2, 5 ); cad3[5] = '\0'; /* caracter de fin de cadena agregado manualmente */ puts( cad1 ); puts( cad2 ); puts( cad3 ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { const int tamanyo = 40; char cad1[]= "Ser o no ser"; char cad2; char cad3; /* Copiar la cantidad máxima permitida por cad2 */ strncpy( cad2, cad1, tamanyo ); /* copia parcial (colamente 5 chars): */ strncpy( cad3, cad2, 5 ); cad3[5] = '\0'; /* caracter de fin de cadena agregado manualmente */ cout<<cad1<<endl; cout<<cad2<<endl; cout<<cad3<<endl; return 0; } Salida: Ser o no ser Ser o no ser Ser o char * strcat ( char * destino, const char * fuente ); Pega una copia de la cadena "fuente" al final de la cadena "destino". El "pegado" se produce desde la posición del caracter nulo o de fin de cadena (barra invertida cero) en "destino", sobreescribiéndolo con el primer caracter de "fuente" y continuando. Un nuevo caracter de fin de cadena es insertado al final del proceso. Como con strcpy(), hay que resaltar que el arreglo "destino" tiene que tener un tamaño suficiente para anexar su contenido a la cadena "fuente", es decir: tamaño de destino >= strlen( destino ) + strlen( fuente ) + 1. Valor de retorno: un puntero a "destino". Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char cad[80]; char cad2[] = "estan "; strcpy( cad, "estas " ); strcat( cad, "cadenas " ); strcat( cad, cad2 ); strcat( cad, "concatenadas." ); puts( cad ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char cad[80]; char cad2[] = "estan "; strcpy( cad, "estas " ); strcat( cad, "cadenas " ); strcat( cad, cad2 ); strcat( cad, "concatenadas." ); cout<<cad; return 0; } Salida: estas cadenas estan concatenadas. char * strncat ( char * destino, const char * fuente, size_t cuantos ); Pega los primeros "cuantos" caracteres de fuente al final de "destino", agregando solo el caracter de fin de cadena. Si el tamaño de la cadena "fuente" es menor a la cantidad de caracteres "cuantos", se copiará y pegará solamente los caracteres que haya hasta llegar al caracter de fin de cadena de "fuente". Se deben tener las mismas precauciones de tamaños de arreglos que las indicadas para strcat(). Valor de retorno: puntero a "destino". Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char cad1[20]; char cad2[20]; strcpy( cad1, "Ser " ); strcpy( cad2, "o no ser" ); strncat( cad1, cad2, 4 ); puts( cad1 ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char cad1[20]; char cad2[20]; strcpy( cad1, "Ser " ); strcpy( cad2, "o no ser" ); strncat( cad1, cad2, 4 ); cout<<cad1; return 0; } Salida: Ser o no int strcmp ( const char * cad1, const char * cad2 ); Compara la cadena "cad1" con la cadena "cad2". Esta función comienza comparando el primer caracter de cada cadena. Si estos caracteres son iguales, continua con el siguiente par hasta que los caracteres comparados son distintos o se alcanza un caracter nulo o de fin de cadena. Valor de retorno: retorna un valor int que indica la relación entre las dos cadenas: - Un valor 0 (cero) indica que las dos cadenas son iguales. - Un valor mayor a cero indica que el primer caracter encontrado que es distinto tiene un valor mayor en cad1 que en cad2. - Un valor menor a cero indica lo contrario a lo anterior. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char clave[] = "manzana\n"; char entradaClave[80]; do { printf( "Cual es mi fruta preferida? " ); fgets( entradaClave, 80, stdin ); } while ( strcmp( clave, entradaClave ) != 0 ); puts( "Respuesta correcta!" ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char clave[] = "manzana"; char entradaClave[80]; do { cout<<"Cual es mi fruta preferida? "; cin.getline( entradaClave, 80 ); } while ( strcmp( clave, entradaClave ) != 0 ); cout<<"Respuesta correcta!"; return 0; } Salida: Cual es mi fruta preferida? pera Cual es mi fruta preferida? manzana Respuesta correcta! int strncmp ( const char * cad1, const char * cad2, size_t cuantos ); Compara los primeros "cuantos" caracteres de la cadena "cad1" con los primeros "cuantos" caracteres de la cadena "cad2". Esta función comienza comparando el primer caracter de cada cadena. Si estos caracteres son iguales, continua con el siguiente par hasta que los caracteres comparados son distintos, o se alcanza un caracter nulo o de fin de cadena, o hasta que "cuantos" caracteres ah sido comparados y encontrados iguales, lo que pase primero. Valor de retorno: retorna un valor int que indica la relación entre las dos cadenas: - Un valor 0 (cero) indica que las dos cadenas son iguales. - Un valor mayor a cero indica que el primer caracter encontrado que es distinto tiene un valor mayor en cad1 que en cad2. - Un valor menor a cero indica lo contrario a lo anterior. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char str[][5] = { "R2D2" , "C3PO" , "R2A6" }; int n; puts ("Buscando astromech droids R2..."); for( n=0 ; n<3; n++ ) { if ( strncmp ( str, "R2xx", 2) == 0 ) { printf ("Encontrado: %s\n", str ); } } return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char str[][5] = { "R2D2" , "C3PO" , "R2A6" }; cout<<"Buscando astromech droids R2..."<<endl; for( int n=0 ; n<3; n++ ) { if ( strncmp ( str, "R2xx", 2) == 0 ) { cout<<"Encontrado: "<<str<<endl; } } return 0; } Salida: Buscando astromech droids R2... Encontrado: R2D2 Encontrado: R2A6 const char * strchr ( const char * cad, int letra ); /* sólo en C++ */ char * strchr ( char * cad, int letra ); /* en C y C++ */ Nota: por más que el prototipo diga "int", "letra" es un char; lo que pasa es que todos los char en realidad son valores numéricos int que corresponden al número del código ascii cuyo valor representan. La función empieza a buscar el caracter "letra" en la cadena "cad". Si lo encuentra, devuelve un puntero a la posición de la cadena donde lo encontró, es decir, devuelve un puntero a una subcadena dentro de la cadena que comienza con el caracter "letra". El caracter de fin de cadena también se considera parte de la cadena, por lo tanto, también puede ser buscado de esta forma para obtener un puntero al final de la cadena. Valor de retorno: Un puntero a la primera ocurrencia de "letra" en "cad". Si el caracter no es encontrado, devuelve NULL. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char str[] = "Esta es una cadena de ejemplo"; char * pch; printf( "Buscando el caracter 's' en \"%s\"...\n\n", str ); pch = strchr( str, 's' ); while( pch != NULL ) { printf( "Encontrado en la posicion %d\n", pch-str+1 ); printf( "El puntero retornado apunta a: %s\n", pch ); /* busca desde después del último encontrado */ pch = strchr( pch+1, 's' ); } return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char str[] = "Esta es una cadena de ejemplo"; char * pch; cout<<"Buscando el caracter 's' en \""<<str<<"\"...\n\n"; pch = strchr( str, 's' ); while( pch != NULL ) { cout<<"Encontrado en la posicion "<<pch-str+1<<endl; cout<<"El puntero retornado apunta a: "<<pch<<endl; /* busca desde después del último encontrado */ pch = strchr( pch+1, 's' ); } return 0; } Salida: Buscando el caracter 's' en "Esta es una cadena de ejemplo"... Encontrado en la posicion 2 El puntero retornado apunta a: sta es una cadena de ejemplo Encontrado en la posicion 7 El puntero retornado apunta a: s una cadena de ejemplo const char * strrchr ( const char * cad, int letra ); /* sólo en C++ */ char * strrchr ( char * str, int character ); /* en C y C++ */ Retorna un puntero a la última ocurrencia de "letra" en "cad". El caracter de fin de cadena es considerado parte de la cadena, por lo que puede ser buscado para obtener un puntero al final de la misma. Valor de retorno: Un puntero a la última ocurrencia de "letra" en "cad". Si el caracter no es encontrado, devuelve NULL. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char str[] = "Esta es una cadena de prueba"; char * pch; pch = strrchr( str, 's' ); printf( "La ultima ocurrencia de 's' fue encontrada en la posicion %d \n", pch-str+1 ); printf( "El puntero retornado apunta a: %s \n", pch ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char str[] = "Esta es una cadena de prueba"; char * pch; pch = strrchr( str, 's' ); cout<<"La ultima ocurrencia de 's' fue encontrada en la posicion "<<pch-str+1<<endl; cout<<"El puntero retornado apunta a: "<<pch; return 0; } Salida: La ultima ocurrencia de 's' fue encontrada en la posicion 7 El puntero retornado apunta a: s una cadena de prueba size_t strcspn ( const char * cad1, const char * cad2 ); La función busca dentro de "cad1" la existencia de cualquiera de los caracteres contenidos en "cad2", retornando la cantidad de caracteres que hay en "cad1" antes de la primera ocurrencia de estos valores. La búsqueda incluye el valor de fin de cadena. Por lo tanto, la función retornará el largo de "str1" si ninguno de los caracteres contenidos en "str2" fueron encontrados en "str1". Valor de retorno: El largo de la parte inicial de "str1" que no contiene ninguno de los caracteres que componen a "str2". Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char cad[] = "fcba73"; char claves[] = "1234567890"; int i; i = strcspn( cad, claves ); printf( "El primer numero en cad esta en la posicion %d.\n", i+1 ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char cad[] = "fcba73"; char claves[] = "1234567890"; int i = strcspn( cad, claves ); cout<<"El primer numero en cad esta en la posicion "<<i+1; return 0; } Salida: El primer numero en cad esta en la posicion 5 const char * strpbrk ( const char * cad1, const char * cad2 ); /* sólo en C++ */ char * strpbrk ( char * cad1, const char * cad2 ); /* en C y C++ */ Retorna un puntero a la primera ocurrencia en "cad1" de cualquiera de los caracteres contenidos en "cad2", o un puntero a NULL si no hubo coincidencias. La búsqueda no incluye al caracter de fin de cadena de ninguno de los dos cstrings, pero termina ahí. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char cadena[] = "Esto es un cstring de prueba"; char claves[] = "aeiou"; char * pch; printf( "Vocales en '%s': ", cadena ); pch = strpbrk( cadena, claves ); while( pch != NULL ) { printf( "%c " , *pch ); pch = strpbrk( pch+1, claves ); } printf( "\n" ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char cadena[] = "Esto es un cstring de prueba"; char claves[] = "aeiou"; char * pch; cout<<"Vocales en '"<<cadena<<"': "; pch = strpbrk( cadena, claves ); while( pch != NULL ) { cout<<*pch<<" "; pch = strpbrk( pch+1, claves ); } return 0; } Salida: Vocales en 'Esto es un cstring de prueba': o e u i e u e a size_t strspn ( const char * cad1, const char * cad2 ); Retorna la longitud de la porción inicial de "cad1" que consiste solamente de caracteres que son parte de "cad2". La búsqueda no incluye el caracter de fin de cadena de ninguna de las dos cadenas, pero termina en ese punto. Valor de retorno: la longitud de la porción inicial de "cad1" que consiste solamente de caracteres que son parte de "cad2". Por lo tanto, si todos los caracteres de "cad1" están en "cad2" la función retorna el largo de "cad1", y si el primer caracter de "cad1" no está en "cad2" la función retorna cero. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { int i; char cadena[] = "129asd"; char numeros[] = "1234567890"; i = strspn( cadena, numeros ); printf( "El numero inicial de la cadena tiene %d digitos.\n", i ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char cadena[] = "129asd"; char numeros[] = "1234567890"; int i = strspn( cadena, numeros ); cout<<"El numero inicial de la cadena tiene "<< i <<" digitos."; return 0; } Salida: El numero inicial de la cadena tiene 3 digitos. const char * strstr ( const char * cad1, const char * cad2 ); /* sólo en C++ */ char * strstr ( char * cad, const char * cad2 ); /* en C y C++ */ Retorna un puntero a la primera ocurrencia de "cad2" (completa) en "cad1", o un puntero NULL si "cad2" no es parte de "cad1". El proceso de búsqueda y comparación no incluye al caracter de fin de cadena, pero se detiene al llegar al mismo. El siguiente ejemplo busca "simple" dentro de la cadena y lo reemplaza por "propia": /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char str[] ="Esta es una cadena simple"; char * pch; pch = strstr( str, "simple" ); strncpy( pch, "propia", 6 ); puts( str ); return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char str[] ="Esta es una cadena simple"; char * pch; pch = strstr( str, "simple" ); strncpy( pch, "propia", 6 ); cout<<str; return 0; } Salida: Esta es una cadena propia char * strtok ( char * cad, const char * delimitantes ); Divide una cadena en segmentos (o tokens). Una secuencia de llamadas a esta función divide "cad" en tokens, que son secuencias de caracteres contíguos separados por cualquiera de los caracteres contenidos en la cadena "delimitantes". En la primera llamada, la función espera un cstring como argumento para "cad", cuyo primer caracter es utilizado como el punto de partida para buscar tokens. En las llamadas siguientes, la función espera un puntero NULL y utiliza la posición inmediatamente siguiente al fina del último token como nuevo punto de partida para realizar la búsqueda. Para determinar el principio y el fin de un token, la función primero busca desde la posición inicial la ubicación del primer caracter en "cad" que no está contenido en la cadena "delimitantes" (que se convierte en el principio del token). Y entonces busca, partiendo desde ese principio del token, al primer caracter en "cad" contenido en "delimitantes", que se convierte en el final del token. La búsqueda también se detiene si se llega al caracter de fin de cadena. Este final del token es automáticamente reemplazado por el caracter de fin de cadena, y el principio del token es retornado como puntero por la función. Una vez que el caracter de fin de cadena en "cad" es encontrado por strtok(), todas las subsecuentes llamadas a esta función (con un puntero NULL como primer argumento) retornarán un puntero NULL. El punto donde el último token fue encontrado se mantiene guardado internamente por la función para ser usado en la siguiente llamada. Valor de retorno: Un puntero al último token encontrado en "cad". Un puntero NULL si no hay más tokens por retirar. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> int main () { char cad[] ="- Esta, una cadena de prueba."; char * pch; printf( "Partiendo la cadena \"%s\" en tokens:\n\n", cad ); pch = strtok( cad, " ,.-" ); while( pch != NULL ) { printf( "%s\n", pch ); pch = strtok( NULL, " ,.-" ); } return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> using namespace std; int main () { char cad[] ="- Esta, una cadena de prueba."; char * pch; cout<<"Partiendo la cadena \""<<cad<<"\" en tokens:\n\n"; pch = strtok( cad, " ,.-" ); while( pch != NULL ) { cout<< pch <<endl; pch = strtok( NULL, " ,.-" ); } return 0; } Salida: Partiendo la cadena "- Esta, una cadena de prueba." en tokens: Esta una cadena de prueba char * strerror ( int errnum ); Devuelve un puntero a una cadena que contiene un mensaje sobre un error producido. La función interpreta el valor de errnum, generando una cadena con un mensaje que describe la condición del error como fue descrita en errno por una función de la librería. El puntero retornado apunta a una cadena estática, que no deberá ser modificada por el programa. Subsecuentes llamadas a esta función pueden sobrescribir su contenido. La cadena producida por strerror() puede ser específica para cada sistema e implementaciones de la librería. Valor de retorno: Un puntero a una cadena que describe el error cuyo código representa errnum. Ejemplos de uso: /* ejemplo en C */ #include <stdio.h> #include <string.h> #include <errno.h> int main () { FILE * pFile; pFile = fopen( "inexistente.ent", "r" ); if ( pFile == NULL ) { printf("Error al abrir el archivo unexist.ent: %s\n", strerror( errno ) ); } return 0; } // Ejemplo de uso en C++ #include <iostream> #include <cstring> #include <fstream> #include <cerrno> using namespace std; int main () { ifstream flujo( "inexistente.ent" ); if ( !flujo.is_open() ) { cout<<"Error al abrir el archivo unexist.ent: "<<strerror( errno ); } return 0; } Salida: Error al abrir el archivo unexist.ent: No such file or directory En el ejemplo el programa trata de abrir un archivo que no existe, falla e informa del error. El error es identificado por el macro errno, al que se accede haciendo el correspondiente include al principio, y este es pasado como argumento a strerror() para obtener su cadena informativa correspondiente. errno representa el "número del último error". Este macro se expande a un lvalue modificable de tipo int. Por lo tanto, puede ser tanto leído como modificado por un programa. errno es seteado en cero cuando el programa es iniciado, y cualquier función de la librería estándar de C puede modificar su valor a otro diferente de cero, generalmente para señalar una categoría específica de error (ninguna librería vuelve a poner su valor en cero una vez que fue cambiado). Un programa también puede modificar su valor. De hecho, si esta variable fue pensada para utilizarse para comprobar errores después de la llamada a una función de la librería, debería ser reiniciada a cero por el programa antes de la llamada (ya que cualquier llamada anterior a una función de la librería podría haber alterado su valor). Y bueno eso ha sido todo. Espero que les sea útil y les sirva como una pequeña referencia para su día a día en el aprendizaje y resolución de problemas. En un futuro post les mostraré algunas maneras de combinar las funciones y sacarles el jugo al máximo empleando sobre todo operaciones con punteros. ¡Hasta la próxima!

Java para programadores en C++ << Volver al índice >> Parte I: ¿Java? Nota: este es el post más "libro" de la serie. El resto es más tutorial e información concreta, no se preocupen Un poco de historia Los microprocesadores estaban teniendo un profundo impacto en los dispositivos electrónicos inteligentes para uso doméstico. Al reconocer esto, Sun Microsystems patrocinó en 1991 un proyecto interno de investigación denominado Green, el cual desembocó en el desarrollo de un lenguaje basado en C++ al que su creador, James Gosling, llamó Oak debido a un roble que tenía a la vista desde su ventana en las ofi cinas de Sun. Posteriormente se descubrió que ya existía un lenguaje de computadora con el mismo nombre. Cuando un grupo de gente de Sun visitó una cafetería local, sugirieron el nombre Java (una variedad de café) y así se quedó. Pero el proyecto Green tuvo algunas difi cultades. El mercado para los dispositivos electrónicos inteligentes de uso doméstico no se desarrollaba tan rápido a principios de los noventa como Sun había anticipado. El proyecto corría el riesgo de cancelarse. Pero para su buena fortuna, la popularidad de World Wide Web explotó en 1993 y la gente de Sun se dio cuenta inmediatamente del potencial de Java para agregar contenido dinámico, como interactividad y animaciones, a las páginas Web. Esto trajo nueva vida al proyecto. Sun anunció formalmente a Java en una importante conferencia que tuvo lugar en mayo de 1995. Java generó la atención de la comunidad de negocios debido al fenomenal interés en World Wide Web. En la actualidad, Java se utiliza para desarrollar aplicaciones empresariales a gran escala, para mejorar la funcionalidad de los servidores Web (las computadoras que proporcionan el contenido que vemos en nuestros exploradores Web), para proporcionar aplicaciones para los dispositivos domésticos (como teléfonos celulares, radiolocalizadores y asistentes digitales personales) y para muchos otros propósitos. Estructura de Java Los programas en Java constan de varias piezas llamadas clases. Estas clases incluyen piezas llamadas métodos, los cuales realizan tareas y devuelven información cuando completan esas tareas. Los programadores pueden crear cada una de las piezas que necesitan para formar programas en Java. Sin embargo, la mayoría de los programadores en Java aprovechan las ricas colecciones de clases existentes en las bibliotecas de clases de Java, que también se conocen como APIs (Interfaces de programación de aplicaciones) de Java. Por lo tanto, en realidad existen dos fundamentos para conocer el “mundo” de Java. El primero es el lenguaje Java en sí, de manera que usted pueda programar sus propias clases; el segundo son las clases incluidas en las extensas bibliotecas de clases de Java. Programas en Java Por lo general, los programas en Java pasan a través de cinco fases: edición, compilación, carga, verifi cación y ejecución. Fase 1: Creación de un programa La fase 1 consiste en editar un archivo con un programa de edición (conocido comúnmente como editor). Usted escribe un programa en Java (conocido, por lo general, como código fuente) utilizando el editor, realiza las correcciones necesarias y guarda el programa en un dispositivo de almacenamiento secundario, como su disco duro. Un nombre de archivo que termina con la extensión .java indica que éste contiene código fuente en Java. En este libro asumimos que usted ya sabe cómo editar un archivo. Dos de los editores que se utilizan ampliamente en sistemas Linux son vi y emacs. En Windows, basta con usar un programa editor simple, como el Bloc de notas. También hay muchos editores de freeware y shareware disponibles para descargarlos de Internet, en sitios como www.download.com. Para las organizaciones que desarrollan sistemas de información extensos, hay entornos de desarrollo integrados (IDEs) disponibles de la mayoría de los proveedores de software. Los IDEs proporcionan herramientas que dan soporte al proceso de desarrollo del software, incluyendo editores para escribir y editar programas, y depuradores para localizar errores lógicos. Fase 2: Compilación de un programa en Java para convertirlo en códigos de bytes En la fase 2, el programador utiliza el comando javac (el compilador de Java) para compilar un programa. Por ejemplo, para compilar un programa llamado Bienvenido.java, escriba javac Bienvenido.java en la ventana de comandos de su sistema (es decir, el indicador de MS-DOS en Windows 95/98/ME, el Símbolo del sistema en Windows NT/2000/XP/7, el indicador de shell en Linux o la aplicación Terminal en Mac OS X). Si el programa se compila, el compilador produce un archivo .class llamado Bienvenido.class, que contiene la versión compilada del programa. El compilador de Java traduce el código fuente en códigos de bytes que representan las tareas a ejecutar en la fase de ejecución (fase 5). La Máquina Virtual de Java (JVM), una parte del JDK y la base de la plataforma Java, ejecuta los códigos de bytes. Una máquina virtual (VM) es una aplicación de software que simula a una computadora, pero oculta el sistema operativo y el hardware subyacentes de los programas que interactúan con la VM. Si se implementa la misma VM en muchas plataformas computacionales, las aplicaciones que ejecute se podrán utilizar en todas esas plataformas. La JVM es una de las máquinas virtuales más utilizadas. A diferencia del lenguaje máquina, que depende del hardware de una computadora específi ca, los códigos de bytes son instrucciones independientes de la plataforma; no dependen de una plataforma de hardware en especial. Entonces, los códigos de bytes de Java son portables (es decir, se pueden ejecutar en cualquier plataforma que contenga una JVM que comprenda la versión de Java en la que se compilaron). La JVM se invoca mediante el comando java. Por ejemplo, para ejecutar una aplicación llamada Bienvenido, debe escribir el comando java Bienvenido en una ventana de comandos para invocar la JVM, que a su vez inicia los pasos necesarios para ejecutar la aplicación. Esto comienza la fase 3. Fase 3: Cargar un programa en memoria En la fase 3, el programa debe colocarse en memoria antes de ejecutarse; a esto se le conoce como cargar. El cargador de clases toma los archivos .class que contienen los códigos de bytes del programa y los transfi ere a la memoria principal. El cargador de clases también carga cualquiera de los archivos .class que su programa utilice, y que sean proporcionados por Java. Puede cargar los archivos .class desde un disco en su sistema o a través de una red (como la de su universidad local o la red de la empresa, o incluso desde Internet). Fase 4: Verificación del código de bytes En la fase 4, a medida que se cargan las clases, el verifi cador de códigos de bytes examina sus códigos de bytes para asegurar que sean válidos y que no violen las restricciones de seguridad. Java implementa una estrecha seguridad para asegurar que los programas que llegan a través de la red no dañen sus archivos o su sistema (como podrían hacerlo los virus de computadora y los gusanos). Fase 5: Ejecución En la fase 5, la JVM ejecuta los códigos de bytes del programa, realizando así las acciones especifi cadas por el mismo. En las primeras versiones de Java, la JVM era tan sólo un intérprete de códigos de bytes de Java. Esto hacía que la mayoría de los programas se ejecutaran con lentitud, ya que la JVM tenía que interpretar y ejecutar un código de bytes a la vez. Por lo general, las JVMs actuales ejecutan códigos de bytes usando una combinación de la interpretación y la denominada compilación justo a tiempo (JIT). En este proceso, la JVM analiza los códigos de bytes a medida que se interpretan, buscando puntos activos: partes de los códigos de bytes que se ejecutan con frecuencia. Para estas partes, un compilador justo a tiempo (JIT) (conocido como compilador HotSpot de Java) traduce los códigos de bytes al lenguaje máquina correspondiente a la computadora. Cuando la JVM encuentra estas partes compiladas nuevamente, se ejecuta el código en lenguaje máquina, que es más rápido. Por ende, los programas en Java en realidad pasan por dos fases de compilación: una en la cual el código fuente se traduce a código de bytes (para tener portabilidad a través de las JVMs en distintas plataformas computacionales) y otra en la que, durante la ejecución, los códigos de bytes se traducen en lenguaje máquina para la computadora actual en la que se ejecuta el programa. Problemas que pueden ocurrir en tiempo de ejecución Es probable que los programas no funcionen la primera vez. Cada una de las fases anteriores puede fallar, debido a diversos errores que describiremos en este texto. Por ejemplo, un programa en ejecución podría intentar una división entre cero (una operación ilegal para la aritmética con números enteros en Java). Esto haría que el programa de Java imprimiera un mensaje de error. Si esto ocurre, tendría que regresar a la fase de edición, hacer las correcciones necesarias y proseguir con las fases restantes nuevamente, para determinar que las correcciones resolvieron el(los) problema(s). [Nota: la mayoría de los programas en Java reciben o producen datos. Cuando decimos que un programa muestra un mensaje, por lo general, queremos decir que muestra ese mensaje en la pantalla de su computadora. Los mensajes y otros datos pueden enviarse a otros dispositivos, como los discos y las impresoras, o incluso a una red para transmitirlos a otras computadoras]. << Volver al índice >>

Java para programadores en C++ << Volver al índice >> Parte III: Codear y Compilar. Mostrar datos en la consola. Java está basado en C++, por lo que verán que la mayoría de las cosas son similares sino idénticas. Por esto, la metodología de estos post va a ser empezar asumiendo que las cosas se hacen igual que en C++, e ir marcando las diferencias. Empecemos con una de ellas: En Java no se hace una separación prototipo-implementación. Por esto, tampoco se separa un mismo código en dos archivos (como en C++ .h y .cpp). Hay una forma de seguir obteniendo el beneficio de brindar una interfaz sin que el cliente/usuario tenga acceso al código fuente, pero es tema para más adelante. La estructura de un código de Java es mucho más restrictiva que en C++. En un código de Java solamente puede haber clases. Es decir, no hay "espacio global", no hay funciones, no hay variables globales, etc. Todo código debe estar encapsulado adentro de una clase. Las únicas dos excepciones son los includes (que en java son imports) y la declaración de paquetes (de los que hablo más adelante). Archivos - Los archivos de código fuente tienen extención ".java". - Los archivos de clases compiladas tienen extención ".class" y vendrían a ser los análogos en C++ a los archivos ".o" y a las librerías. Los ".class" se pueden ejecutar escribiendo en la consola java NombreArchivo (sin la extension). - Los archivos de código fuente ".java" se compilan a ".class" escribiendo en la consola javac NombreArchivo.java - Los ejecutables finales de los programas compilados tienen extención ".jar". Se ejecutan ingresando en consola java -jar NombreArchivo.jar Java no genera archivos .exe . Para crearlos pueden hacer un programa en C++ que tenga simplemente un main con la línea system( "java -jar NombreArchivo.jar" ); o como se les ocurra. Estructura de un programa Un programa es una clase. En java, las clases pueden tener nivel de accesibilidad como los métodos (public, private). Las clases públicas deben tener el mismo nombre que el archivo de código fuente en el que se declaran y definen. Así, por ejemplo, una clase pública definida en el archivo fuente "MiPrograma.java" se llamara "MiPrograma" y viceversa. Cabe aclarar que por esta característica solamente puede haber una clase pública por archivo de código fuente. La estructura básica de un programa es una clase principal del mismo nombre que el programa que tiene un método main (en Java no hay funciones, así que main es un método). Su declaración es: public class NombrePrograma { public static void main( String[] args ) { // Acciones del programa. } } En el post sobre clases de la serie hay más información sobre esta sintaxis. Comentarios Los comentarios son iguales a C++. La forma de comentar propia de java se denomina Javadoc y su estructura es /** comentario */ Es utilizada para generar documentación en html mediante el javadoc utility program (incluido en el JDK). Es similar a generar documentación con Doxygen en C++. Punteros en Java Este tema es algo peculiar. En Java hay dos grandes grupos de tipos: los tipos de datos primitivos (char, float, int, etc) y las clases. TODA VARIABLE QUE NO CORRESPONDE A UN TIPO DE DATO PRIMITIVO ES UN PUNTERO. Dicho esto, no podemos elegir cuándo trabajar con punteros y cuándo no, porque siempre trabajaremos con punteros cuando empleemos una clase, el lenguaje está hecho así . Los objetos se van a instanciar entonces con el operador new. A razón de que no hay distinción entre objeto y puntero, el operador de acceso de miembro mendiante punteros ( -> ) no existe. Para acceder a los miembros siempre se utiliza el operador punto. Escribiendo en la pantalla Para mostrar datos en la pantalla mediante la consola se utilizan los métodos (recordar que no hay funciones) de la clase System definida en el paquete java.lang de la API de Java. System tiene 3 objetos miembros static llamados in, out y err, y corresponden a lo que en C++ son cin, cout y cerr respectivamente. Cada uno de estos objetos tiene a su vez los métodos de entrada/salida que vamos a utilizar. Como son objetos miembro de la clase System, y a su vez necesitamos acceder a los métodos de cada uno, los invocaremos con la sintaxis: System.nombreObjeto.nombreMetodo(); (En Java se accede a los miembros static mediante el operador punto y no mediante el operador de resolución de ámbito ( :: ) como en C++). - Para mostrar una cadena en pantalla y ubicar el cursor al final se utiliza: System.out.print( "cadena" ); - Para mostrar una cadena en pantalla y pasar a la linea de abajo se utiliza: System.out.println( "cadena" ); - Para mostrar una cadena con formato se utiliza: System.out.printf( "cadena de formato", arg_1, ..., arg_n ); Cada método print de System.out se utiliza y funciona de la misma forma que las funciones de mismo nombre de C. out es una instancia de la clase PrintStream (del paquete java.io). Pueden ver sus otros métodos consultado su documentación: http://docs.oracle.com/javase/6/docs/api/java/io/PrintStream.html Creando nuestro primer programa Ya tenemos los recursos para crear nuestro primer programa en Java. Si están en un IDE crean un nuevo proyecto y dentro del mismo una nueva clase. Si utilizan un editor, creen el archivo PrimerPrograma.java. Dentro de él escribimos, como no podía ser de otra forma : public class PrimerPrograma { public static void main( String[] args ) { System.out.println( "Hola mundo!" ); } } Corren el programa en el IDE o, si crearon la fuente con un editor, abran la consola, ubíquense en el directorio del archivo fuente y compilen la fuente a un archivo .class como se indicó más arriba escribiendo: javac PrimerPrograma.java y luego lo ejecutan con java PrimerPrograma Obtenemos así nuestra primera salida Hola mundo! << Volver al índice >>