Java soluciones de programación

545  19  Descargar (0)

Texto completo

(1)
(2)
(3)

Java, C, C++ y C#, y es un maestro programador en Windows. Se han vendido más de 3.5 millones de ejemplares de sus libros sobre programación y se han traducido a todos los idiomas importantes. Es autor de gran cantidad de bestsellers de Java, incluidos Java: Manual de referencia, Java: Manual de referencia y Fundamentos de Java. Entre sus otros bestsellers se incluyen Manual de referencia y El arte de programar en Java. Schildt tiene grado universitario y maestría de la Universidad de Illinois. Su sitio Web es

(4)

Soluciones de programación

Herbert Schildt

Traducción

Eloy Pineda Rojas

Traductor profesional

MÉXICO • BOGOTÁ • BUENOS AIRES • CARACAS • GUATEMALA • LISBOA • MADRID NUEVA YORK • SAN JUAN • SANTIAGO • AUCKLAND • LONDRES • MILÁN

(5)

Supervisora de producción: Jacqueline Brieño Álvarez Formación: Gráfica FX

JAVA Soluciones de programación

Prohibida la reproducción total o parcial de esta obra, por cualquier medio, sin la autorización escrita del editor.

DERECHOS RESERVADOS © 2009 respecto a la primera edición en español por McGRAW-HILL INTERAMERICANA EDITORES, S.A. DE C.V.

A Subsidiary of The McGraw-Hill Companies, Inc.

Corporativo Punta Santa Fe

Prolongación Paseo de la Reforma 1015 Torre A Piso 17, Colonia Desarrollo Santa Fe,

Delegación Álvaro Obregón C.P. 01376, México, D. F.

Miembro de la Cámara Nacional de la Industria Editorial Mexicana, Reg. Núm. 736 ISBN10 : 970-10-6756-8

ISBN13 : 978-970-10-6756-7

Translated from the 1st English edition of

Herb Schildt’s Java Programming Cookbook

By: Herbert Schildt

Copyright © 2008 by The McGraw-Hill Companies. All rights reserved.

ISBN: 978-0-07-226315-2

1234567890 0876543219

(6)

Contenido

v

Prefacio xix

1 Revisión general 1

¿Qué encontrará en el interior? 1

¿Cómo están organizadas las recetas? 2

Una cuantas palabras de precaución 3

Es necesaria experiencia en Java 3

¿Qué versión de Java? 4

2 Trabajo con cadenas y expresiones regulares 5

Una revisión general de las clases de cadena de Java 6

La API de expresiones regulares de Java 8

Introducción a las expresiones regulares 8

Caracteres normales 9

Clases de caracteres 9

El carácter comodín 10

Cuantificadores 10

Cuantificadores avaros, renuentes y posesivos 11

Comparadores de límites 11

El operador O 11

Grupos 12

Secuencias de marcas 13

Recuerde incluir el carácter de escape \ en cadenas de Java 13

Ordene una matriz de cadenas de manera inversa 14

Paso a paso 14

Análisis 14

Ejemplo 16

Opciones 17

Ignore las diferencias entre mayúsculas y minúsculas cuando ordene una matriz de cadenas 18

Paso a paso 18

Análisis 19

Ejemplo 19

Opciones 21

Ignore las diferencias entre mayúsculas y minúsculas cuando busque o reemplace subcadenas 22

Paso a paso 22

Análisis 22

Ejemplo 23

Opciones 24

Divida una cadena en partes empleando split( ) 25

(7)

Análisis 26

Ejemplo 26

Opciones 28

Recupere pares clave/valor de una cadena 28

Paso a paso 29

Análisis 29

Ejemplo 29

Opciones 32

Compare y extraiga subcadenas empleando la API de expresiones regulares 32

Paso a paso 33

Análisis 33

Ejemplo 33

Opciones 34

Divida en fichas una cadena empleando la API de expresiones regulares 35

Paso a paso 36 Análisis 37 Ejemplo 38 Ejemplo adicional 40 Opciones 47 3 Manejo de archivos 49

Una revisión general del manejo de archivos 50

Flujos 50

La clase RandomAccessFile 53

La clase File 54

Las interfaz de E/S 55

Los flujos de archivos comprimidos 57

Lea bytes de un archivo 59

Paso a paso 59

Análisis 59

Ejemplo 60

Opciones 61

Escriba bytes en un archivo 62

Paso a paso 63

Análisis 63

Ejemplo 63

Opciones 64

Use el búfer para la E/S de un archivo basada en bytes 65

Paso a paso 66

Análisis 66

Ejemplo 66

Opciones 68

Lea caracteres de un archivo 69

(8)

Análisis 69

Ejemplo 70

Opciones 71

Escriba caracteres en un archivo 72

Paso a paso 72

Análisis 73

Ejemplo 73

Opciones 74

Use el búfer para la E/S de un archivo basada en caracteres 75

Paso a paso 76

Análisis 76

Ejemplo 77

Opciones 79

Lea y escriba archivos de acceso aleatorio 80

Paso a paso 80

Análisis 80

Ejemplo 81

Opciones 83

Obtenga atributos de archivos 83

Paso a paso 84

Análisis 84

Ejemplo 84

Opciones 86

Establezca atributos de archivos 86

Paso a paso 87

Análisis 87

Ejemplo 87

Opciones 89

Elabore una lista de un directorio 90

Paso a paso 90

Análisis 90

Ejemplo 91

Ejemplo adicional 93

Opciones 94

Comprima y descomprima datos 95

Paso a paso 95

Análisis 96

Ejemplo 96

Opciones 99

Cree un archivo ZIP 100

Paso a paso 100

Análisis 101

Ejemplo 102

(9)

Descomprima un archivo ZIP 105 Paso a paso 105 Análisis 106 Ejemplo 107 Opciones 109 Serialice objetos 110 Paso a paso 111 Análisis 111 Ejemplo 112 Opciones 115 4 Formato de datos 117

Revisión general de Formatter 118

Fundamentos de formación 119

Especificación de un ancho mínimo de campo 121

Especificación de precisión 121

Uso de las marcas de formato 122

La opción en mayúsculas 122

Uso de un índice de argumentos 123

Cuatro técnicas simples de formación numérica que emplean Formatter 124

Paso a paso 124

Análisis 124

Ejemplo 125

Opciones 126

Alinee verticalmente datos numéricos empleando Formatter 126

Paso a paso 126

Análisis 127

Ejemplo 127

Ejemplo adicional: centro de datos 128

Opciones 131

Justifique a la izquierda la salida con Formatter 131

Paso a paso 131

Análisis 131

Ejemplo 132

Opciones 133

Forme fecha y hora empleando Formatter 133

Paso a paso 134

Análisis 134

Ejemplo 136

Opciones 137

Especifique un idioma local usando Formatter 138

Paso a paso 138

Análisis 138

Ejemplo 139

(10)

Use flujos con Formatter 140

Paso a paso 140

Análisis 140

Ejemplo 141

Opciones 142

Use printf( ) para desplegar datos formados 143

Paso a paso 143

Análisis 143

Ejemplo 144

Ejemplo adicional 145

Opciones 145

Forme fecha y hora con DateFormat 146

Paso a paso 147

Análisis 148

Ejemplo 148

Opciones 149

Forme fecha y hora con patrones empleando SimpleDateFormat 150

Paso a paso 151

Análisis 151

Ejemplo 152

Opciones 153

Forme valores numéricos con NumberFormat 153

Paso a paso 154

Análisis 154

Ejemplo 155

Opciones 156

Forme valores monetarios usando NumberFormat 156

Paso a paso 157

Análisis 157

Ejemplo 157

Opciones 157

Forme valores numéricos con patrones empleando DecimalFormat 158

Paso a paso 158

Análisis 158

Ejemplo 159

Opciones 160

5 Trabajo con colecciones 161

Revisión general de las colecciones 162

Tres cambios recientes 163

Las interfaz de Collection 164

Las clases de la colección 173

La clase ArrayList 173

La clase LinkedList 174

La clase HashSet 175

(11)

