Comparar cadenas es inherentemente diferente a comparar
enteros. Los nombres tienen un significado universal y
constante. Para evaluar la relación entre las magnitudes de
dos cadenas, se necesita hacer una comparación léxica. Una
comparación léxica significa que cuando se comprueba un
carácter para saber si es "mayor que" o
"menor que" otro carácter, está en
realidad comparando la representación numérica de aquellos
caracteres tal como están especificados en el orden del
conjunto de caracteres que está siendo usado. La ordenación
más habitual suele ser la secuencia ASCII, que asigna a los
caracteres imprimibles para el lenguaje inglés números en un
rango del 32 al 127 decimal. En la codificación ASCII, el
primer "carácter" en la lista es el
espacio, seguido de diversas marcas de puntuación común, y
después las letras mayúsculas y minúsculas. Respecto al
alfabeto, esto significa que las letras cercanas al principio
tienen un valor ASCII menor a aquellos más cercanos al
final. Con estos detalles en mente, se vuelve más fácil
recordar que cuando una comparació léxica reporta que
s1
es "mayor que"
s2
, simplemente significa que cuando fueron
comparados, el primer carácter diferente en
s1
estaba atrás en el alfabeto que el
carácter en la misma posición en s2
.
C++ provee varias maneras de comparar cadenas, y cada una
tiene ventajas. La más simple de usar son las funciones
no-miembro sobrecargadas de operador:
operator==
,
operator!=
operator>
,
operator<
,
operator>=
y
operator<=
.
//: C03:CompStr.h #ifndef COMPSTR_H #define COMPSTR_H #include <string> #include "../TestSuite/Test.h" using std::string; class CompStrTest : public TestSuite::Test { public: void run() { // Strings to compare string s1("This"); string s2("That"); test_(s1 == s1); test_(s1 != s2); test_(s1 > s2); test_(s1 >= s2); test_(s1 >= s1); test_(s2 < s1); test_(s2 <= s1); test_(s1 <= s1); } }; #endif // COMPSTR_H ///:~
Listado 4.22. C03/CompStr.h
//: C03:CompStr.cpp //{L} ../TestSuite/Test #include "CompStr.h" int main() { CompStrTest t; t.run(); return t.report(); } ///:~
Listado 4.23. C03/CompStr.cpp
Los operadores de comaración sobrecargados son útiles para comparar dos cadenas completas y elementos individuales de una cadena de caracteres.
Nótese en el siguiente ejemplo la flexibilidad de los tipos de
argumento ambos lados de los operadores de comparación. Por
eficiencia, la clase string
provee
operadores sobrecargados para la comparación directa de
objetos string
, literales de cadena, y
punteros a cadenas estilo C sin tener que crear objetos
string
temporales.
//: C03:Equivalence.cpp #include <iostream> #include <string> using namespace std; int main() { string s2("That"), s1("This"); // The lvalue is a quoted literal // and the rvalue is a string: if("That" == s2) cout << "A match" << endl; // The left operand is a string and the right is // a pointer to a C-style null terminated string: if(s1 != s2.c_str()) cout << "No match" << endl; } ///:~
Listado 4.24. C03/Equivalence.cpp
La función c_str()
retorna un const
char*
que apunta a una cadena estilo C terminada en
nulo, equivalente en contenidos al objeto
string
. Esto se vuelve muy útil cuando
se quiere pasar un strin a una función C, como atoi() o
cualquiera de las funciones definidas en la cabecera
cstring. Es un error usar el valor
retornado por c_str()
como un argumento
constante en cualquier función.
No encontrará el operador not
(!
) o los operadores de comparación
lógicos (&&
y
||
) entre los operadore para
string
. (No encontrará ninguna versión
sobrecargada de los operadores de bits de C: &, |, ^, o
~.) Los operadores de conversión no miembros sobrecargados
para la clases string
están limitados a
un subconjunto que tiene una aplicación clara y no ambigua
para caracteres individuales o grupos de caracteres.
La función miembro compare()
le ofrece un
gran modo de comparación más sofisticado y preciso que el
conjunto de operadores nomiembro. Provee versiones
sobrecargadas para comparar:
Dos string
completos
Parte de un string
con un string
completo
Partes de dos string
//: C03:Compare.cpp // Demonstrates compare() and swap(). #include <cassert> #include <string> using namespace std; int main() { string first("This"); string second("That"); assert(first.compare(first) == 0); assert(second.compare(second) == 0); // Which is lexically greater? assert(first.compare(second) > 0); assert(second.compare(first) < 0); first.swap(second); assert(first.compare(second) < 0); assert(second.compare(first) > 0); } ///:~
Listado 4.25. C03/Compare.cpp
La función swap()
en este ejemplo hace lo
que su nombre implica: cambia el contenido del objeto por el del
parámetro. Para comparar un subconjunto de caracteres en un o
ambos string
, añada argumentos que
definen donde empezar y cuantos caracteres considerar. Por
ejemplo, puede usar las siguientes versiones sobrecargadas de
compare()
:
s1.compare(s1StartPos, s1NumberChars, s2, s2StartPos,
s2NumberChars);
Aqui un ejemplo:
//: C03:Compare2.cpp // Illustrate overloaded compare(). #include <cassert> #include <string> using namespace std; int main() { string first("This is a day that will live in infamy"); string second("I don't believe that this is what " "I signed up for"); // Compare "his is" in both strings: assert(first.compare(1, 7, second, 22, 7) == 0); // Compare "his is a" to "his is w": assert(first.compare(1, 9, second, 22, 9) < 0); } ///:~
Listado 4.26. C03/Compare2.cpp
Hasta ahora, en los ejemplos, hemos usado la sintaxis de
indexación de arrays estilo C para referirnos a un carácter
individual en un string
. C++ provee de
una alternativa a la notación s[n]
: el miembro
at()
. Estos dos mecanismos de indexación
producen los mismos resultados si todo va bien:
//: C03:StringIndexing.cpp #include <cassert> #include <string> using namespace std; int main() { string s("1234"); assert(s[1] == '2'); assert(s.at(1) == '2'); } ///:~
Listado 4.27. C03/StringIndexing.cpp
Sin embargo, existe una importante diferencia entre [
]
y at()
. Cuando usted intenta
referenciar el elemento de un arreglo que esta fuera de sus
límites, at()
tiene la delicadeza de lanzar
una excepción, mientras que ordinariamente [
]
le dejará a su suerte.
//: C03:BadStringIndexing.cpp #include <exception> #include <iostream> #include <string> using namespace std; int main() { string s("1234"); // at() saves you by throwing an exception: try { s.at(5); } catch(exception& e) { cerr << e.what() << endl; } } ///:~
Listado 4.28. C03/BadStringIndexing.cpp
Los programadores responsables no usarán índices erráticos, pero
puede que quiera beneficiarse de la comprobación automática de
indices, usandoat()
en el lugar de
[ ]
le da la oportunidad de recuperar
diligentemente de las referencias a elementos de un arreglo que
no existen. La ejecución de sobre uno de nuestros compiladores
le da la siguiente salida: "invalid string
position"
La función miembro at()
lanza un objeto de
clase out_of_class
, que deriva finalmente
de std::exception
. Capturando este objeto
en un manejador de excepciones, puede tomar las medidas
adecuadas como recalcular el índice incorrecto o hacer crecer el
arreglo. Usar
string::
operator[](
)
no proporciona ningún tipo de protección y es tan
peligroso como el procesado de arreglos de caracteres en C.[37]
[7]
[7] Por las razones de seguridad mencionadas, el
C++ Standards Committee está
considerando una propuesta de redefinición del
string::
operator[]
para comportarse de manera idéntica al
string::
at()
para C++0x.