FIND en ABAP: Buscar patrones y subcadenas

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

La sentencia FIND busca patrones o subcadenas dentro de strings y tablas. Soporta busquedas simples, expresiones regulares y proporciona informacion detallada sobre las coincidencias encontradas.

Sintaxis

FIND [ FIRST OCCURRENCE OF | ALL OCCURRENCES OF ]
{ <subcadena> | REGEX <patron> }
IN [ TABLE ] <fuente>
[ MATCH OFFSET <offset> ]
[ MATCH LENGTH <longitud> ]
[ MATCH COUNT <conteo> ]
[ RESULTS <resultados> ]
[ SUBMATCHES <subcoincidencias> ]
[ IGNORING CASE | RESPECTING CASE ].

Campos del sistema

Despues de FIND:

  • sy-subrc:
    • 0: Coincidencia encontrada
    • 4: No se encontraron coincidencias

Ejemplos

1. Busqueda simple de subcadena

DATA: lv_text TYPE string VALUE 'ABAP es un lenguaje de programacion',
lv_offset TYPE i,
lv_length TYPE i.
FIND 'lenguaje' IN lv_text
MATCH OFFSET lv_offset
MATCH LENGTH lv_length.
IF sy-subrc = 0.
WRITE: / 'Encontrado en posicion:', lv_offset,
/ 'Longitud:', lv_length.
ENDIF.

2. Busqueda ignorando mayusculas/minusculas

DATA: lv_text TYPE string VALUE 'El sistema SAP es potente'.
" Por defecto es RESPECTING CASE
FIND 'sap' IN lv_text.
WRITE: / 'Con case:', sy-subrc. " 4 (no encontrado)
" Ignorar mayusculas/minusculas
FIND 'sap' IN lv_text IGNORING CASE.
WRITE: / 'Sin case:', sy-subrc. " 0 (encontrado)

3. Contar todas las ocurrencias

DATA: lv_text TYPE string VALUE 'uno, dos, uno, tres, uno',
lv_count TYPE i.
FIND ALL OCCURRENCES OF 'uno' IN lv_text
MATCH COUNT lv_count.
WRITE: / 'Ocurrencias de "uno":', lv_count. " 3

4. Obtener todas las posiciones (RESULTS)

DATA: lv_text TYPE string VALUE 'ABC 123 XYZ 456 ABC',
lt_results TYPE match_result_tab.
FIND ALL OCCURRENCES OF 'ABC' IN lv_text
RESULTS lt_results.
LOOP AT lt_results INTO DATA(ls_result).
WRITE: / 'Posicion:', ls_result-offset,
'Longitud:', ls_result-length.
ENDLOOP.

5. Expresiones regulares (REGEX)

DATA: lv_text TYPE string VALUE 'Email: [email protected], [email protected]'.
" Buscar patron de email simple
FIND REGEX '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
IN lv_text
MATCH OFFSET DATA(lv_off)
MATCH LENGTH DATA(lv_len).
IF sy-subrc = 0.
DATA(lv_email) = substring( val = lv_text off = lv_off len = lv_len ).
WRITE: / 'Primer email:', lv_email.
ENDIF.

6. REGEX con todas las coincidencias

DATA: lv_text TYPE string VALUE 'Numeros: 123, 456, 789',
lt_matches TYPE match_result_tab.
" Buscar todos los numeros
FIND ALL OCCURRENCES OF REGEX '\d+' IN lv_text
RESULTS lt_matches.
LOOP AT lt_matches INTO DATA(ls_match).
DATA(lv_number) = substring( val = lv_text
off = ls_match-offset
len = ls_match-length ).
WRITE: / 'Numero encontrado:', lv_number.
ENDLOOP.

7. SUBMATCHES (grupos de captura)

DATA: lv_text TYPE string VALUE 'Fecha: 2024-03-15',
lv_year TYPE string,
lv_month TYPE string,
lv_day TYPE string.
" Patron con grupos de captura ()
FIND REGEX '(\d{4})-(\d{2})-(\d{2})' IN lv_text
SUBMATCHES lv_year lv_month lv_day.
IF sy-subrc = 0.
WRITE: / 'Ano:', lv_year,
/ 'Mes:', lv_month,
/ 'Dia:', lv_day.
ENDIF.

8. SUBMATCHES con tabla de resultados

DATA: lv_text TYPE string VALUE 'Juan Garcia, Ana Lopez, Pedro Martinez',
lt_results TYPE match_result_tab.
FIND ALL OCCURRENCES OF REGEX '(\w+)\s+(\w+)' IN lv_text
RESULTS lt_results.
LOOP AT lt_results INTO DATA(ls_result).
WRITE: / 'Nombre completo encontrado en:', ls_result-offset.
" Acceder a submatches
LOOP AT ls_result-submatches INTO DATA(ls_sub).
DATA(lv_part) = substring( val = lv_text
off = ls_sub-offset
len = ls_sub-length ).
WRITE: / ' Parte:', lv_part.
ENDLOOP.
ENDLOOP.

9. FIND en tabla interna

DATA: lt_lines TYPE TABLE OF string,
lt_results TYPE match_result_tab.
lt_lines = VALUE #(
( `Primera linea de texto` )
( `Segunda linea con palabra clave` )
( `Tercera linea normal` )
( `Cuarta linea con palabra clave de nuevo` )
).
" Buscar en toda la tabla
FIND ALL OCCURRENCES OF 'palabra clave' IN TABLE lt_lines
RESULTS lt_results.
LOOP AT lt_results INTO DATA(ls_result).
WRITE: / 'Linea:', ls_result-line,
'Posicion:', ls_result-offset.
ENDLOOP.