La clase TreeSet 176

La clase PriorityQueue 176

La clase ArrayDeque 177

La clase EnumSet 178

Revisión general de los mapas 178

Las interfaz de Map 178

Las clases de Map 183

Algoritmos 185

Técnicas básicas de colecciones 186

Paso a paso 187

Análisis 187

Ejemplo 188

Opciones 190

Trabaje con listas 191

Paso a paso 191

Análisis 192

Ejemplo 192

Opciones 195

Trabaje con conjuntos 195

Paso a paso 196

Análisis 196

Ejemplo 197

Ejemplo adicional 198

Opciones 201

Use Comparable para almacenar objetos en una colección ordenada 201

Paso a paso 202

Análisis 202

Ejemplo 203

Opciones 204

Use un Comparator con una colección 205

Paso a paso 205

Análisis 205

Ejemplo 206

Opciones 209

Itere en una colección 209

Paso a paso 210

Análisis 210

Ejemplo 211

Opciones 213

Cree una cola o una pila empleando Deque 214

Paso a paso 214

Análisis 215

Ejemplo 216

(12)

Invierta, gire y ordene al azar una List 218

Paso a paso 219

Análisis 219

Ejemplo 219

Opciones 220

Ordene una List y busque en ella 221

Paso a paso 221

Análisis 221

Ejemplo 222

Opciones 223

Cree una colección comprobada 224

Paso a paso 224

Análisis 224

Ejemplo 225

Opciones 227

Cree una colección sincronizada 227

Paso a paso 228

Análisis 228

Ejemplo 228

Opciones 231

Cree una colección inmutable 231

Paso a paso 231

Análisis 232

Ejemplo 232

Opciones 233

Técnicas básicas de Map 233

Paso a paso 234

Análisis 235

Ejemplo 235

Opciones 238

Convierta una lista de Properties en un HashMap 238

Paso a paso 239

Análisis 239

Ejemplo 239

Opciones 240

6 Applets y servlets 241

Revisión general de las applets 241

La clase Applet 242

Arquitectura de Applet 244

El ciclo de vida de la applet 245

Las interfaz AppletContext, AudioClip y AppletStub 246

Revisión general de la servlet 246

El paquete javax.servlet 246

(13)

La clase HttpServlet 251

La clase Cookie 251

El ciclo de vida de la servlet 253

Uso de Tomcat para desarrollo de servlets 254

Cree un esqueleto de Applet basado en AWT 255

Paso a paso 256

Análisis 256

Ejemplo 256

Opciones 257

Cree un esqueleto de Applet basado en Swing 257

Paso a paso 258

Análisis 258

Ejemplo 259

Opciones 260

Cree una GUI y maneje sucesos en una Applet de Swing 260

Paso a paso 261

Análisis 261

Nota histórica: getContentPane( ) 263

Ejemplo 263

Ejemplo adicional 266

Opciones 268

Pinte directamente en la superficie de la Applet 269

Paso a paso 269

Análisis 270

Ejemplo 271

Opciones 273

Pase parámetros a Applets 275

Paso a paso 275

Análisis 275

Ejemplo 276

Opciones 277

Use AppletContext para desplegar una página Web 278

Paso a paso 278

Análisis 278

Ejemplo 278

Opciones 281

Cree una servlet simple usando GenericServlet 282

Paso a paso 282

Análisis 282

Ejemplo 283

Opciones 284

Maneje solicitudes HTTP en una servlet 185

Paso a paso 285

Análisis 285

(14)

Ejemplo adicional 287

Opciones 290

Use una cookie con una servlet 290

Paso a paso 290

Análisis 290

Ejemplo 291

Opciones 293

7 Multiprocesamiento 295

Fundamentos del multiprocesamiento

La interfaz Runnable 297

La clase Thread 298

Cree un subproceso al implementar Runnable 299

Paso a paso 300

Análisis 300

Ejemplo 300

Opciones 303

Cree un subproceso al extender Thread 304

Paso a paso 305

Análisis 305

Ejemplo 305

Opciones 306

Use el nombre y el ID de un subproceso 307

Paso a paso 307

Análisis 308

Ejemplo 308

Opciones 310

Espere a que termine un subproceso 311

Paso a paso 311 Análisis 311 Ejemplo 312 Opciones 313 Sincronice subprocesos 314 Paso a paso 315 Análisis 315 Ejemplo 316 Opciones 318

Establezca comunicación entre subprocesos 318

Paso a paso 319

Análisis 319

Ejemplo 320

Opciones 322

Suspenda, reanude y detenga un subproceso 323

(15)

Análisis 324

Ejemplo 325

Opciones 327

Use un subproceso de daemon 328

Paso a paso 329

Análisis 329

Ejemplo 329

Ejemplo adicional: una clase simple de recordatorio 331

Opciones 336 Interrumpa un subproceso 336 Paso a paso 337 Análisis 337 Ejemplo 337 Opciones 339

Establezca y obtenga una prioridad de subproceso 341

Paso a paso 341

Análisis 342

Ejemplo 342

Opciones 344

Monitoree el estado de un subproceso 344

Paso a paso 345

Análisis 345

Ejemplo 346

Ejemplo adicional: un monitor de subprocesos en tiempo real 349

Opciones 353

Use un grupo de subprocesos 353

Paso a paso 354

Análisis 354

Ejemplo 355

Opciones 357

8 Swing 359

Revisión general de Swing 360

Componentes y contenedores 361

Componentes 362

Contenedores 362

Los paneles de contenedor de nivel superior 363

Revisión general del administrador de diseño 363

Manejo de sucesos 364

Sucesos 365

Orígenes de sucesos 365

Escuchas de sucesos 365

Cree una aplicación simple de Swing 366

Paso a paso 366

(16)

Nota histórica: getContentPane( ) 369

Ejemplo 369

Opciones 371

Establezca el administrador de diseño del panel de contenido 372

Paso a paso 372

Análisis 372

Ejemplo 373

Opciones 375

Trabaje con JLabel 376

Paso a paso 376

Análisis 377

Ejemplo 379

Opciones 382

Cree un botón simple 383

Paso a paso 384

Análisis 384

Ejemplo 385

Opciones 387

Use iconos, HTML y mnemotécnica con JButton 390

Paso a paso 391

Análisis 391

Ejemplo 393

Opciones 395

Cree un botón interruptor 396

Paso a paso 397

Análisis 397

Ejemplo 398

Opciones 400

Cree casillas de verificación 400

Paso a paso 401

Análisis 401

Ejemplo 401

Opciones 405

Cree botones de opción 405

Paso a paso 406

Análisis 406

Ejemplo 407

Opciones 410

Ingrese texto con JTextField 411

Paso a paso 411

Análisis 412

Ejemplo 413

Ejemplo adicional: cortar, copiar y pegar 416

Opciones 419

(17)

Paso a paso 420

Análisis 420

Ejemplo 422

Opciones 424

Use una barra de desplazamiento 426

Paso a paso 427

Análisis 427

Ejemplo 429

Opciones 431

Use JScrollPane para manejar el desplazamiento 433

Paso a paso 433

Análisis 433

Ejemplo 433

Opciones 436

Despliegue datos en una JTable 438

Paso a paso 439

Análisis 440

Ejemplo 441

Opciones 444

Maneje sucesos de JTable 446

Paso a paso 447

Análisis 447

Ejemplo 450

Opciones 455

Despliegue datos en un JTree 456

Paso a paso 458

Análisis 458

Ejemplo 461

Opciones 464

Cree un menú principal 466

Paso a paso 467

Análisis 467

Ejemplo 469

Opciones 471

9 Miscelánea 473

Acceda a un recurso mediante una conexión HTTP 474

Paso a paso 474 Análisis 474 Ejemplo 475 Opciones 476 Use un semáforo 480 Paso a paso 481 Análisis 482 Ejemplo 482 Opciones 485

(18)

Devuelva un valor de un subproceso 486

Paso a paso 487

Análisis 487

Ejemplo 488

Opciones 491

Use reflexión para obtener información acerca de una clase en tiempo de ejecución 491

