/* This program or library is part of amv::software. Copyright (C) 1995-2007 Ariel Alonzo Medina Vázquez This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, visit http://www.gnu.org/licenses/lgpl.html or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Ariel Alonzo Medina Vázquez ariel_medina21@hotmail.com http://www.paginasprodigy.com/campechedigital/arielmedina1978 Campeche, Campeche, México */ /* Biblioteca de cadenas bcstring y substring - 2007/12/02 bcstring es una abreviatura para BSTR / C string / C++ string Esta clase es un híbrido que aprovecha características de las 3 clases de cadenas mencionadas. De BSTR: Internamente cuenta con casi todas las características y beneficios de una BSTR (*), (Se aconseja leer la definición de BSTR al final del archivo). De las cadenas C: Terminación en nulo, de manera que se puede incluir como parámetro de entrada en funciones como strcpy, strcmp, etc. De las cadenas C++: El manejo como un objeto, con constructores, destructores, sobrecarga de operadores, funciones miembro que entregan informacíón como el tamaño, iteradores, etc. Tiene como fin manejar cadenas de caracteres de manera potente y veloz (se pueden asignar, concatenar (+), etc. ). Se ha construido como una alternativa a las strings de la STL (que en algunos compiladores no son seguras en entornos multihilo, pues manejan contadores de referencias que pueden corromperse si no se usan objetos de sincronización cuando varios hilos acceden concurrentemente a dos objetos con el mismo valor) y buscando la facilidad de uso en un ambiente orientado a objetos y velocidad de los char * tradicionales. El usuario ya no tendrá que preocuparse por crear o liberar memoria, ni guardar el tamaño (ya que el objeto lo conoce). Asimismo se podrán ordenar al estilo de la STL como cualquier otro objeto o como si fuera un arreglo de char * ( lo cual sería más veloz e implica conversiones de tipo adecuadas para evitar los constructores, asignadores u operadores sobrecargados) aprovechando la naturaleza del objeto. Toda la clase sirve de envoltorio a un char * y realmente cada objeto bcstring es equivalente a un char *. De tal manera que &bcstring == char **. (*) Es similar a una BSTR excepto por tres cosas: a) Es como una cadena terminada en nulo de C, por lo que no debe contener caracteres nulos sino es al final de la cadena. b) Sólo opera con el tipo char (un sólo octeto) c) El terminador es sólo un carácter nulo Respecto de la clase substring, encontrará la explicación casi al final, en donde se define esa clase. */ #ifndef __BCSTRING_AMV #define __BCSTRING_AMV #include #include #include #include #include #include #include #include #include #include #include "amvdefs.h" #include "REEvaluator.h" #define SIZE_OFFSET sizeof(size_t) // Desplazamiento para guardar el tamaño #define BCSTRING_INITIAL_SIZE 128 // tamaño inicial (y mínimo cuando tiene datos) de una cadena bcstring // Siempre debe ser una potencia de 2 using namespace std; namespace amv { inline bool less_str_bcstring(const char *s1, const char *s2) { if(s1 && s2) return (strcmp(s1,s2)<0); else if(s1~bcstring(); ps=0; } bcstring &operator=(const bcstring &bs) { if(bs.ps) return assign(bs.ps,bs.size()); this->~bcstring(); ps=0; return *this; } bcstring &operator=(const char *s) { if(*s) return assign(s,::strlen(s)); this->~bcstring(); ps=0; return *this; } bcstring &operator=(const char c) { if(c) return assign(&c,1); this->~bcstring(); ps=0; return *this; } bcstring &operator=(const substring &ss); bcstring &operator+=(const bcstring &bs1) { if(!bs1.ps) return *this; return add(bs1.ps,bs1.size()); } bcstring &operator+=(const char *s) { if(!*s) return *this; return add(s,::strlen(s)); } bcstring &operator+=(const char c) { if(!c) return *this; return add(&c,1); } bcstring &operator+=(const substring &ss); // Si no está definida la siguiente constante de preprocesador, se considera que las cadenas tienen muchas // probabilidades de cambiar por lo que generalmente se le deja un espacio que llegue a la siguiente // potencia de 2; si se define dicha constante el código será optimizado para cadenas que cambiarán con poca // frecuencia #ifndef __ALMOST_CONSTANT_BCSTRING #define TOTAL_OFFSET (SIZE_OFFSET*2) // No se proporcionan funciones virtuales porque esta clase no esta pensada para ser heredada de esa forma, // ya que perderíamos la ventaja del sizeof(char *)==sizeof(bcstring), char * == *&bcstring, que nos permite // ordenamientos rápidos, previa conversión de tipos o utilización de funciones de la clase que hacen dicha // conversión ~bcstring() { if(ps) free(ps-TOTAL_OFFSET); } bcstring &assign(const char *s, size_type n) { size_type newSize=getNewSize(n); if(ps) free(ps-TOTAL_OFFSET); if(ps=(char *)malloc(newSize)) { memcpy(ps+TOTAL_OFFSET,s,n); *((size_type *)ps)=newSize-(TOTAL_OFFSET+1)-n; *((size_type *)(ps+SIZE_OFFSET))=n; ps+=TOTAL_OFFSET; ps[n]=0; // Para garantizar que termina en nulo } else throw bad_alloc(); return *this; } bcstring &add(const char *s, size_type n2) { if(n2==0) return *this; size_type n1=size(), n=n1+n2, newSize, total=capacity(); if(total<=n) { char *ps1; newSize=getNewSize(n); if(ps && (ps1=(char *)realloc(ps-TOTAL_OFFSET,newSize))) { *((size_type *)(ps1))=newSize-(TOTAL_OFFSET+1)-n; *((size_type *)(ps1+SIZE_OFFSET))=n; ps1+=TOTAL_OFFSET; memcpy(ps1+n1,s,n2); *(ps1+n1+n2)=0; ps=ps1; } else { if(ps1=(char *)malloc(newSize)) { *((size_type *)(ps1))=newSize-(TOTAL_OFFSET+1)-n; *((size_type *)(ps1+SIZE_OFFSET))=n; ps1+=TOTAL_OFFSET; memcpy(ps1,ps,n1); memcpy(ps1+n1,s,n2); *(ps1+n1+n2)=0; if(ps) free(ps-TOTAL_OFFSET); ps=ps1; } else throw bad_alloc(); } } else { *((size_type *)(ps-SIZE_OFFSET))=n; *((size_type *)(ps-TOTAL_OFFSET))=total-n; memcpy(ps+n1,s,n2); *(ps+n1+n2)=0; } return *this; } // s debe ser el miembro ps de un objeto bcstring, se debe llamar enseguida a la función unbind() // del objeto que era propietario de s void bind(const char *s) { if(ps) free(ps-TOTAL_OFFSET); ps=const_cast(s); } void bind(bcstring &bs) { if(ps) free(ps-TOTAL_OFFSET); ps=bs.ps; bs.unbind(); } size_type avail() const { if(ps) return *((size_type *)(ps-TOTAL_OFFSET)); return 0; } size_type capacity() const { return (size()+avail()); } // Dado que por diseño no podemos tener la cadena vacia con memoria asignada, // si la cadena está vacía, se le pone la letra 'a' en el primer carácter // Esta función es usada cuando se quiere modificar externamente la cadena mediante // el apuntador interno, e implica el compromiso de llamar luego a updatesize() // para actualizar el tamaño de la cadena // Ya que la cadena será modificada, ponerle la letra 'a' al principio no debe dar problemas // Se aconseja en vez de esta función usar las demás que ofrece la clase, pues actualizan // automáticamente el tamaño de la cadena void setcapacity(size_type n) { if(ps) { size_type oc=capacity(); char *ps1; if(oc<=n) { n=getNewSize(n); if(ps1=(char *)realloc(ps-TOTAL_OFFSET,n)) { ps=ps1; *((size_type *)(ps))+=n-(TOTAL_OFFSET+1)-oc; //*((size_type *)(ps+SIZE_OFFSET))=0; //se conserva el tamaño actual ps+=TOTAL_OFFSET; //*ps=0; //ya tenía datos, sólo cambiaron los tamaños } else throw bad_alloc(); } } else { n=getNewSize(n); if(ps=(char *)malloc(n)) { *((size_type *)(ps))=n-(TOTAL_OFFSET+1)-1; *((size_type *)(ps+SIZE_OFFSET))=1; ps+=TOTAL_OFFSET; *ps='a'; //la cadena vacía no debe tener memoria asignada, por lo que se le pone un carácter *(ps+1)=0; } else throw bad_alloc(); //*** una alternativa es devolver false, implica la verificacion desde el llamador } } // Los nuevos tamaños se dan en potencias de 2, porque el tamaño de los bloques de memoria // se da usualmente en potencias de 2 y de esta manera se aprovechan todos los octetos antes // de una reasignacion de memoria. size_type getNewSize(size_type n) { // if(max_size::max()-(TOTAL_OFFSET+1); // SIZE_OFFSET octetos para los disponibles, SIZE_OFFSET para los ocupados y 1 para el terminador nulo n+=(TOTAL_OFFSET+1); size_type newSize=BCSTRING_INITIAL_SIZE;//capacity(); /*if(newSize) newSize+=(TOTAL_OFFSET+1); // debe dar una potencia de 2 else newSize=128; // Tamaño inicial */ while(newSize && newSize<=n) //hasta que quepa en una potencia de 2 newSize<<=1; if(newSize) return newSize; else return ~newSize; // El tamaño debió ser el máximo } #else #define TOTAL_OFFSET (SIZE_OFFSET) ~bcstring() { if(ps) free(ps-SIZE_OFFSET); } bcstring &assign(const char *s, size_type n) { if(ps) free(ps-SIZE_OFFSET); if(ps=(char *)malloc(n+(SIZE_OFFSET+1))) { memcpy(ps+SIZE_OFFSET,s,n); *((size_type *)ps)=n; ps+=SIZE_OFFSET; ps[n]=0; // Para garantizar que termina en nulo } else throw bad_alloc(); return *this; } bcstring &add(const char *s, size_type n2) { if(n2==0) return *this; size_type n1=size(), n=n1+n2; char *ps1; if(ps && (ps1=(char *)realloc(ps-SIZE_OFFSET,n+(SIZE_OFFSET+1)))) { *((size_type *)ps1)=n; ps1+=SIZE_OFFSET; memcpy(ps1+n1,s,n2); *(ps1+n1+n2)=0; ps=ps1; } else { if(ps1=(char *)malloc(n+(SIZE_OFFSET+1))) { *((size_type *)ps1)=n; ps1+=SIZE_OFFSET; memcpy(ps1,ps,n1); memcpy(ps1+n1,s,n2); *(ps1+n1+n2)=0; if(ps) free(ps-SIZE_OFFSET); ps=ps1; } else throw bad_alloc(); } return *this; } // s debe ser el miembro ps de un objeto bcstring, se debe llamar enseguida a la función unbind() // del objeto que era propietario de s void bind(const char *s) { if(ps) free(ps-SIZE_OFFSET); ps=const_cast(s); } void bind(bcstring &bs) { if(ps) free(ps-SIZE_OFFSET); ps=bs.ps; bs.unbind(); } size_type avail() const { return 0; } size_type capacity() const { return (size()); } #endif // Desenlaza la memoria, si había un apuntador válido se pierde void unbind() { ps=0; } operator const char *() const { if(!ps) return pes; return ps; } // Se debe revisar el valor devuelto, podría ser 0 const char *b_str() const { return ps; } const char *c_str() const { if(!ps) return pes; return ps; } size_type size() const { if(ps) return *((size_type *)(ps-SIZE_OFFSET)); return 0; } bool empty() const { return !ps; } bool operator==(const bcstring &bs1) const { return eq_str_bcstring(ps,bs1.ps); } bool operator<(const bcstring &bs1) const { return less_str_bcstring(ps,bs1.ps); } bool operator==(const char *s) const { return eq_str_bcstring(ps,*s? s: 0); // Para la cadena vacía debe pasarse 0 (nulo) a la función } bool operator<(const char *s) const { return less_str_bcstring(ps,*s? s: 0); } bcstring operator+(const bcstring &bs1) const { bcstring temp(*this); return temp+=bs1; } bcstring operator+(const char *s) const { bcstring temp(*this); return temp+=s; } bcstring operator+(const char c) const { bcstring temp(*this); return temp+=c; } bcstring operator+(const substring &ss) const { bcstring temp(*this); return temp+=ss; } const char &operator[](size_type i) const { return *(ps+i); } char &operator[](size_type i) { return *(ps+i); } const_iterator begin() const { return (ps? ps: pes); } iterator begin() { return (ps? ps: (char *)pes); } const_iterator end() const { return (ps? ps+size(): pes); } iterator end() { return (ps? ps+size(): (char *)pes); } iterator erase(iterator first, iterator last) { size_t n=last-first, newSize; iterator ite=end(); if(n) { if(last=cs) { this->~bcstring(); ps=0; } else erase(ps,ps+n); } else { if(n>=cs-pos) { erase(ps+pos,ps+cs); } else erase(ps+pos,ps+pos+n); } return *this; } // replace // Se remplazan todas las ocurrencias de manera predeterminada void replace(const char oldc,const char *news, size_t n=npos) { if(ps) { size_t no=std::count(begin(),end(),oldc); int newSize, newnc=::strlen(news), newssize; newSize=(newnc-1)*no; newssize=size()+newSize; if(newssize==0) { clear(); return; } newSize=getNewSize(newssize); char *p=(char *)malloc(newSize), *p1, *p2, *pend=end(); #ifndef __ALMOST_CONSTANT_BCSTRING *((size_type *)p)=newSize-(TOTAL_OFFSET+1)-newssize; *((size_type *)(p+SIZE_OFFSET))=newssize; p+=TOTAL_OFFSET; #else *((size_type *)(p+SIZE_OFFSET))=newssize; p+=SIZE_OFFSET; #endif p1=p; p2=ps; if(n==npos) { while(p2 vp; int oldnc=::strlen(olds), newnc=::strlen(news), newSize, diff, newssize; if(ps==0) return; if (olds && *olds && news) { char *pc=ps; if(n==npos) { while(pc=strstr(pc,olds)) { vp.push_back(pc); pc+=oldnc; } } else { while(n>0 && (pc=strstr(pc,olds))) { vp.push_back(pc); pc+=oldnc; --n; } } if(vp.size()) { newSize=(newnc-oldnc)*vp.size(); newssize=size()+newSize; if(newssize==0) { clear(); return; } newSize=getNewSize(newssize); char *p=(char *)malloc(newSize), *p1, *p2; #ifndef __ALMOST_CONSTANT_BCSTRING *((size_type *)p)=newSize-(TOTAL_OFFSET+1)-newssize; *((size_type *)(p+SIZE_OFFSET))=newssize; p+=TOTAL_OFFSET; #else *((size_type *)(p+SIZE_OFFSET))=newssize; p+=SIZE_OFFSET; #endif p1=p; p2=ps; for(int i=0; ip2) { diff=vp[i]-p2; memcpy(p1,p2,diff); p1+=diff; p2+=diff+oldnc; memcpy(p1,news,newnc); p1+=newnc; } else { p2+=oldnc; memcpy(p1,news,newnc); p1+=newnc; } } if(p2~bcstring(); ps=0; } } #else void setsize(size_type n) { if(n) *((size_type *)(ps-SIZE_OFFSET))=n; else { this->~bcstring(); ps=0; } } #endif public: void ltrim() { if(ps) { size_type n=0, oldsize=size(); char *p=ps, *pe=end(); while(p=ps && isspace(*p)) { --p; ++n; } *(p+1)=0; setsize(oldsize-n); } } void alltrim() { ltrim(); rtrim(); } friend void swap(bcstring &bcs1, bcstring &bcs2) { char *p=bcs1.ps; bcs1.ps=bcs2.ps; bcs2.ps=p; } // Desenlaza la memoria de los objetos (se pierde el apuntador), si alguno de ellos tenía un apuntador válido // debió haber sido asignado a otro apuntador que el usuario habrá de liberar o a otro // objeto bcstring mediante la función miembro bind, para que al destruirse libere la memoria. // También podría haberse asignado por copia directa de apuntadores (con el operador de conversión adecuado). static void unbind(bcstring *p, size_t n) { memset(p,0,n*sizeof(char *)); } //*** Las 2 funciones siguientes son para bcstring en memoria contigua, es decir: // - Arreglos estáticos o dinámicos del lenguaje o // - begin() y end() de un contenedor como std::vector, si es que los iteradores devueltos se manejan como apuntadores al tipo // Ordenamiento para una secuencia de elementos bcstring // El código dentro de la función puede llamarse desde fuera, // pero se creo esta función para que el usuario no haga la conversión de tipo explícita // y sólo invoque esta con el estilo de la STL static void sort(bcstring *first, bcstring *last) { // invoca directamente a less_str_bcstring y considera los bcstring como char * // realmente es equivalente por la naturaleza del objeto // esta versión debe ser la más veloz para secuencias bcstring std::sort((const char **)first,(const char **)last,less_str_bcstring); /* // Las siguientes versiones se basarían en los tipos pero serían más costosas std::sort(first,last); // invoca operator< y less_str_bcstring std::sort(first,last,less_str_bcstring); // invoca const char * operator y less_str_bcstring */ } // Ordenamiento para una secuencia de elementos bcstring * // El código dentro de la función puede llamarse desde fuera, // pero se creo esta función para que el usuario no haga la conversión de tipo explícita // y sólo invoque esta con el estilo de la STL static void sort(bcstring **first, bcstring **last) { // invoca directamente a less_str_ptr y considera los bcstring como char * // realmente es equivalente por la naturaleza del objeto // esta versión debe ser la más veloz para secuencias bcstring * std::sort((const char ***)first,(const char ***)last,less_str_ptr_bcstring); /* // Las siguientes versiones se basarían en los tipos pero serían más costosas std::sort(first,last,less_ptr); // invoca operator< y less_str_bcstring std::sort(first,last,less_str_ptr); // invoca const char * operator y less_str_bcstring */ } //*** Las 2 funciones siguientes son para bcstring en un contenedor como std::deque // Optmización para un deque // Puede hacer el ordenamiento con el std::sort directamente, pero esta implementacion es más rápida static void sort(deque::iterator first, deque::iterator last) { typedef deque::iterator * pit; pit it1=(pit)&first; pit it2=(pit)&last; std::sort(*it1,*it2,less_str_bcstring); // Cuando se trate de vector puede usar bcstring::sort(v.begin(),v.end()), ya que en general // el iterator de un vector es un apuntador al tipo, si no se resuelve, use std::sort(v.begin(),v.end()) } // Optmización para un deque // Puede hacer el ordenamiento con el std::sort directamente, pero esta implementacion es más rápida static void sort(deque::iterator first, deque::iterator last) { typedef deque::iterator * pit; pit it1=(pit)&first; pit it2=(pit)&last; std::sort(*it1,*it2,less_str_ptr_bcstring); // Cuando se trate de vector puede usar bcstring::sort(v.begin(),v.end()), ya que en general // el iterator de un vector es un apuntador al tipo, si no se resuelve, use std::sort(v.begin(),v.end()) } // Devuelve la subcadena propia, es decir, la misma cadena como una subcadena operator substring() const; substring substr() const; void print(ostream &os) { os<<"size(): "<::max(); // Referencia a una subcadena, si la cadena a la que se hace referencia llegara a ser modificada, // esta referencia ya no es válida. // Es útil cuando se quiere conservar la cadena original y no se desea crear otras cadenas que // repitan parte del contenido, pero se quiere hacer referencia a esas partes como un objeto independiente. // Lea la clase amv::url para ver una aplicación de este concepto. // Es importante notar que no se proporciona la función c_str() // porque no está garantizado que el caracter siguiente al último sea 0, por lo cual se proporcionan // las funciones sobrecargadas strcpy, etc. // No existe conversión implícita a char * para evitar que se modifique // involuntariamente la cadena original o que hayan modificaciones fuera de los límites de la subcadena, // pero si en realidad desea hacer eso a partir de un objeto de esta clase, abajo se mencionan funciones // que lo permiten (devolviendo char * o iterator, o bien un char &) // no olvide tener en cuenta el tamaño de la subcadena cuando use estas funciones. // Al principio no se pensaba una subcadena para ser modificada, solo para acceder a segmentos de la cadena // original, pero dado que ya tenemos en ella la referencia a dichos segmentos y para facilitarle la vida // a los usuarios de la clase, se han proporcionado las funciones que fuerzan el const char * a char * // de manera que podamos modificar la cadena original: // iterator begin(), iterator end() y char &operator[](size_type i) // sin embargo, debe tenerse especial cuidado al utilizarlas pues modifican la cadena original, por lo que // debemos considerar 2 puntos: // 1. No escribir caracteres nulos ('\0') // 2. No escribir fuera de los límites de la subcadena [ begin(),end() ), pues no se sabe que hay fuera de // esos límites ni se sabe si pertenece al bloque de memoria de la cadena original. // No tomar en cuenta los puntos anteriores puede afectar negativamente el estado de la cadena original // para el caso de cadenas terminadas en 0 como la bcstring y que tienen un registro del tamaño en una variable // interna. Por otra parte, si la referencia es a una cadena del compilador, no se recomienda modificarlas // porque producirá errores. // Si se utiliza substring para referenciar cadenas de tipo distinto a bcstring, los iteradores deben corresponder // a const char * y char * para const_iterator e iterator respectivamente. class substring { const char *ps; // Apuntador al inicio de la cadena size_t n; // Número de caracteres public: typedef size_t size_type; typedef bcstring::iterator iterator; typedef bcstring::const_iterator const_iterator; substring(const char *s=0, size_type nc=bcstring::npos) { if(s) { ps=s; if(nc==bcstring::npos) n=::strlen(s); else n=nc; } else { ps=bcstring::pes; n=0; } } substring(const char *sb, const char *se) { ps=sb; n=se-sb; } substring &operator=(const char *s) { return assign(s,::strlen(s)); } substring &assign(const char *s, size_type nc=bcstring::npos) { ps=s; if(nc==bcstring::npos) n=::strlen(s); else n=nc; return *this; } bcstring operator+(const substring &s) const { bcstring bcs(ps,n); bcs+=s; //agregar operator+=substring a bcstring o add(s,n) return bcs; } bcstring operator+(const char *s) const { bcstring bcs(ps,n); bcs+=s; //agregar operator+=substring a bcstring o add(s,n) return bcs; } const char &operator[](size_type i) const { return *(ps+i); } char &operator[](size_type i) { return *((char *)(ps+i)); } const_iterator begin() const { return ps; } iterator begin() { return (char *)ps; } const_iterator end() const { return ps+size(); } iterator end() { return (char *)(ps+size()); } bool operator==(const substring &ss) const { if(n!=ss.n) return false; else return strncmp(ps,ss.ps,n)==0; } bool operator!=(const substring &ss) const { return !(*this==ss); } // Comparacion lexicográfica bool operator<(const substring &ss) const { if(n!=ss.n) { int res=strncmp(ps,ss.ps,amv::min(n,ss.n)); if(res<0 || (res==0 && n(const substring &ss) const { return (!(*this~bcstring(); ps=0; return *this; } inline bcstring &bcstring::operator+=(const substring &ss) { if(!ss.ps) return *this; return add(ss.ps,ss.n); } // Devuelve la subcadena propia, es decir, la misma cadena como una subcadena inline bcstring::operator substring() const { return substring(ps,size()); } inline substring bcstring::substr() const { return substring(ps,size()); } // Encuentra el elemento i ([0,n]) tomando como separador ls substring bcstring::gettoken(const char *ls, size_type i) const { if(ps) { char *pc=ps, *plt=ps, *p; size_type n=0, nc=strlen(ls); while(p=strstr(pc,ls)) { plt=pc; pc=p+nc; if(n==i) break; else ++n; } if(n!=i) return substring(); if(p) { return substring(plt,p-plt); } else { if(pc==ps) return *this; else return substring(pc,end()-pc); } } return substring(); } int sprintf(bcstring &bs, const char *format,...) { // El evaluador deberia ser global y estar preparado para multihilo // Una mejora importante para el rendimiento sería codificar el autómata aquí mismo // %[flags] [width] [.precision] [{h | l | I64 | L}]type // por ahora omitimos lo que está entre llaves ( {} ) static REEvaluator re="%<(\\-|\\+|0|''|\\#)?<(\\*|([0-9]+))?<(.<(\\*|([0-9]+)))?<(c|C|d|i|o|u|x|X|e|E|f|g|G|n|p|s|S)"; char buf[1024]; istrstream *pis; const char *p=format; va_list vl, vllast; const char *s; bcstring bcsx, *pbcs; substring *pss; string s1; char c; int na; va_start(vl,format); while(*p) { if(*p=='%') { if(*++p==0) break; else { switch(*p) { case '%': bcsx+='%'; break; case 's': s=va_arg(vl, const char *); bcsx+=s; //cout<<"char * leido\n"; break; #ifdef _WIN32 case 'b': //bcs=va_arg(vl, bcstring); pbcs=(bcstring *)vl; // Utilizamos el apuntador para prescindir del operador de asignación va_arg(vl, bcstring); // Avanzamos al siguiente argumento bcsx+=*pbcs; //cout<<"bcstring leido\n"; break; case '$': //ss=va_arg(vl, substring); pss=(substring *)vl; va_arg(vl, substring); bcsx+=*pss; //cout<<"substring leido\n"; break; #endif default: { pis=new istrstream (p-1,strlen(p-1)); if(re.getLexema(*pis,s1)) { //cout<<"ree"<