Text tables can be utilized to export SAP internal tables into text files properly or to share them with a consistent format on communication platforms.
Function
FUNCTION ZABAP2TEXTTABLE.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(IT_DATA) TYPE ANY TABLE
*" EXPORTING
*" REFERENCE(EV_TEXT_TABLE) TYPE STRING
*" EXCEPTIONS
*" CONVERSION_ERROR
*"----------------------------------------------------------------------
TYPES: BEGIN OF ty_col_info,
name TYPE abap_compname,
display_name TYPE string,
max_width TYPE i,
is_numeric TYPE abap_bool,
END OF ty_col_info.
DATA: lo_tabledescr TYPE REF TO cl_abap_tabledescr,
lo_structdescr TYPE REF TO cl_abap_structdescr,
lo_typedescr TYPE REF TO cl_abap_typedescr,
lt_components TYPE abap_component_tab,
ls_component TYPE abap_componentdescr,
lt_col_info TYPE TABLE OF ty_col_info,
ls_col_info TYPE ty_col_info,
lv_field_value TYPE string,
lv_line TYPE string,
lv_border TYPE string,
lv_separator TYPE string,
lv_cell TYPE string,
lv_dashes TYPE string,
lv_width TYPE i,
lv_val_len TYPE i,
lv_col_count TYPE i,
lv_col_index TYPE i,
lv_newline TYPE string.
FIELD-SYMBOLS: TYPE any,
TYPE any.
CLEAR ev_text_table.
lv_newline = cl_abap_char_utilities=>newline.
* ----------------------------------------------------------------------
* 1. Retrieve table structure using RTTI
* ----------------------------------------------------------------------
TRY.
lo_typedescr = cl_abap_tabledescr=>describe_by_data( it_data ).
lo_tabledescr ?= lo_typedescr.
lo_structdescr ?= lo_tabledescr->get_table_line_type( ).
lt_components = lo_structdescr->get_components( ).
CATCH cx_sy_move_cast_error.
RAISE conversion_error.
ENDTRY.
IF lt_components IS INITIAL.
RAISE conversion_error.
ENDIF.
* ----------------------------------------------------------------------
* 2. Collect column metadata and initialize widths
* ----------------------------------------------------------------------
LOOP AT lt_components INTO ls_component.
" Skip deep types (tables, structures, references)
IF ls_component-type->kind = cl_abap_typedescr=>kind_table OR
ls_component-type->kind = cl_abap_typedescr=>kind_struct OR
ls_component-type->kind = cl_abap_typedescr=>kind_ref.
CONTINUE.
ENDIF.
CLEAR ls_col_info.
ls_col_info-name = ls_component-name.
ls_col_info-display_name = to_lower( ls_component-name ).
ls_col_info-max_width = strlen( ls_col_info-display_name ).
" Flag numeric types for right-alignment
IF ls_component-type->type_kind = cl_abap_typedescr=>typekind_int OR
ls_component-type->type_kind = cl_abap_typedescr=>typekind_int1 OR
ls_component-type->type_kind = cl_abap_typedescr=>typekind_int2 OR
ls_component-type->type_kind = cl_abap_typedescr=>typekind_packed OR
ls_component-type->type_kind = cl_abap_typedescr=>typekind_float OR
ls_component-type->type_kind = cl_abap_typedescr=>typekind_decfloat OR
ls_component-type->type_kind = cl_abap_typedescr=>typekind_decfloat16 OR
ls_component-type->type_kind = cl_abap_typedescr=>typekind_decfloat34.
ls_col_info-is_numeric = abap_true.
ENDIF.
APPEND ls_col_info TO lt_col_info.
ENDLOOP.
lv_col_count = lines( lt_col_info ).
IF lv_col_count = 0.
RAISE conversion_error.
ENDIF.
* ----------------------------------------------------------------------
* 3. Scan data rows to determine maximum column widths
* ----------------------------------------------------------------------
LOOP AT it_data ASSIGNING .
LOOP AT lt_col_info INTO ls_col_info.
lv_col_index = sy-tabix.
ASSIGN COMPONENT ls_col_info-name OF STRUCTURE TO .
IF sy-subrc = 0.
TRY.
lv_field_value = .
CATCH cx_root.
CLEAR lv_field_value.
ENDTRY.
CONDENSE lv_field_value.
lv_val_len = strlen( lv_field_value ).
IF lv_val_len > ls_col_info-max_width.
ls_col_info-max_width = lv_val_len.
MODIFY lt_col_info FROM ls_col_info INDEX lv_col_index.
ENDIF.
ENDIF.
ENDLOOP.
ENDLOOP.
* ----------------------------------------------------------------------
* 4. Build border and separator lines
* ----------------------------------------------------------------------
" Top/Bottom border: +---+---+
lv_border = ``.
LOOP AT lt_col_info INTO ls_col_info.
lv_width = ls_col_info-max_width + 2.
lv_dashes = repeat( val = `-` occ = lv_width ).
lv_border = lv_border && `+` && lv_dashes.
ENDLOOP.
lv_border = lv_border && `+`.
" Header separator: |---+---|
lv_separator = ``.
lv_col_index = 0.
LOOP AT lt_col_info INTO ls_col_info.
lv_col_index = lv_col_index + 1.
lv_width = ls_col_info-max_width + 2.
lv_dashes = repeat( val = `-` occ = lv_width ).
IF lv_col_index = 1.
lv_separator = `|` && lv_dashes.
ELSE.
lv_separator = lv_separator && `+` && lv_dashes.
ENDIF.
ENDLOOP.
lv_separator = lv_separator && `|`.
* ----------------------------------------------------------------------
* 5. Build header row with aligned column names
* ----------------------------------------------------------------------
lv_line = ``.
LOOP AT lt_col_info INTO ls_col_info.
IF ls_col_info-is_numeric = abap_true.
lv_cell = |{ ls_col_info-display_name WIDTH = ls_col_info-max_width ALIGN = RIGHT }|.
ELSE.
lv_cell = |{ ls_col_info-display_name WIDTH = ls_col_info-max_width ALIGN = LEFT }|.
ENDIF.
lv_line = lv_line && `| ` && lv_cell && ` `.
ENDLOOP.
lv_line = lv_line && `|`.
* ----------------------------------------------------------------------
* 6. Assemble final output: border + header + separator + data + border
* ----------------------------------------------------------------------
ev_text_table = lv_border && lv_newline
&& lv_line && lv_newline
&& lv_separator.
" Append each data row with padded and aligned cell values
LOOP AT it_data ASSIGNING .
lv_line = ``.
LOOP AT lt_col_info INTO ls_col_info.
ASSIGN COMPONENT ls_col_info-name OF STRUCTURE TO .
IF sy-subrc = 0.
TRY.
lv_field_value = .
CATCH cx_root.
CLEAR lv_field_value.
ENDTRY.
CONDENSE lv_field_value.
ELSE.
CLEAR lv_field_value.
ENDIF.
IF ls_col_info-is_numeric = abap_true.
lv_cell = |{ lv_field_value WIDTH = ls_col_info-max_width ALIGN = RIGHT }|.
ELSE.
lv_cell = |{ lv_field_value WIDTH = ls_col_info-max_width ALIGN = LEFT }|.
ENDIF.
lv_line = lv_line && `| ` && lv_cell && ` `.
ENDLOOP.
lv_line = lv_line && `|`.
ev_text_table = ev_text_table && lv_newline && lv_line.
ENDLOOP.
" Close with bottom border
ev_text_table = ev_text_table && lv_newline && lv_border.
ENDFUNCTION.
Example
types : begin of ts_abap_version,
version type text20,
year type char4,
platform type text20,
end of ts_abap_version.
data: lt_data type standard table of ts_abap_version,
ls_data type ts_abap_version,
ev_text_table type string.
ls_data-version = 'ABAP/4'.
ls_data-year = '1983'.
ls_data-platform = 'SAP R/2'.
append ls_data to lt_data. clear ls_data.
ls_data-version = 'ABAP R/3'.
ls_data-year = '1992'.
ls_data-platform = 'SAP R/3'.
append ls_data to lt_data. clear ls_data.
ls_data-version = 'ABAP Objects'.
ls_data-year = '1999'.
ls_data-platform = 'R/3 4.6'.
append ls_data to lt_data. clear ls_data.
ls_data-version = 'ABAP 7.0'.
ls_data-year = '2006'.
ls_data-platform = 'NetWeaver 7.0'.
append ls_data to lt_data. clear ls_data.
ls_data-version = 'ABAP Platform 2020+'.
ls_data-year = '2020'.
ls_data-platform = 'S/4HANA 2020+'.
append ls_data to lt_data. clear ls_data.
ls_data-version = 'ABAP 2505 (Cloud)'.
ls_data-year = '2025'.
ls_data-platform = 'SAP BTP / Cloud'.
append ls_data to lt_data. clear ls_data.
call function 'ZABAP2TEXTTABLE'
exporting
it_data = lt_data
importing
ev_text_table = ev_text_table
exceptions
conversion_error = 1
others = 2.
if sy-subrc <> 0.
* Implement suitable error handling here
endif.
Output
+---------------------+------+-----------------+
| version | year | platform |
|---------------------+------+-----------------|
| ABAP/4 | 1983 | SAP R/2 |
| ABAP R/3 | 1992 | SAP R/3 |
| ABAP Objects | 1999 | R/3 4.6 |
| ABAP 7.0 | 2006 | NetWeaver 7.0 |
| ABAP Platform 2020+ | 2020 | S/4HANA 2020+ |
| ABAP 2505 (Cloud) | 2025 | SAP BTP / Cloud |
+---------------------+------+-----------------+