Paso a paso 492

Análisis 492

Ejemplo 493

Ejemplo adicional: una utilería de reflexión 494

Opciones 496

Use reflexión para crear dinámicamente un objeto y llamar métodos 496

Paso a paso 497

Análisis 497

Ejemplo 498

Opciones 501

Cree una clase personalizada de excepción 501

Paso a paso 502

Análisis 502

Ejemplo 504

Opciones 505

Calendarice una tarea para ejecución futura 506

Paso a paso 507

Análisis 507

Ejemplo 508

Opciones 510

(19)
(20)

xix

D

urante muchos años, amigos y lectores me han pedido que escriba un libro de soluciones

para Java, compartiendo algunas de las técnicas y métodos que utilizo cuando programo. Desde el principio me gustó la idea, pero no lograba darme tiempo para ella con un calendario de escritura muy ocupado. Como muchos lectores saben, escribo acerca de muchas facetas de programación, con énfasis especial en Java, C/C++ y C#. Debido a los rápidos ciclos de revisión de estos lenguajes, dedico casi todo mi tiempo disponible a actualizar mis libros para que cubran las versiones más recientes de esos lenguajes. Por fortuna, a principios de 2007 se abrió una ventana de oportunidad y fi nalmente pude dedicar tiempo a escribir este libro de soluciones de Java. Debo admitir que rápidamente se volvió uno de los proyectos que más he disfrutado.

Este libro destila la esencia de muchas técnicas de propósito general en un conjunto de soluciones paso a paso. En cada solución se describe un conjunto de componentes clave, como clases, interfaz y métodos. Luego se muestran los pasos necesarios para ensamblar esos componentes en una secuencia de código que logre los resultados deseados. Esta organización facilita la búsqueda de técnicas en que está interesado y luego ponerlas en acción.

En realidad, “en acción” es una parte importante de este libro. Creo que los buenos libros de programación contienen dos elementos: teoría sólida y aplicación práctica. En las soluciones, las instrucciones paso a paso y los análisis proporcionan la teoría. Para llevar esa teoría a la práctica, cada solución incluye un ejemplo completo de código. En los ejemplos se demuestra de manera concreta, sin ambigüedades, la manera en que pueden aplicarse. En otras palabras, en los ejemplos se eliminan las “adivinanzas” y se ahorra tiempo.

Aunque ningún libro puede incluir todas las soluciones que pudieran desearse (hay un número casi ilimitado de soluciones posibles), traté de abarcar un amplio rango de temas. Mis criterios para incluir una solución se analizan de manera detallada en el capítulo 1, pero, en resumen, incluí las que serían útiles para muchos programadores y que responderían las preguntas más frecuentes. Aún con estos criterios, fue difícil decidir qué incluir y qué dejar fuera. Ésta fue la parte más desafiante de la escritura del libro. Al final, se impusieron la experiencia, el juicio y la intuición. Por fortuna, ¡he incluido algo para satisfacer al gusto de cada programador!

(21)

Código de ejemplo en Web

El código fuente para todos los ejemplos de este libro está disponible de manera gratuita en Web en http://www.mcgraw-hill-educacion.com/

Más de Herbert Schildt

Java, Soluciones de programación es sólo uno de los muchos libros de programación de Herb. He aquí algunos otros que le resultarán de interés:

Para aprender más acerca de Java recomendamos: Java: Manual de referencia

Fundamentos de Java Java: Manual de referencia

Para aprender más acerca de C++, estos libros le resultarán especialmente útiles. C++: Soluciones de programación

C++: A Begginer’s Guide C++ The complete reference

STL Programming From the Ground Up The Art of C++

Para aprender acerca de C#, sugerimos los siguientes libros de Schildt: C#: The Complete Reference

C#: A Begginer’s Guide

Si quiere aprender acerca del lenguaje C, entonces le interesará el siguiente título: C: The complete reference

Cuando necesite respuestas sólidas, rápidas, busque algo de Herbert Schildt, la autoridad reconocida en programación.

(22)

CAPÍTULO

Revisión general

1

1

E

ste libro es una colección de técnicas que muestra cómo realizar varias tareas de programación en Java. Cada solución ilustra la manera de realizar una operación específi ca. Por ejemplo, hay soluciones que leen bytes de un archivo, iteran una colección, forman datos numéricos, construyen componentes de Swing, crean un servlet, etc. Cada técnica de este libro describe un conjunto de elementos de programa claves y la secuencia de pasos necesarios para usarlos y realizar una tarea de programación.

A fi n de cuentas, el objetivo de este libro es ahorrarle tiempo y esfuerzo durante el desarrollo de programas. Muchas tareas de programación incluyen un conjunto de clases de API, interfaces y métodos que deben aplicarse en una secuencia específi ca. El problema es que a veces no sabe cuáles clases de API usar o en qué orden llamar a los métodos. En lugar de tener que recorrer una gran cantidad de documentación y de tutoriales en línea para determinar la manera de realizar alguna tarea, puede buscar su receta. Cada receta muestra una manera de llegar a una solución, describiendo los elementos necesarios y el orden en que deben usarse. Con esta información puede diseñar una solución que se adecue a sus necesidades específi cas.

¿Qué encontrará en el interior?

Para elegir las soluciones para este libro, me concentré en las siguientes categorías: • Procesamiento de cadenas (incluidas expresiones regulares)

• Manejo de archivos • Formateo de datos • Applets y servlets • Swing

• Las colecciones del marco conceptual • Multiprocesamiento

(23)

Elegí estas categorías porque se relacionan con un amplio rango de programadores (evité temas especializados que se aplican sólo a un subconjunto estrecho de casos). Cada una de estas categorías se vuelve la base de un capítulo. Además de las soluciones relacionadas con los temas anteriores, tengo otros que quiero incluir pero para los cuales no fue posible un capítulo completo. Agrupé esas soluciones en el capítulo fi nal.

Por supuesto, la elección de temas sólo fue el principio del proceso de selección. Dentro de cada categoría, tuve que decidir lo que se incluía y lo que se dejaba fuera. En general, incluí una solución si cumplía los dos criterios siguientes:

1. La técnica es útil para un amplio rango de programadores.

2. Proporciona una respuesta a una pregunta frecuente de programación.

El primer criterio se explica por sí solo y se basa en mi experiencia. Incluí soluciones que describen la manera de realizar un conjunto de tareas que se encontrarían comúnmente cuando se crean aplicaciones de Java. Algunas de ellas ilustran un concepto general que puede adaptarse para resolver varios tipos diferentes de problemas. Por ejemplo, en el capítulo 2 se muestra una solución que usa la API de expresión regular para buscar y extraer subcadenas de una cadena. Este procedimiento general es útil en varios contextos, como encontrar una dirección de correo electrónico, o un número telefónico dentro de una frase, o extraer una palabra clave de una consulta de base de datos. Otras soluciones describen técnicas más específi cas pero de uso más amplio. Por ejemplo, en el capítulo 4 se muestra la manera de formar la fecha y hora usando SimpleDateFormat.

El segundo criterio se basa en mi experiencia como autor de libros de programación. A través de los muchos años que llevo escribiendo, los lectores me han hecho miles y miles de preguntas del tipo “¿Cómo lo hago?”. Estas preguntas vienen de todas las áreas de la programación de Java y van de las muy fáciles a las muy difíciles. Sin embargo, he encontrado que un núcleo central de preguntas surge una y otra vez. He aquí un ejemplo: “¿Cómo formo la salida?”. He aquí otra: “¿Cómo comprimo un archivo?”. Hay muchas otras. Este mismo tipo de preguntas también se presenta con frecuencia en varios foros de programadores en Web. He utilizado estas preguntas comunes como guía para mi selección de soluciones.

