Biblioteca estándar de C: Algunas funciones y trucos Lectura de Input fscanf(FILE *stream, const char *format, ...) Caracterı́sticas: Retorna el número de ı́temes asignado (puede ser menor que el suplementado). Retorna EOF si se leyó el fin de archivo. Ignora todo tipo de separadores (espacios en blanco, fines de lı́nea, tabulaciones). Recomendada para: leer números, palabras (una por una), hacer parseos simples. Ejemplos de buen uso: fscanf(f,"%s%d",str,&x); /* para leer un string seguido de un entero sin importar que hay entremedio */ Uso no recomendable: fscanf(f,"numero: %d",&x);/* para leer número seguido de la palabra "numero:" (peligroso) */ Jorge Baier Aranda, PUC 1 Se permite que el string de formato tenga caracteres al comienzo. En este caso, la función fscanf espera que éstos aparezcan al comienzo del input. Si no es ası́, no leerá nada. No recomendada para: En general no se recomienda usarla si importa leer caracteres normalmente ignorados. En particular, no sirve para leer archivos lı́nea por lı́nea y no sirve para hacer cualquier tipo de “parseo”. Truco 1: Es posible obtener el número de caracteres que fscanf ha leı́do usando el caracter de formato n. En el siguiente programa se usa dicho parámetro. #include <stdio.h> int main() { char c; int x,i,r,cant; while (!feof(stdin)) { r=fscanf(stdin,"%d%n",&x,&cant); if (r) printf("%d\t%d\t%d\n",r,x,cant); else break; } return 0; Jorge Baier Aranda, PUC 2 } Este es un ejemplo de ejecución: 34 54 67 1 34 1 54 1 67 45 1 45 2 3 3 3 Truco 2: Es posible descartar el almacenamiento un dato exitosamente convertido con fscanf. Esto se hace anteponiendo un * al caracter de formato. Ejemplo: fscanf(stdin,"%*s%d%n",&x); /* solo interesa leer el entero */ char *fgets(char *s, int size, FILE *stream) Caracterı́sticas: Lee una lı́nea completa desde el archivo stream, almacena el resultado en el string s. Lee a lo más size caracteres. Retorna s en caso de éxito y NULL en caso de error. Advertencia: fgets lee también el fin de lı́nea \n. Jorge Baier Aranda, PUC 3 int fgetc(FILE *stream) Caracterı́sticas: Lee un caracter desde stream como unsigned char convertido a int. int ungetc(int c, FILE *stream) Caracterı́sticas: Devuelve un caracter hacia stream. Sólo se garantiza que se pueda devolver un caracter. No se puede devolver EOF. Retorna el caracter devuelto o EOF en caso de error. int feof(FILE *stream) Caracterı́sticas: Retorna un valor no nulo si se ha leı́do el fin de archivo desde stream. Manejo de Strings char *strstr(const char *haystack, const char *needle) Caracterı́sticas: Retorna un puntero al primer caracter de la primera ocurrencia del string needle dentro del string heystack o NULL si no hay tal ocurrencia. char *strpbrk(const char *s, const char *accept) Jorge Baier Aranda, PUC Caracterı́sticas: 4 Retorna un puntero al caracter en s que está presente en el string accept o NULL si tal caracter no es encontrado. char *strtok(char *s, const char *delim) Busca en s por tokens delimitados por caracteres en delim. El primer llamado a strtok debe tener a s como primer argumento; los siguientes, NULL. Cada llamado retorna un puntero al siguiente token o NULL cuando no se encuentran más tokens. Cuando un token termina con un delimitador, el caracter delimitador es sobreescrito con un \0. Esto significa que el string original es alterado. Dato curioso: En el manual de unix de esta función aparece lo siguiente: BUGS Never use these functions. If you do, note that: These functions modify their first argument. These functions cannot be used on constant strings. The identity of the delimiting character is lost. Ejemplo de Uso: El siguiente programa encuentra los campos en un archivo del estilo /etc/passwd de Unix. Tiene un serio problema con los campos vacı́os... Jorge Baier Aranda, PUC 5 #include <stdio.h> #include <string.h> #define MAX 1024 #define SEP "-----------------------" int main() { FILE *fp; char buf[MAX],datos[7][MAX]; char *ptr; int i; fp=fopen("passwd","r"); while (fgets(buf,MAX,fp)!=NULL) { ptr=strtok(buf,":\n"); for(i=0;ptr;ptr=strtok(NULL,":\n"),i++) strcpy(datos[i],ptr); if (i<7) printf("No encontré todos los datos!\n"); printf("login: %s\npasswd: %s\nuser: %s\ngroup: %s\nname: %s\nhome: %s\nshell: %s\n%s\n", datos[0],datos[1],datos[2],datos[3], datos[4],datos[5],datos[6],SEP); } fclose(fp); return 0; } Con el archivo: rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin Jorge Baier Aranda, PUC 6 nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin pcap:x:77:77::/var/arpwatch:/sbin/nologin tiene el siguiente comportamiento: login: rpcuser passwd: x user: 29 group: 29 name: RPC Service User home: /var/lib/nfs shell: /sbin/nologin ----------------------login: nfsnobody passwd: x user: 65534 group: 65534 name: Anonymous NFS User home: /var/lib/nfs shell: /sbin/nologin ----------------------No encontré todos los datos! login: pcap passwd: x user: 77 group: 77 name: /var/arpwatch home: /sbin/nologin ----------------------- Jorge Baier Aranda, PUC 7 STL: Standard Template Library STL es uno de los elementos más interesantes de C++. Permite usar strings, algoritmos pre-definidos y contenedores (vectores, colas, stacks, etc.) de manera muy directa. Manejo de Strings STL provee la clase string para manejo de strings. Muchas operaciones (como concatenación y reemplazo) tienen sus propios operadores. Jorge Baier Aranda, PUC 8 El siguiente ejemplo muestra algunas de sus potencialidades. #include <iostream> #include <string> using namespace std; int main() { string s1; // inicializa un string vacı́o string s2="hola"; // inicializa string en "hola"; string s3(6,’c’); // string s3 = "cccccc"; s1 = s2 + " " + s3; // s1 = "hola cccccc"; cout << ((s1==s2)? 1 : 0) << endl; // imprime "0", se puede usar >, < , >=, etc. s1[0] = ’t’; // s1 = "tola cccccc"; printf("%s\n",s1.c_str()); // compatibilidad con strings de C s1.insert(0,s2); // s1 = "holatola cccccc" string::size_type i = s1.find("cc"); // i=9 i = s2.length(); // i = 4 s3=s2.substr(1,3); // s3="ola" s1.replace(s1.find("tola"),4,","); // s1 = "hola, cccccc" return 0; } Jorge Baier Aranda, PUC 9 Contenedores en STL Un contenedor objeto que permite almacenar otros objetos. Existen contenedores basados en estructuras de datos conocidas. Pueden almacenar distintos tipos de datos pues están definidos como templates. Estos son los contenedores definidos en STL: Cabecera Contenedor <vector> <list> <deque> <queue> <stack> <map> <set> <bitset> arreglo unidimensional de T lista doblemente ligada de T cola de doble extremo de T cola de T pila de T arreglo asociativo de T conjunto de T arreglo de bits T El contenedor priority_queue se declara en <queue>. Veremos el uso de algunos de estos contenedores. Jorge Baier Aranda, PUC 10 Vectores Los vectores son muy similares a los arreglos. De hecho, se permite acceder a la enésima posición a través de A[n]. Agregar elementos al final de un vector es muy eficiente (O(1)) pero eliminar o agregar elementos en otra parte toma en general tiempo O(n), con n el número de elementos en el vector. Las siguientes están dentro de los métodos más útiles de la clase vector: Método Efecto/Retorno begin() end() push_back(valor) pop_back(variable) insert(iterador,valor ) erase(iterador) size() empty() [] Retorna iterador apuntando al primer elemento Retorna iterator apuntando después del último elemento Agrega un elemento al final del vector Destruye el elemento al final del vector Inserta un nuevo elemento Elimina un elemento (*) Número de elementos Verdadero si el vector está vacı́o Acceso directo Jorge Baier Aranda, PUC 11 El siguiente ejemplo muestra el uso de algunas funciones. #include <iostream> #include <vector> #include <cstdlib> using namespace std; void muestra(vector<int> &v) { for (vector<int>::iterator viter=v.begin(); viter!=v.end(); ++viter) cout << *viter << " "; cout << endl; } int main() { vector<int> v; for (int i=0; i<5; i++) { v.push_back(rand()/100000); } muestra(v); sort(v.begin(), v.end() ); muestra(v); v.erase(v.begin()+1); v.insert(v.begin(),-1); muestra(v); return( EXIT_SUCCESS ); } El siguiente es el resultado que produce: 18042 8469 16816 17146 19577 Jorge Baier Aranda, PUC 12 8469 16816 17146 18042 19577 -1 8469 17146 18042 19577 Jorge Baier Aranda, PUC 13 Lectura de Input en C++ En C++, la lectura de input se debiera realizar usando las funciones C++ para lectura de archivo. El operador >> aplicado sobre un objeto del tipo ifstream tiene un efecto similar al de scanf (ignora separadores). Ejemplo: #include <iostream> #include <string> ... string s; cin >> s; Para leer lı́neas completas, se usa el método getline. Ejemplo: string s; getline(cin,s,’\n’); El siguiente programa lee lı́neas que contienen un string y luego un entero. Jorge Baier Aranda, PUC 14 #include <iostream> #include <fstream> #include <string> using namespace std; int main() { string s,s2; int n; ifstream f; istream *p; // comentar siguientes lı́neas para leer de entrada estándar f.open("hola.txt"); if (f.fail()) { perror("no se puede abrir el archivo"); exit(1); } p = &f; //p = &cin; // descomentar para leer de la entrada estándar while (1) { *p >> s; *p >> n; if (p->fail()) break; cout <<"!"<< s << "!" << n << "!" << endl; } f.close(); return 0; } Jorge Baier Aranda, PUC 15 Colas con acceso doble (deque) Son similares a los vectores, pero: • Agregar/eliminar al principio y al final es O(1). El método push_front() y pop_front() permite operar al principio de la lista. Listas (list) Son similares a los deques, pero: • No es posible tener acceso directo al enésimo elemento. • Eliminar un elemento que no está al final ni al principio es O(1). Colas (queue) Son similares a las listas, pero: • Sólo es posible agregar un elemento al final de la cola. • Sólo es posible extraer un elemento del principio de la cola. Jorge Baier Aranda, PUC 16 Colas de Prioridades (priority queue) Son similares a las colas. Sirven cuando es necesario tener un contenedor donde sólo extraer importa el elemento más grande. La operación de eliminación e inserción es O(log n). (Se usan los métodos push() y pop() respectivamente.) Conocer el elemento mayor es O(1). (Se usa el método top()) El siguiente es un ejemplo: // C++ STL Headers #include <iostream> #include <queue> #include <string> using namespace std; class Proceso { private: unsigned int prioridad; string nombre; public: friend ostream& operator << ( ostream& os, const Proceso &task ); friend bool operator < (const Proceso &p1, const Proceso &p2); Jorge Baier Aranda, PUC 17 Proceso(const char *nom = "", unsigned int prio = 0 ){ nombre = nom; prioridad = prio; } }; // Compara dos procesos bool operator < (const Proceso &p1, const Proceso &p2) { return p1.prioridad < p2.prioridad; } // Muestra un proceso en pantalla ostream & operator<<( ostream &os, const Proceso &task ) { os << "Proceso: " << task.nombre << "\tPrioridad: " << task.prioridad; return os; } int main( int argc, char *argv[] ) { int i; priority_queue<Proceso> task_queue; Proceso tasks[] = { Proceso("JORGE"), Proceso("INIT",6), Proceso("Swapper",16), Proceso("LPD",8) , Proceso("HTTPD",8) }; for ( i = 0; i < sizeof(tasks)/sizeof(tasks[0]) ; i++ ) task_queue.push( tasks[i] ); while ( !task_queue.empty() ) { cout << task_queue.top() << endl; task_queue.pop(); } Jorge Baier Aranda, PUC 18 cout << endl; return( EXIT_SUCCESS ); } Y produce el siguiente output Proceso: Proceso: Proceso: Proceso: Proceso: Swapper Prioridad: 16 LPD Prioridad: 8 HTTPD Prioridad: 8 INIT Prioridad: 6 JORGE Prioridad: 0 Jorge Baier Aranda, PUC 19 Algoritmos La STL provee una gran cantidad de algoritmos predefinidos cuando se incluye la cabecera <numeric>. Destacan los siguientes (generalmente usados en deques, listas y vectores): Función Descripción find() replace_if() reverse() random_shuffle() sort() stable_sort() binary_search() generate() unique() lower_bound() upper_bound() merge() Encuentra un elemento en una secuencia Reemplaza los elementos que satisfacen una propiedad Invierte el orden de los elementos propiedad Reordena los elementos en forma aleatoria Ordena los elementos Ordena los elementos en forma estable Busca un elemento en una secuencia ordenada Reemplaza cada elemento con el resultado de una operación sobre éste. Elimina elementos adyacentes que son iguales Encuentra la cota inferior en una secuencia ordenada Encuentra la cota superior en una secuencia ordenada Mezcla dos secuencias ordenadas Jorge Baier Aranda, PUC 20