10. FIND en tabla con REGEX

DATA: lt_emails TYPE TABLE OF string,
lt_results TYPE match_result_tab.
lt_emails = VALUE #(
( `Contacto: [email protected]` )
( `Sin email aqui` )
).
FIND ALL OCCURRENCES OF REGEX '[a-z]+@[a-z]+\.com'
IN TABLE lt_emails
RESULTS lt_results
IGNORING CASE.
LOOP AT lt_results INTO DATA(ls_res).
DATA(lv_email) = substring(
val = lt_emails[ ls_res-line ]
off = ls_res-offset
len = ls_res-length
).
WRITE: / 'Email en linea', ls_res-line, ':', lv_email.
ENDLOOP.

11. Verificar existencia (sin detalles)

DATA: lv_text TYPE string VALUE 'Error: operacion fallida'.
" Solo verificar si existe
IF find( val = lv_text sub = 'Error' ) >= 0.
WRITE: / 'Se encontro mensaje de error'.
ENDIF.
" Alternativa con sentencia FIND
FIND 'Error' IN lv_text.
IF sy-subrc = 0.
WRITE: / 'Es un mensaje de error'.
ENDIF.

12. Patrones REGEX comunes

" Numeros enteros
FIND REGEX '^\d+$' IN lv_text.
" Numeros decimales
FIND REGEX '^\d+[.,]\d+$' IN lv_text.
" Direcciones email
FIND REGEX '^[\w.+-]+@[\w.-]+\.\w{2,}$' IN lv_text.
" Numeros de telefono (formato aleman)
FIND REGEX '^\+?[\d\s-]{10,}$' IN lv_text.
" Codigo postal aleman
FIND REGEX '^\d{5}$' IN lv_text.
" Fechas (YYYY-MM-DD)
FIND REGEX '^\d{4}-\d{2}-\d{2}$' IN lv_text.
" Solo letras
FIND REGEX '^[a-zA-Z]+$' IN lv_text.
" Alfanumerico
FIND REGEX '^[a-zA-Z0-9]+$' IN lv_text.

13. Reemplazar con FIND (obtener posiciones)

DATA: lv_text TYPE string VALUE 'Hola mundo, hola ABAP',
lt_results TYPE match_result_tab,
lv_new TYPE string.
FIND ALL OCCURRENCES OF 'hola' IN lv_text
RESULTS lt_results
IGNORING CASE.
" Reemplazar de atras hacia adelante (para no alterar offsets)
lv_new = lv_text.
LOOP AT lt_results INTO DATA(ls_res).
lv_new = substring( val = lv_new off = 0 len = ls_res-offset )
&& 'Saludos'
&& substring( val = lv_new off = ls_res-offset + ls_res-length ).
ENDLOOP.
WRITE: / lv_new.

14. PCRE vs POSIX Regex

" ABAP usa POSIX-style regex por defecto
" Algunas diferencias con PCRE:
" Clases de caracteres
" \d -> [0-9]
" \w -> [a-zA-Z0-9_]
" \s -> espacio, tab, newline
" Cuantificadores
" + -> uno o mas
" * -> cero o mas
" ? -> cero o uno
" {n} -> exactamente n
" {n,} -> n o mas
" {n,m} -> entre n y m
" Anclas
" ^ -> inicio de cadena/linea
" $ -> fin de cadena/linea
" Grupos
" () -> grupo de captura
" | -> alternativa (OR)

15. Performance: FIND vs alternativas

" Para busquedas simples, usar funciones builtin puede ser mas rapido
" Funcion find() - retorna posicion (-1 si no encontrado)
DATA(lv_pos) = find( val = lv_text sub = 'buscar' ).
" Funcion count() - cuenta ocurrencias
DATA(lv_cnt) = count( val = lv_text sub = 'buscar' ).
" Funcion contains() - retorna booleano
IF contains( val = lv_text sub = 'buscar' ).
" ...
ENDIF.
" Operador CS (Contains String)
IF lv_text CS 'buscar'.
" Mas simple pero menos flexible
ENDIF.

Estructura match_result

" Componentes de match_result:
" - OFFSET: Posicion del inicio de la coincidencia
" - LENGTH: Longitud de la coincidencia
" - LINE: Numero de linea (para busquedas en tablas)
" - SUBMATCHES: Tabla de subcoincidencias (grupos de captura)

Resumen

VarianteDescripcion
FIRST OCCURRENCE OFSolo primera coincidencia (por defecto)
ALL OCCURRENCES OFTodas las coincidencias
REGEXUsar expresion regular
IGNORING CASEIgnorar mayusculas/minusculas
IN TABLEBuscar en tabla interna
RESULTSObtener detalles de todas las coincidencias
SUBMATCHESObtener grupos de captura

Notas importantes / Mejores practicas

  • Verificar sy-subrc para saber si hubo coincidencias.
  • Usar IGNORING CASE para busquedas insensibles a mayusculas.
  • ALL OCCURRENCES con RESULTS para procesar multiples coincidencias.
  • REGEX para patrones complejos pero es mas lento.
  • SUBMATCHES extrae grupos de captura () de regex.
  • Para busquedas simples, las funciones find() y contains() son mas concisas.
  • En tablas, ls_result-line indica el numero de fila.
  • Escapar caracteres especiales en regex con \.