Las soluciones de este libro abarcan varios niveles de habilidad. Algunas ilustran técnicas básicas, como la lectura de bytes de un archivo o la creación de una JTable de Swing. Otras son más avanzadas, como la creación de una servlet o el uso de refl ejo para crear una instancia de un objeto en tiempo de ejecución. Por tanto, el nivel de difi cultad de una solución individual puede ir de relativamente fácil a muy avanzada. Por supuesto, casi todo en programación es fácil una vez que sabe cómo hacerlo, pero es difícil cuando no lo sabe. Por tanto, no se sorprenda si algunas soluciones parecen obvias. Eso sólo signifi ca que ya sabe cómo realizar esa tarea.

¿Cómo están organizadas las soluciones?

Cada solución de este libro sigue el mismo formato, que tiene las siguientes partes: • Una descripción del problema que resuelve.

• Una tabla de componentes clave usados. • Los pasos necesarios para completar la solución. • Un análisis a profundidad de los pasos.

• Un ejemplo de código que pone la solución en acción. • Opciones que sugieren otras maneras de llegar a la solución.

(24)

Una solución empieza por describir la tarea que se realizará. Los componentes clave empleados se muestran en una tabla. Ésta incluye las clases de API, las interfaces y los métodos necesarios para crear una solución. Por supuesto, tal vez para ponerla en práctica se requiera el uso de elementos adicionales, pero los componentes clave son los fundamentales para la tarea a mano.

Cada solución presenta después instrucciones paso a paso que resumen el procedimiento. Estas son seguidas por un análisis a profundidad de los pasos. En muchos casos el resumen bastará, pero los detalles están allí, si los necesita.

A continuación, se presenta un ejemplo de código que muestra la solución en acción. Todos los ejemplos de código se presentan completos. Esto evita la ambigüedad y le permite ver con precisión y claramente lo que está sucediendo sin tener que llenar los detalles adicionales. En ocasiones, se incluye un ejemplo extra que ilustra un poco más cómo puede aplicarse la solución.

Cada una concluye con un análisis de varias opciones. Esta sección resulta especialmente importante porque sugiere diferentes maneras de implementar una solución u otras maneras de pensar acerca del problema.

Unas cuantas palabras de precaución

Hay unos cuantos elementos importantes que debe tener en cuenta cuando use este libro.

En primer lugar, se muestra una manera de llegar a una solución. Es probable (y a menudo sucede) que haya otras maneras. Es probable que su aplicación específi ca requiera un método diferente del mostrado. Las soluciones de este libro pueden servir como puntos de partida y ayudarle a elegir un acercamiento general a una solución, además de que pueden despertar su imaginación. Sin embargo, en todos los casos, debe determinar lo que es apropiado y lo que no lo es para su aplicación.

En segundo lugar, es importante comprender que los ejemplos de código no tienen un

rendimiento óptimo. En realidad, están optimizados para ser claros y de fácil comprensión. El objetivo es ilustrar de manera evidente los pasos de la solución. En muchos casos tendrá pocos problemas para escribir un código más condensado y efi ciente. Además, los ejemplos no son más que eso: ejemplos. Hay usos simples que no refl ejan necesariamente la manera en que escribirá código para su propia aplicación. En todas las circunstancias, debe crear una solución propia que satisfaga las necesidades de su aplicación.

En tercer lugar, cada ejemplo contiene un manejo de errores que resulta apropiado para ese ejemplo específi co, pero que tal vez no lo sea en otras situaciones. En todos los casos, debe manejar de manera apropiada los diversos errores y excepciones que pueden producirse cuando adapta una solución para usarla en su propio código. Permítame dejar en claro de nuevo este punto importante: cuando se implementa una solución, debe proporcionar un manejo apropiado de errores para su aplicación. No puede suponer simplemente que la manera en que se manejan (o no se manejan) los errores o las excepciones en un ejemplo es sufi ciente o adecuada para su uso. Por lo general, en las aplicaciones reales se requerirá el manejo de excepciones.

Es necesaria experiencia en Java

Este libro está dirigido a todos los programadores en Java, sean principiantes o profesionales con experiencia. Sin embargo, se supone que conoce los fundamentos de la programación en Java, incluidos palabras clave de Java, sintaxis y clases de API básicas. También debe tener la capacidad de crear, compilar y ejecutar programas en Java. Nada de esto se enseña en esta obra. (Como ya se explicó, este libro trata sobre la aplicación de Java a diversos problemas de programación reales.

(25)

No busca enseñar los fundamentos del lenguaje Java). Si necesita mejorar sus habilidades en Java, recomiendo mi libro Java: Manual de referencia, séptima edición. Publicado por McGraw-Hill.

¿Qué versión de Java?

Como muchos lectores lo saben, Java se encuentra en un estado de evolución constante desde su creación. Con cada nueva versión, se agregan características. En muchos casos, con cada nueva versión también se vuelven obsoletas otras características. Como resultado, no todo el código moderno de Java puede compilarse en un compilador antiguo de Java. Esto resulta importante porque el código de este libro se basa en Java SE 6, que (al momento de escribir el libro) es la versión actual de Java. El kit del desarrollador para Java SE 6 es JDK 6. También es el JDK usado para probar todos los ejemplos de código.

Como tal vez ya lo sepa, a partir de JDK 5 se agregaron varias características importantes a Java. Entre éstas se incluyen genéricos, enumeraciones y autoencuadre. Algunas de las técnicas de este libro emplean estas características. Si está utilizando una versión de Java anterior a JDK 5, entonces no podrá compilar los ejemplos que utilizan estas nuevas características. Por tanto, se recomienda mucho que utilice una versión moderna de Java.

(26)

Trabajo con cadenas

y expresiones regulares

5

2

CAPÍTULO

U

na de las tareas de programación más comunes es el manejo de cadenas. Casi todos los

programas tratan con cadenas, de una forma u otra, porque suelen ser el conducto por el cual los seres humanos interactúan con la información digital. Debido a la parte importante que juega el manejo de cadenas, Java le proporciona amplio soporte.

Como lo saben todos los programadores que usan Java, la clase más importante para trabajar con cadenas es String. Proporciona un amplio conjunto de métodos para el manejo de cadenas. Muchos de estos métodos proporcionan las operaciones de cadena básicas con que están familiarizados la mayoría de los programadores que usan Java. Entre éstos se incluyen métodos que comparan dos cadenas, buscan la aparición de una cadena en otra, etc. Sin embargo, String también contiene varios métodos menos conocidos que aumentan de manera importante sus opciones, porque operan con expresiones regulares. Una expresión regular defi ne un patrón general, no una secuencia específi ca de caracteres. Este patrón también puede usarse para buscar subcadenas que coincidan con un patrón. Se trata de un concepto poderoso que está revolucionando la manera en que los programadores que usan Java piensan en el manejo de cadenas.

Java empezó a proporcionar soporte a expresiones regulares en la versión 1.4. Las expresiones regulares están soportadas por la API de expresiones regulares, que está empaquetada en java. util.regex. Como se acaba de explicar, las expresiones regulares también tienen soporte en varios métodos de String. Con la adición de las expresiones regulares, se han facilitado muchas tareas de manejo de cadenas que, de otra manera, serían difíciles.

Este capítulo contiene soluciones que ilustran varias técnicas de manejo de cadenas que van más allá de las operaciones básicas de búsqueda, comparación y reemplazo encontradas en String. Varias también usan expresiones regulares. En algunos casos, se emplean las capacidades de expresión regular de String. En otros se usa la propia API de expresiones regulares.

He aquí las soluciones incluidas en este capítulo: • Ordene una matriz de cadenas de manera inversa

• Ignore las diferencias entre mayúsculas y minúsculas cuando ordene una matriz de cadenas • Ignore las diferencias entre mayúsculas y minúsculas cuando busque o reemplace

subcadenas

• Divida una cadena en partes empleando split( ) • Recupere pares clave/valor de una cadena

(27)

• Compare y extraiga subcadenas empleando la API de expresiones regulares • Divida en fi chas una cadena empleando la API de expresiones regulares

Una revisión general de las clases de cadena de Java

Una cadena es una secuencia de caracteres. A diferencia de otros lenguajes de programación, Java no implementa las cadenas como matrices de caracteres. En cambio, las implementa como objetos. Esto le permite a Java defi nir una serie rica de métodos que actúan sobre las cadenas. Aunque éstas son un territorio familiar para casi todos los programadores que usan Java, aún es útil revisar sus atributos y capacidades clave.

Casi todas las cadenas que usará en un programa son objetos de tipo String. String es parte de java.lang. Por tanto, queda a disposición automáticamente de todos los programas en Java. Uno de los aspectos más interesantes de String es que crea cadenas inmutables. Esto signifi ca que una vez que se crea una instancia de String, no puede modifi carse su contenido. Aunque ésta parece una restricción importante, no lo es. Si necesita cambiar una cadena, simplemente cree una nueva que contenga la modifi cación. La cadena original permanecerá sin cambios. Si ya no se necesita ésta, descártela. La cadena que no se usa será reciclada la próxima vez que se ejecute el recolector de basura. Mediante el uso de cadenas inmutables, String puede implementarse de manera más efi ciente de lo que sería si se usara una modifi cable.

Es posible crear cadenas de varias maneras. Puede construir explícitamente una cadena al usar uno de los constructores de String. Por ejemplo, hay constructores que crean una instancia de String a partir de una matriz de caracteres, una matriz de bytes u otra cadena. Sin embargo, la manera más fácil de crear una cadena consiste en usar una literal de cadena, que es una cadena entre comillas. Todas las literales de cadena son automáticamente objetos de tipo String. Por tanto, una literal de cadena puede asignarse a una referencia a String, como se muestra aquí:

String cad = "Prueba";

Esta línea crea un objeto de String que contiene la palabra "Prueba" y luego le asigna a cad una referencia a ese objeto.

String sólo soporta un operador: +. Éste une dos cadenas. Por ejemplo, String cadA = "Hola,";

String cadB = " allá"; String cadC = cadA + cadB;

Esta secuencia da como resultado cadC, que contiene la secuencia "Hola, allá".

String defi ne varios métodos que operan en cadenas. Debido a que la mayoría de los lectores tienen por lo menos una familiaridad pasable con String, no es necesaria una descripción

detallada de todos sus métodos. Más aún, las soluciones de este capítulo describen por completo los métodos de String que emplean. Sin embargo, es útil revisar las capacidades centrales de manejo de cadenas de String al agruparlas en categorías.

String defi ne los siguientes métodos que buscan el contenido de una cadena en otra: contains Devuelve verdadero si una cadena contiene otra.

endsWith Devuelve verdadero si una cadena termina con una cadena específica.

indexOf Devuelve el índice dentro de una cadena en que se encuentra la primera aparición de otra cadena. Devuelve –1 si no se encuentra la cadena.

lastIndexOf Devuelve el índice dentro de la cadena que invoca en que se encuentra la última aparición de la cadena especificada. Devuelve –1 si no se encuentra la cadena. startsWith Devuelve verdadero si una cadena empieza con una cadena específica.

(28)

Los siguientes métodos comparan una cadena con otra:

compareTo Compara una cadena con otra.

compareToIgnoreCase Compara una cadena con otra. Se ignoran las diferencias entre mayúsculas y minúsculas.

contentEquals Compara una cadena con una secuencia específica de caracteres. equals Devuelve verdadero si dos cadenas contienen la misma

secuencia de caracteres.

equalsIgnoreCase Devuelve verdadero si dos cadenas contienen la misma secuencia de caracteres. Se ignoran las diferencias entre mayúsculas y minúsculas.

matches Devuelve verdadero si una cadena coincide con una expresión regular específica.

regionMatches Devuelve verdadero si la región especificada de una cadena coincide con la región especificada de otra.

Cada uno de los métodos dentro del siguiente grupo reemplaza una parte de una cadena con otra:

replace Reemplaza todas las apariciones de un carácter o una subcadena con otra.

replaceFirst Reemplaza la primera secuencia de caracteres que coincide con una expresión regular específica.

replaceAll Reemplaza todas las secuencias de caracteres que coinciden con una expresión regular específica.

Los siguientes dos métodos cambian las mayúsculas y minúsculas de las letras dentro de una cadena:

toLowercase Convierte la cadena a minúsculas. toUpperCase Convierta la cadena a mayúsculas.

Además de los métodos de manejo de cadena centrales que acabamos de describir, String defi ne otros más. Dos de uso muy común son length( ), que devuelve el número de caracteres en una cadena, y charAt( ), que devuelve el carácter en un índice específi co.

En su mayor parte, las soluciones de este capítulo usan String, y suelen ser su mejor opción cuando trabaja con cadenas. Sin embargo, en los pocos casos en que necesita que se modifi que una cadena, Java ofrece otras dos opciones. La primera es StringBuffer, que ha sido parte de Java desde el principio. Es similar a String, excepto que permite cambiar el contenido de una cadena.

Por tanto, proporciona métodos, como setCharAt( ) e insert( ), que modifi can la cadena. La segunda opción es la más nueva StringBuilder, que se agregó a Java en la versión 1.5. Resulta

(29)

similar a StringBuffer, excepto que no es segura para subprocesos. Por tanto, es más efi ciente cuando no se usan multiprocesamientos. (En aplicaciones con multiprocesamientos, debe usar StringBuffer, porque es segura para subprocesos). Tanto StringBuffer como StringBuilder están empaquetados en java.lang.

La API de expresiones regulares de Java

Las expresiones regulares tienen soporte en Java con las clases Matcher y Pattern, que están empaquetadas en java.util.regex. Estas clases funcionan juntas. Utilizará Pattern para defi nir una expresión regular. Comparará el patrón contra otra sección empleando Matcher. Los procedimientos precisos se describen en las soluciones en que se usan.

Las expresiones regulares también se usan en otras partes de la API de Java. Tal vez lo más importante sea que varios métodos de String, como split( ) y matches( ), aceptan una expresión regular como argumento. Por tanto, con frecuencia usará una expresión regular sin usar explícitamente Pattern o Matcher.

Varias de las soluciones de este capítulo usan expresiones regulares. La mayor parte de ellas lo hacen mediante métodos de String, pero tres de ellas usan explícitamente Pattern y Matcher. Para tener un control detallado del proceso de comparación, a menudo es necesario usar Pattern y Matcher. Sin embargo, en muchos casos la funcionalidad de expresiones regulares que proporciona String es sufi ciente y más conveniente.

Varios métodos que usan expresiones regulares lanzarán una excepción

PatternSyntaxException cuando se hace un intento por usar una expresión regular sintácticamente incorrecta. Esta excepción se defi ne mediante la API de expresiones regulares y está empaquetada en java.util.regex. Necesitará manejar esta excepción de una manera apropiada en su aplicación.

Introducción a las expresiones regulares

Antes de que pueda usar expresiones regulares, debe comprender cómo están construidas. Si es nuevo en las expresiones regulares, entonces esta revisión general le ayudará a iniciarse en ellas. Antes de seguir, es importante establecer que el tema de las expresiones regulares es más bien amplio. En realidad, se han escrito libros completos sobre ellas. Está más allá del alcance de este libro describirlas de manera detallada. En cambio, aquí se presenta una breve introducción que incluye sufi ciente información para que comprenda los ejemplos de las soluciones. También le permitirá empezar a experimentar con expresiones regulares propias. Sin embargo, si las usa de manera reiterada, entonces tendrá que estudiarlas con mucho mayor detalle.

Tal como se usa aquí el término, una expresión regular es una cadena de caracteres que describe un patrón. Un patrón comparará cualquier secuencia de caracteres que satisfaga el patrón. Por tanto, éste constituye una forma general que coincidirá con diversas secuencias específi cas. En conjunto con un motor de expresiones regulares (como las proporcionadas por la API de expresiones regulares de Java), puede usarse un patrón para buscar coincidencias en otra secuencia de caracteres. Es esta capacidad la que da a las expresiones regulares su poder cuando se manipulan cadenas.

Una expresión regular consta de uno o más de los siguientes elementos: caracteres normales, clases de caracteres (conjuntos de caracteres), el carácter comodín, cuantifi cadores, comparadores de límites, operadores y grupos. Aquí se examinará cada uno de manera breve.

(30)

NOTA Hay cierta variación en la manera en que los diferentes motores de expresiones regulares manejan

éstas. En este análisis se analiza la implementación de Java.

Caracteres normales

Un carácter normal (es decir una literal de carácter) se compara tal cual. Por tanto, si un patrón consta de xy, entonces la única secuencia de entrada con la que coincidirá es "xy". Caracteres como nueva línea y tabulador se especifi can empleando secuencias de escape, que empiezan con una \. Por ejemplo, una nueva línea se especifi ca como \n.

Clases de caracteres

Una clase de caracteres es un conjunto de caracteres. Una clase de caracteres se especifi ca al poner los caracteres en la clase entre corchetes. Una clase coincidirá con cualquier carácter que sea parte de la clase. Por ejemplo, la clase [wxyz] buscará coincidencias de w, x, y o z. Para especifi car un conjunto invertido, anteceda los caracteres con un ^. Por ejemplo, [^wxyz] buscará coincidencias con cualquier carácter, excepto w, x, y o z. Puede especifi car un rango de caracteres empleando un guión. Por ejemplo, para especifi car una clase de caracteres que coincida con los dígitos 1 a 9, use [1–9]. Una clase puede contener dos o más rangos con sólo especifi carlos. Por ejemplo, la clase [0–9A–Z] busca todos los dígitos y las letras mayúsculas, de la A a la Z.

La API de expresiones regulares de Java proporciona varias clases predefi nidas. He aquí algunas de las de uso más común:

Clase predefinida Coincide con

\d Los dígitos del 0 al 9.

\D Todos los caracteres que no son dígitos.

\s Espacio en blanco.

\S Todo lo que no es un espacio en blanco.

\w Caracteres que pueden ser parte de una palabra. En Java, son las letras mayúsculas y minúsculas, los dígitos del 0 al 9 y los guiones de subrayado. Suele denominárseles caracteres de palabra.

\W Todos los caracteres que no son de palabra.

Además de estas clases, Java proporciona una amplia cantidad de clases de caracteres adicionales que tienen la siguiente forma general:

\p{nombre}

Aquí, nombre especifi ca el nombre de la clase. He aquí algunos ejemplos:

\p{Lower} Contiene las letras minúsculas.

\p{Upper} Contiene las letras mayúsculas.

\p{Punct} Contiene todos los signos de puntuación.

Hay otros más. Debe consultar la documentación de la API para conocer las clases de caracteres que soporta su JDK.

Una clase puede contener otra. Por ejemplo, [[abc][012]] defi ne una clase que buscará

(31)

conjuntos. Por supuesto, este ejemplo podría escribirse de manera más conveniente como [abc012]. Sin embargo, las clases anidadas son muy útiles en otros contextos, como cuando se trabaja con conjuntos predefi nidos o cuando quiere crear la intersección de dos conjuntos.

Para crear una clase que contenga la intersección de dos o más conjuntos de caracteres, use el operador &&. Por ejemplo, esto crea un conjunto que busca coincidencias de todos los caracteres de palabra, excepto para las letras mayúsculas [\w && [^A–Z]].

Otros dos puntos: La parte exterior de una clase de caracteres, – se trata como un carácter normal. Asimismo, la parte exterior de una clase, la ^ se usa para especifi car el inicio de una línea, como se describe en breve.

El carácter comodín

El carácter comodín es . (punto) y coincide con cualquier carácter. Por tanto, un patrón que consta de un . buscará coincidencias de estas (y otras) secuencias de entrada: "A", "a", "x" y "!" En esencia, el punto es una clase predefi nida que coincide con todos los caracteres.

Para crear un patrón que busque coincidencias con un punto, anteceda éste con una \. Por ejemplo, dada esta cadena de entrada.

Final del juego. esta expresión juego\.

busca coincidencias con la secuencia "juego".

Cuantifi cadores

Un cuantifi cador determina cuántas veces se buscarán coincidencias con una expresión. A continuación se muestran los cuantifi cadores:

+ Busca una o más coincidencias. * Busca cero o más coincidencias. ? Busca cero o una coincidencia.

Por ejemplo, x+ buscará una o más x, como "x", "xx", "xxx", etc. El patrón .* buscará coincidencias de cualquier carácter cero o más veces. El patrón ,? buscará cero o una coma.

También puede especifi car un cuantifi cador que buscará coincidencias de un patrón un número específi co de veces. He aquí una forma general:

{núm}

Por tanto, x{2} encontrará "xx", pero no "x" o "xxx". Puede especifi car que se busquen coincidencias de un patrón por lo menos un número mínimo de veces al usar este cuantifi cador:

{mín,}

Por ejemplo, x{2,} buscará xx, xxx, xxxx, etc.

Puede especifi car que se busques coincidencias de un patrón por lo menos un número mínimo de veces, pero no más de un número máximo usando este cuantifi cador:

(32)

Cuantifi cadores avaros, renuentes y posesivos

En realidad hay tres tipos de cuantifi cadores: avaros, renuentes y posesivos. Los ejemplos de cuantifi cadores que se acaban de mostrar son de la variedad avara. Encuentran la secuencia coincidente más larga. Un cuantifi cador renuente (también denominado cuantifi cador holgazán) encuentra la secuencia coincidente más corta. Para crear uno renuente, incluya al fi nal un ? Un cuantifi cador posesivo busca la secuencia coincidente más larga y no encontrará una secuencia más corta, aunque habilite toda la expresión a fi n de tener éxito. Para crear un cuantifi cador posesivo, coloque un + al fi nal.

Recorramos ejemplos de cada tipo de cuantifi cador que trata de encontrar una coincidencia en la cadena "sarape simple". El patrón s.+e buscará coincidencias con la secuencia más larga, que es toda la cadena "sarape simple", porque el cuantifi cador avaro .+ buscará coincidencias con todos los caracteres después de la primera s hasta la e fi nal.

El patrón s.+?e encontrará "sarape", que es la coincidencia más corta. Esto se debe a que el cuantifi cador renuente .+? se detendrá después de encontrar la primera secuencia coincidente.

El patrón s.++e fallará, porque el cuantifi cador posesivo .++ encontrará todos los

caracteres coincidentes después de la s inicial. Debido a que es posesivo, no liberará la e fi nal para permitir coincidencias con el patrón general. Por tanto, no se encontrará la e fi nal y la coincidencia fallará.

Comparadores de límites

En ocasiones querrá especifi car un patrón que empieza o termina en algún límite, como al fi nal de una palabra o el principio de una línea. Para ello, usará los comparadores de límites. Tal vez los comparadores de límites de uso más amplio sean ^ y $. Encuentran coincidencias en el inicio y el fi nal de la línea en que se está buscando, que como opción predeterminada son el principio y el fi nal de la cadena de entrada. Por ejemplo, dada la cadena "prueba1 prueba2", el patrón prueba.?$ encontrará "prueba2", pero el patrón ^prueba.? encontrará "prueba1". Si quiere que encuentre una coincidencia con uno de estos caracteres por sí solo, necesitará usar la secuencia de escape \^ o \$.

Aquí se muestran los demás comparadores de límites.

Comparador Coincide con

\A Inicio de cadena

\b Límite de palabra

\B Límite que no es de palabra \G Fin de la coincidencia anterior

\Z Final de la cadena (no incluye el terminador de línea) \z Final de la cadena (incluye el terminador de línea)

El operador O

Cuando creamos un patrón, puede especifi car una o más opciones al usar el operador O, que es |. Por ejemplo, la expresión puede|podría buscará la palabra "puede" o la palabra "podría". A menudo el operador O se usa dentro de un grupo entre paréntesis. Para comprender la razón, imagine que quiere encontrar todos los usos de "puede" o "podría" además de cualquier palabra que se encuentre junto a ellas. He aquí una manera de componer una expresión que hace esto: \w+\s+(puede | podría)\s+\w+\b

(33)

Dada la cadena

Ella podría ir. No puede ahora.

esta expresión regular encuentra estas dos coincidencias: Ella podría ir

No puede ahora

Si se eliminan los paréntesis alrededor de |, como en \w+\s+puede | podría\s+\w+\b

la expresión encontraría estas dos coincidencias: podría ir

No puede

La razón es que el | ahora separa a toda la subexpresión \w\s+puede de la subexpresión podría \s+\w+\b. Por tanto, toda la expresión coincidirá con frases que empiezan con alguna palabra seguida de "puede" o frases que empiezan con "podría" seguida por alguna palabra.

Grupos

Un grupo se crea al incluir un patrón dentro de paréntesis. Por ejemplo, la expresión entre paréntesis (puede | podría) en la sección anterior forma un grupo. Además de vincular los elementos de una subexpresión, los grupos tienen un segundo propósito. Una vez que ha defi nido un grupo, otra parte de una expresión regular puede hacer referencia a la secuencia capturada por ese grupo. Cada conjunto de paréntesis defi ne un grupo. El paréntesis de apertura del extremo izquierdo defi ne al grupo uno, el siguiente paréntesis de apertura defi ne al grupo dos, etc. Dentro de una expresión regular, se hace referencia a los grupos por número. El primer grupo es \1, el segundo \2, etcétera.

Trabajemos con un ejemplo. Suponga que quiere encontrar frases dentro de la misma oración en que se usan las formas singular y plural de una palabra. Por razones de simplicidad, también suponga que sólo quiere encontrar plurales, que siempre terminan con s. Por ejemplo, dadas estas frases

Tengo un perro, pero él tiene cuatro perros.

Ella tiene un gato, ¿pero quiere una buena cantidad de gatos? Ella también tiene un perro. Pero no quisiera tener cuatro perros.

quiere encontrar la frase "perro, pero él tiene cuatro perros" porque contiene una forma singular y una plural de perro dentro de la misma oración y la frase "gato, ¿pero quiere una buena cantidad de gatos?", porque tiene gato y gatos dentro de la misma oración. No quiere encontrar instancias que abarquen dos o más oraciones, de modo que no querrá encontrar las formas "perro" y "perros" contenidos en las dos últimas frases. He aquí una manera de escribir una expresión regular que haga esto.

\b(\w+)\b[^.?!]*?\1s

Aquí, la expresión entre paréntesis (\w+) crea un grupo que contiene una palabra. Este grupo se usa después para buscar entradas coincidentes posteriores cuando se hace referencia a él con \1. Cualquier número de caracteres puede encontrarse entre la palabra y su plural, siempre y cuando no se localicen terminadores de oración (.?!). Por tanto, el resto de la expresión sólo tiene éxito

(34)

cuando se encuentra una palabra posterior dentro de la misma oración que sea el plural de la palabra que está contenida en \1. Por ejemplo, cuando se aplica a la primera oración de ejemplo, (\w+) encontrará palabras coincidentes, poniendo la palabra en el grupo \1. Para que toda la expresión tenga éxito, una palabra posterior en la oración debe ser igual a la palabra contenida en \1 y debe ir seguida de inmediato por una s. Esto sólo ocurre cuando \1 contiene la palabra "perro", porque "perros" se encuentra después en la misma oración.

Un tema adicional. Puede crear un grupo de no captura al añadir ?: después de los paréntesis de apertura, como en (?:\s*). Son posibles otros tipos de grupos que usan búsqueda hacia delante o hacia atrás positiva o negativa, pero están más allá del alcance de este libro.

Secuencias de marcas

El motor de expresiones regulares de Java soporta varias opciones que controlan la manera en que se buscan coincidencias de un patrón. Estas opciones se establecen o limpian al usar la siguiente construcción: (?f), donde f especifi ca que se establezca una marca. Hay seis marcas, que se muestran aquí:

d Habilita el modo de línea de Unix.

i Ignora las diferencias entre mayúsculas y minúsculas.

m Habilita el modo multilínea, en que ^ y $ buscan coincidencias al principio y el final de las líneas, en lugar de toda la cadena de entrada.

s Habilita el modo "punto en todo", que hace que el punto (.) busque coincidencias de todos los caracteres, incluido el terminador de línea.

u Junto con i, causa que se hagan coincidencias no sensibles a mayúsculas y minúsculas de acuerdo con el estándar de Unicode, en lugar de suponer sólo caracteres ASCII.

x Ignora espacios en blanco y comentarios con # en una expresión regular.

Puede deshabilitar un modo al anteceder su marca con un signo de menos. Por ejemplo, (?–i) deshabilita la coincidencia no sensible a mayúsculas y minúsculas.

Recuerde incluir el carácter de escape \ en cadenas de Java

Un breve recordatorio antes de pasar a las soluciones. Cuando se crean cadenas de Java que contienen expresiones regulares, recuerde que debe usar la secuencia de escape \\ para especifi car una \. Por tanto, la siguiente expresión regular

\b\w+\b

Debe escribirse así cuando se especifi que como una literal de cadena en un programa de Java: "\\b\\w+\\b"

Olvidar la inclusión del carácter de escape \ es una fuente común de problemas porque no siempre da como resultado errores en tiempo de compilación o de ejecución. En cambio, su expresión regular simplemente no encontrará coincidencias donde pensaba que las hallaría. Por ejemplo, si usa \b en lugar de \\b en la cadena anterior verá que una expresión trata de buscar coincidencias del carácter de retroceso, en lugar de utilizarse como límite de palabra.

(35)

Ordenar es una tarea común en programación, y ordenar matrices de cadenas no es la excepción. Por ejemplo, tal vez quiera ordenar una lista de los artículos vendidos por una tienda en línea o una lista de nombres de clientes y direcciones de correo electrónico. Por fortuna, Java facilita el ordenamiento de matrices de cadenas porque proporciona el método de utilería sort( ), que está defi nido por la clase Arrays en java.util. En su forma predeterminada, sort( ) ordena cadenas en orden alfabético, sensible a mayúsculas y minúsculas, y esto es adecuado por muchas situaciones. Sin embargo, en ocasiones querrá ordenar una matriz de cadenas en orden alfabético inverso. Esto requiere un poco más de trabajo.

Hay varias maneras de tratar el problema de ordenar a la inversa. Por ejemplo, una solución inocente consiste en ordenar la matriz y luego copiarla de atrás hacia delante en otra matriz. Además de carecer de elegancia, esta técnica también es inefi ciente. Por fortuna, Java proporciona una manera simple, pero efectiva de ordenar a la inversa una matriz de cadenas. Este método usa un Comparator personalizado para especifi car la manera en que debe aplicarse el orden y una versión de sort( ) que toma Comparator como argumento.

Paso a paso

Para ordenar una matriz de cadenas a la inversa se requieren tres pasos:

1. Cree un Comparator que invierte la salida de una comparación entre dos cadenas. 2. Cree un objeto de ese Comparator.

3. Pase la matriz que se ordenará y el Comparator a una versión de java.util.Arrays.sort( ) que tome un comparador como argumento. Cuando sort( ) regrese, la matriz se ordenará a la inversa.

Análisis

Comparator es una interfaz genérica que se declara como se muestra aquí: Comparator<T>

Ordene una matriz de cadenas de manera inversa

Componentes clave

Clases e interfaces Métodos

java.lang.String int compareTo(String cad) java.util.Arrays static <T> void sort(T[ ] matriz,

Comparator<? Super T> comp) java.util.Comparator<T> int compare(T objA, T objB)

(36)

El parámetro de tipo T especifi ca el tipo de datos que se habrá de comparar. En este caso, String se pasará a T.

Comparator defi ne los dos métodos siguientes: int compare(T objA, T objB)

boolean equals(Object obj)

De éstos, sólo compare( ) debe implementarse. El método equals( ) simplemente especifi ca una sobreescritura de equals( ) en Object. La implementación de equals( ) le permite determinar si dos Comparator son iguales. Sin embargo, esta capacidad no siempre es necesaria. Cuando no se necesita (como sucede en este capítulo), no es necesario sobreescribir la implementación de Object.

El método en que estamos interesados es compare( ). Determina la manera en que se compara un objeto con otro. Por lo general, debe devolver menos de cero si objA es menor que objB, más que cero si objA es mayor que objB y cero si los dos objetos son iguales. Al implementar compare( ) de esta manera se logra que opere de acuerdo con el orden natural de los datos. En el caso de cadenas, esto signifi ca orden alfabético. Sin embargo, tiene la libertad de implementar compare( ) para adecuarse a las necesidades de su tarea. Para ordenar a la inversa una matriz de cadenas, necesitará crear una versión de compare( ) que invierta la salida de la comparación.

He aquí la manera de implementar un operador inverso para String. // Crea un Comparator que devuelve la salida

// de una comparación de cadena inversa.

class CompCadInv implements Comparator<String> { // Implementa el método compare( ) de modo que // invierte el orden de la comparación de la cadena. public int compare(String cadA, String cadB) {

// Compara cadB con cadA, en lugar de cadA con cadB. return cadB.compareTo(cadA);

} }

Revisemos de cerca CompCadInv. En primer lugar, observe que implementa Comparator. Esto signifi ca que un objeto de tipo CompCadInv se puede usar en cualquier lugar que se necesite un Comparator. Asimismo, observe que implementa una versión específi ca de String de Comparator. Por tanto, CompCadInv no es, en sí, genérico. Sólo funciona con cadenas.

Ahora, observe que el método compare( ) llama al método compareTo( ) de String para comparar dos cadenas. compareTo( ) es especifi cada por la interfaz Comparable, que está

implementada por String (y muchas otras clases). Una clase que implementa Comparable garantiza que los objetos de esa clase pueden ordenarse. A continuación se muestra la forma general de compareTo( ), como la implementa String:

int compareTo(String cad)

Devuelve menos de cero si la cadena de invocación es menor que cad, más de cero si es mayor que cad y cero si son iguales. Una cadena es menor que otra si se encuentra antes en el orden alfabético y es mayor si se encuentra después.

(37)

El método compare( ) de CompCadInv devuelve el resultado de la llamada a compareTo( ). Sin embargo, observe que compare( ) llama a compareTo( ) en orden inverso. Es decir, se llama a compareTo( ) en cadB mientras cadA se pasa como argumento. Para una comparación normal, cadA invocaría a compareTo( ), pasando cadB. Sin embargo, como cadB invoca a compareTo( ), se invierte el resultado de la comparación. Por tanto, se invierte el orden de las dos cadenas.

Una vez que ha creado un comparador inverso, se crea un objeto de ese comparador y se pasa a esta versión de sort( ) defi nida por java.util.Arrays:

static<T> void sort(T[ ] matriz, Comparator<? Super T> comp)

Observe la cláusula super. Asegura que la matriz pasada a sort( ) sea compatible con el tipo de Comparator. Después de la llamada a sort( ), la matriz estará en orden alfabético invertido.

Ejemplo

En el siguiente ejemplo se invierte el orden de una matriz de cadenas. Para fi nes de demostración, también se les ordena de manera natural empleando la versión predeterminada de sort( ).

// Ordena una matriz de cadenas en orden inverso. import java.util.*;

// Crea un Comparator que devuelve la salida // de una comparación de cadena inversa.

class CompCadInv implements Comparator<String> { // Implementa el método compare( ) de modo que // invierte el orden de la comparación de la cadena. public int compare(String cadA, String cadB) {

// Compara cadB con cadA, en lugar de cadA con cadB. return cadB.compareTo(cadA);

} }

// Demuestra el comparador de cadena inverso. class OrdenCadInv {

public static void main(String args[ ]) { // Crea una matriz simple de cadenas.

String cads[ ] = { "perro", "caballo", "cebra", "vaca", "gato" }; // Muestra el orden inicial.

System.out.print("Orden inicial: "); for(String s : cads)

System.out.print(s + " "); System.out.println("\n");

// Ordena la matriz a la inversa.

// Empieza por crear un comparador de cadena inversa. CompCadInv cci = new CompCadInv( );

(38)

// Ahora, ordena las cadenas empleando el comparador inverso. Arrays.sort(cads, cci);

// Muestra el orden inverso.

System.out.print("Orden inverso: "); for(String s : cads)

System.out.print(s + " "); System.out.println("\n");

// Para comparación, ordena la cadena de manera natural. Arrays.sort(cads);

// Muestra el orden natural.

System.out.print("Orden natural: "); for(String s : cads) System.out.print(s + " "); System.out.println("\n"); } }

A continuación se muestra la salida de este programa: Orden inicial: perro caballo cebra vaca gato Orden inverso: vaca perro gato cebra caballo Orden natural: caballo cebra gato perro vaca

Opciones

Aunque esta solución ordena cadenas en orden alfabético inverso, la misma técnica básica puede generalizarse para otras situaciones. Por ejemplo, puede revertir el orden de otros tipos de datos al crear el Comparator apropiado. Simplemente adapta el método mostrado en el ejemplo.

El método compareTo( ) defi nido por String es sensible a mayúsculas y minúsculas. Esto signifi ca que ambos tipos de letra se ordenarán por separado. Tiene la opción de ordenar datos sin importar las diferencias entre mayúsculas y minúsculas al emplear compareToIgnoreCase( ). (Consulte Ignore las diferencias entre mayúsculas y minúsculas cuando ordene una matriz de cadenas).

Puede ordenar cadenas con base en alguna subcadena específi ca. Por ejemplo, si cada cadena contiene un nombre y una dirección de correo electrónico, entonces puede crear un comparador que ordene a partir de la parte de la dirección de cada cadena. Una manera de realizar esto consiste en usar el método regionMatches( ). También puede ordenar por algún criterio diferente de una estricta relación alfabética. Por ejemplo, cadenas que representan tareas pendientes pueden ordenarse por prioridad.

(39)

En Java, el orden natural de las cadenas es sensible a mayúsculas y minúsculas. Esto signifi ca que las letras mayúsculas están separadas y son diferentes de las minúsculas. Como resultado, cuando ordena una matriz de cadenas, podrían ocurrir algunas sorpresas que no son bienvenidas. Por ejemplo, si ordena una matriz String que contiene las siguientes palabras:

alfa beta Gama Zeta

El orden resultante será como se muestra aquí: Gama Zeta alfa beta

Como verá, aunque Gama y Zeta normalmente se encontrarían después de alfa y beta, están al principio de la matriz ordenada. La razón es que, en Unicode, las mayúsculas están representadas por valores menores que los usados para las minúsculas. Por tanto, aunque Zeta se encontraría normalmente al fi nal de la lista cuando se ordena alfabéticamente, se encuentra antes de alfa cuando son importantes las diferencias entre mayúsculas y minúsculas. ¡Esto puede llevar a órdenes que producen resultados técnicamente exactos, pero indeseables!

NOTA Como algo interesante hay que mencionar que una mayúscula vale exactamente 32 menos que su

equivalente en minúsculas. Por ejemplo, el valor de Unicode para la A es 65. Y para la a es 97. Por fortuna, es muy fácil ordenar una matriz de cadenas con base en el verdadero orden alfabético al crear un Comparator que ignore si una letra está en mayúsculas o minúsculas durante el proceso de ordenamiento. La técnica es similar a la descrita en Ordene una matriz de cadenas de manera inversa. Los detalles se describen a continuación.

Paso a paso

Para ignorar las diferencias entre mayúsculas y minúsculas cuando ordene una matriz de cadenas se incluyen estos tres pasos:

1. Cree un Comparator que ignore las diferencias entre mayúsculas y minúsculas de dos cadenas.

2. Cree un objeto de ese Comparator.

Ignore las diferencias entre mayúsculas y minúsculas cuando ordene una matriz de

cadenas

Componentes clave

Clases e interfaces Métodos

java.lang.String int compareToIgnoreCase(String cad) java.util.Arrays static<T> void sort(T[ ] matriz,

Comparator<? super T> comp) java.util.Comparator<T> int compare(T objA, T objB)

Figure

Actualización...

Referencias

Actualización...

Related subjects :