[ABAP] Excel-Datei (*.xlsx) in interne Tabelle laden (cl_fdt_xl_spreadsheet)

* Mit Hilfe der Klasse cl_fdt_xl_spreadsheet können über XML-Transformationen
* aus XLSX-Dateien Daten extrahiert und in eine interne Tabelle konvertiert werden
TRY.
    DATA: lv_rc TYPE i.
    DATA: it_files TYPE filetable.
    DATA: lv_action TYPE i.

* FileOpen-Dialog aufrufen
    cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                  file_filter = |xlsx (*.xlsx)\|*.xlsx\|{ cl_gui_frontend_services=>filetype_all }|
                                                CHANGING
                                                  file_table  = it_files
                                                  rc          = lv_rc
                                                  user_action = lv_action ).

    IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn Datei ausgewählt wurde
      IF lines( it_files ) > 0.
* ersten Tabelleneintrag lesen
        DATA: lv_filesize TYPE w3param-cont_len.
        DATA: lv_filetype TYPE w3param-cont_type.
        DATA: it_bin_data TYPE w3mimetabtype.

* Bild auf Appl. Server hochladen (binary)
        cl_gui_frontend_services=>gui_upload( EXPORTING
                                                filename   = |{ it_files[ 1 ]-filename }|
                                                filetype   = 'BIN'
                                              IMPORTING
                                                filelength = lv_filesize
                                              CHANGING
                                                data_tab   = it_bin_data ).

* solix -> xstring
        DATA(lv_bin_data) = cl_bcs_convert=>solix_to_xstring( it_solix = it_bin_data ).

* Excel (itab) -> XML -> Ref-Objekt
* Achtung: Speicherintensiv und rel. langsam! Es sollten keine großen Datenmengen verarbeitet werden.
        DATA(o_excel) = NEW cl_fdt_xl_spreadsheet( document_name = CONV #( it_files[ 1 ]-filename )
                                                   xdocument     = lv_bin_data ).

        DATA: it_worksheet_names TYPE if_fdt_doc_spreadsheet=>t_worksheet_names.

* Worksheetnamen ermitteln
        o_excel->if_fdt_doc_spreadsheet~get_worksheet_names( IMPORTING worksheet_names = it_worksheet_names ).

        IF lines( it_worksheet_names ) > 0.
* erste Worksheet -> REF to itab
          DATA(o_sheet_itab) = o_excel->if_fdt_doc_spreadsheet~get_itab_from_worksheet( it_worksheet_names[ 1 ] ).

          FIELD-SYMBOLS: <sheet> TYPE table.
          ASSIGN o_sheet_itab->* TO <sheet>.

          cl_demo_output=>write_data( <sheet> ).

* HTML-Daten aus itab generieren
          DATA(lv_html) = cl_demo_output=>get( ).

* Daten im Inline-Browser im SAP-Fenster anzeigen
          cl_abap_browser=>show_html( EXPORTING
                                        title        = 'Excel-Worksheet'
                                        html_string  = lv_html
                                        container    = cl_gui_container=>default_screen ).

* cl_gui_container=>default_screen erzwingen
          WRITE: space.
        ENDIF.
      ENDIF.
    ENDIF.

  CATCH cx_root INTO DATA(e_text).
    MESSAGE e_text->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
ENDTRY.

[ABAP] PDFs mittels Klasse CL_RSPO_PDF_MERGE zusammenfügen (mergen)

* benötigt KEIN ADS (Adobe Document Service)
TRY.
    DATA: lv_rc TYPE i.
    DATA: it_files TYPE filetable.
    DATA: lv_action TYPE i.

* FileOpen-Dialog aufrufen
    cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                  file_filter    = |pdf (*.pdf)\|*.pdf\|{ cl_gui_frontend_services=>filetype_all }|
                                                  multiselection = abap_true
                                                CHANGING
                                                  file_table  = it_files
                                                  rc          = lv_rc
                                                  user_action = lv_action ).

    IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn mind. zwei Dateien ausgewählt worden sind
      IF lines( it_files ) > 1.
* PDF-Merge Objekt
        DATA(o_pdf_merger) = NEW cl_rspo_pdf_merge( ).

* Dateiliste durchgehen
        LOOP AT it_files ASSIGNING FIELD-SYMBOL(<f>).

          DATA(lv_filesize) = CONV w3param-cont_len( '0' ).
          DATA(it_bin_data) = VALUE w3mimetabtype( ).

* Datei auf Appl. Server hochladen (binary)
          cl_gui_frontend_services=>gui_upload( EXPORTING
                                                  filename   = |{ it_files[ sy-tabix ]-filename }|
                                                  filetype   = 'BIN'
                                                IMPORTING
                                                  filelength = lv_filesize
                                                CHANGING
                                                  data_tab   = it_bin_data ).

* solix -> xstring
          DATA(lv_bin_data) = cl_bcs_convert=>solix_to_xstring( it_solix = it_bin_data ).

* Binärdaten der PDF-Datei als xstring zum Merger-Objekt hinzufügen
          o_pdf_merger->add_document( lv_bin_data ).

          WRITE: / |Hinzugefügt: { it_files[ sy-tabix ]-filename } ({ lv_filesize } Bytes)|.
        ENDLOOP.

* PDF-Dokumente mergen
        o_pdf_merger->merge_documents( IMPORTING merged_document = DATA(lv_merged_pdf)
                                                 rc              = lv_rc ).

        IF lv_rc = 0.
          DATA: lv_filename TYPE string.
          DATA: lv_fullpath TYPE string.
          DATA: lv_path TYPE string.

* SaveDialog
          cl_gui_frontend_services=>file_save_dialog( EXPORTING
                                                        default_extension   = 'pdf'
                                                        default_file_name   = 'merged.pdf'
                                                        file_filter         = |pdf (*.pdf)\|*.pdf\|{ cl_gui_frontend_services=>filetype_all }|
                                                        prompt_on_overwrite = abap_true
                                                      CHANGING
                                                        filename          = lv_filename     " Dateiname
                                                        path              = lv_path         " Pfad
                                                        fullpath          = lv_fullpath     " Pfad + Dateiname
                                                        user_action       = lv_action ).    " Benutzeraktion

          IF lv_action EQ cl_gui_frontend_services=>action_ok.
* xstring -> solix
            DATA(it_bin_data_merged) = cl_bcs_convert=>xstring_to_solix( lv_merged_pdf ).

            DATA(lv_size_merged) = xstrlen( lv_merged_pdf ).

* Datei lokal speichern
            cl_gui_frontend_services=>gui_download( EXPORTING
                                                      filename                = lv_fullpath
                                                      filetype                = 'BIN'
                                                      bin_filesize            = lv_size_merged
                                                    CHANGING
                                                      data_tab                = it_bin_data_merged ).

            WRITE: / |Gespeichert: { lv_fullpath } ({ lv_size_merged } Bytes)|.

          ENDIF.
        ENDIF.
      ENDIF.
    ENDIF.
  CATCH cx_root INTO DATA(e_txt).
    WRITE: / e_txt->get_text( ).
ENDTRY.

[ABAP] PDF-Dokument per ADS (Adobe Document Service) in XML konvertieren

* https://www.berater-wiki.de/XML_aus_PDF_extrahieren_mit_Testprogramm_FP_PDF_TEST_03
* http://sapfirst.blogspot.com/2014/05/inbound-abap-proxy-with-pdf-attachment.html
* Paket:     SAFP
* Programme: FP_PDF_TEST_01 (PDF erzeugen)
*            FP_PDF_TEST_03 (Datenextraktion)
* XFD (XML Form Data): https://www.berater-wiki.de/XFD-Datei_(Values_of_PDF_form_fields_in_XML)
* XFT (XML Form Template)
* XDC (XML Printer Definitions)

TRY.
    DATA: lv_rc TYPE i.
    DATA: it_files TYPE filetable.
    DATA: lv_action TYPE i.

* FileOpen-Dialog aufrufen
    cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                  file_filter    = |pdf (*.pdf)\|*.pdf\|{ cl_gui_frontend_services=>filetype_all }|
                                                CHANGING
                                                  file_table  = it_files
                                                  rc          = lv_rc
                                                  user_action = lv_action ).

    IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn mind. zwei Dateien ausgewählt worden sind
      IF lines( it_files ) > 0.
        DATA(lv_filesize) = CONV w3param-cont_len( '0' ).
        DATA(it_bin_data) = VALUE w3mimetabtype( ).

* Datei auf Appl. Server hochladen (binary)
        cl_gui_frontend_services=>gui_upload( EXPORTING
                                                filename   = |{ it_files[ 1 ]-filename }|
                                                filetype   = 'BIN'
                                              IMPORTING
                                                filelength = lv_filesize
                                              CHANGING
                                                data_tab   = it_bin_data ).

* solix -> xstring
        DATA(lv_bin_data) = cl_bcs_convert=>solix_to_xstring( it_solix = it_bin_data ).

* Form Processor erzeugen
        DATA(o_form_processor) = cl_fp=>get_reference( ).
* PDF-Objekt erzeugen
        DATA(o_pdf) = o_form_processor->create_pdf_object( ).
* Binärdaten an PDF-Objekt übergeben
        o_pdf->set_document( pdfdata = lv_bin_data ).
* PDF-Daten extrahieren
        o_pdf->set_extractdata( ).
* ADS-Server rufen
        o_pdf->execute( ).
* extrahierte Daten holen (xstring)
        o_pdf->get_data( IMPORTING formdata = DATA(lv_pdf_formdata) ).

* xstring (binary) -> XML-string (UTF-8)
        DATA(lv_pdf_xml_str) = ||.
        DATA(o_conv) = cl_abap_conv_in_ce=>create( input    = lv_pdf_formdata
                                                   encoding = 'UTF-8' ).
        o_conv->read( IMPORTING data = lv_pdf_xml_str ).

* XML-Zugriff
        DATA(o_ixml) = cl_ixml=>create( ).

        DATA(o_sf) = o_ixml->create_stream_factory( ).
        DATA(o_doc) = o_ixml->create_document( ).
        DATA(o_stream) = o_sf->create_istream_string( lv_pdf_xml_str ).

* XML-Parser
        DATA(o_parser) = o_ixml->create_parser( stream_factory = o_sf
                                                istream        = o_stream
                                                document       = o_doc ).

* XML parsen
        IF o_parser->parse( ) = 0.

          DATA(o_root) = o_doc->get_root_element( ).

          ...

        ELSE.
* Fehlerauswertung
          DO o_parser->num_errors( ) TIMES.
            DATA(o_err) = o_parser->get_error( index = sy-index - 1 ).
            IF o_err IS BOUND.
              WRITE: / o_err->get_column( ), o_err->get_line( ), o_err->get_reason( ).
            ENDIF.
          ENDDO.
        ENDIF.

      ENDIF.
    ENDIF.
  CATCH cx_root INTO DATA(e_txt).
    WRITE: / e_txt->get_text( ).
ENDTRY.

[ABAP] Beliebige Dateien zu einer Zipdatei hinzufügen

TYPES: BEGIN OF bin_file,                          " Typ für Binärdatei mit Meta-Infos für das Zipfile
         name TYPE string,
         size TYPE i,
         data TYPE solix_tab,
       END OF bin_file.

DATA: lv_filename TYPE string.                     " Dateiname für FileOpen/FileSave
DATA: wa_file TYPE bin_file.                       " Binärdatei mit Meta-Infos für das Zipfile
DATA: it_binfiles TYPE STANDARD TABLE OF bin_file. " unkomprimierter Stream (Tabelle mit Dateien zum Zippen)
DATA: lv_path TYPE string.

START-OF-SELECTION.
* ZIP-Objekt erzeugen
  DATA(o_zip) = NEW cl_abap_zip( ).

  DATA: it_sel_filetab TYPE filetable.
  DATA: ret_code TYPE i.
  DATA: lv_action TYPE i.
* FileOpen-Dialog für Dateiauswahl anzeigen
* Mehrfachselektion möglich
  cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                window_title   = 'Dateien zum Komprimieren auswählen'
                                                multiselection = abap_true
                                              CHANGING
                                                file_table     = it_sel_filetab
                                                rc             = ret_code    " Anzahl ausgewählte Dateien, -1 bei Fehler
                                                user_action    = lv_action ).

  IF lv_action = cl_gui_frontend_services=>action_ok.

* Ausgewählte Dateien durchgehen
    LOOP AT it_sel_filetab INTO DATA(wa_sel_file).

      WRITE: / |Datei hinzugefügt: { wa_sel_file-filename }|.

* Dateien auf den Appl.-Server hochladen
      cl_gui_frontend_services=>gui_upload( EXPORTING
                                              filename   = |{ wa_sel_file-filename }|
                                              filetype   = 'BIN'
                                            IMPORTING
                                              filelength = wa_file-size
                                            CHANGING
                                              data_tab   = wa_file-data ).

* Pfad + Dateinamen aufsplitten
      CALL FUNCTION 'SO_SPLIT_FILE_AND_PATH'
        EXPORTING
          full_name     = wa_sel_file-filename
        IMPORTING
          file_path     = lv_path
          stripped_name = wa_file-name
        EXCEPTIONS
          x_error       = 1
          OTHERS        = 2.

* Datei zum Stream hinzufügen
      APPEND wa_file TO it_binfiles.

    ENDLOOP.

    ULINE.

    DATA: lv_xstring TYPE xstring.
* unkomprimierte Daten zum Zip-File hinzufügen
    LOOP AT it_binfiles INTO wa_file.

* jeden Datei-Stream binär zu xstring wandeln
      CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
        EXPORTING
          input_length = wa_file-size
        IMPORTING
          buffer       = lv_xstring
        TABLES
          binary_tab   = wa_file-data.

      o_zip->add( name    = wa_file-name
                  content = lv_xstring ).

    ENDLOOP.

* Daten komprimieren
    DATA(lv_zip) = o_zip->save( ).

    DATA: lv_zip_size TYPE i.
    DATA: it_zip_bin_data TYPE STANDARD TABLE OF raw255.

* xstring mit Zip-Daten zu binär rückwandeln
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        buffer        = lv_zip
      IMPORTING
        output_length = lv_zip_size
      TABLES
        binary_tab    = it_zip_bin_data.

    DATA: lv_dest_filepath TYPE string.

* SaveFile-Dialog aufrufen
    cl_gui_frontend_services=>file_save_dialog( EXPORTING
                                                  window_title         = 'Zipdatei speichern'
                                                  file_filter          = '(*.zip)|*.zip|'
                                                CHANGING
                                                  filename             = lv_filename
                                                  path                 = lv_path
                                                  fullpath             = lv_dest_filepath ).

* Zipdatei vom Appl-Server auf den lokalen Pfad speichern
    cl_gui_frontend_services=>gui_download( EXPORTING
                                              filename                = lv_dest_filepath
                                              filetype                = 'BIN'
                                              bin_filesize            = lv_zip_size
                                            CHANGING
                                              data_tab                = it_zip_bin_data ).

    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ELSE.
      WRITE: / |Zipdatei erfolgreich unter { lv_dest_filepath } gespeichert.|.
    ENDIF.
  ENDIF.

[ABAP] CSV-Datei in interne Table einlesen

Variante 1 (cl_gui_frontend_services, freier Separator)

* Beispiel für die CSV-Eingabedaten:
* Mandant;Fluggesellschaft;Flugnummer;Land;Abflugstadt;Startflugh.;Land;Ankunftstadt;Zielflugh.;Flugdauer;Abflug;Ankunftszeit;Entfernung;Entfernung in;Charter;n Tag(e) später;
* 900;AA;0017;US;NEW YORK;JFK;US;SAN FRANCISCO;SFO;  6:01;11:00:00;14:01:00;2572;MI;;0;
* 900;AA;0064;US;SAN FRANCISCO;SFO;US;NEW YORK;JFK;  5:21;09:00:00;17:21:00;2572;MI;;0;

* Ausgabestruktur (Spalten) für die eingelesenen und gesplitteten CSV-Daten
* der Einfachheit halber hier alles erstmal als Strings abbilden
TYPES : BEGIN OF ty_s_csv,
          mandant          TYPE string,
          fluggesellschaft TYPE string,
          flugnummer       TYPE string,
          land_ab          TYPE string,
          abflugstadt	   TYPE string,
          startflughafen   TYPE string,
          land_an          TYPE string,
          ankunftstadt     TYPE string,
          zielflughafen    TYPE string,
          flugdauer        TYPE string,
          abflug           TYPE string,
          ankunftszeit     TYPE string,
          entfernung       TYPE string,
          einheit          TYPE string,
          charter          TYPE string,
          tage             TYPE string,
        END OF ty_s_csv.

* Tabellentypen
TYPES: ty_it_csv TYPE STANDARD TABLE OF ty_s_csv WITH DEFAULT KEY.

* Überschriften vorhanden
PARAMETERS: p_head AS CHECKBOX DEFAULT 'X'.
* Separator / Trennzeichen
PARAMETERS: p_sep TYPE char1 DEFAULT ';'.

TRY.
    DATA: lv_rc TYPE i.
    DATA: it_files TYPE filetable.
    DATA: lv_action TYPE i.

* FileOpen-Dialog aufrufen
    cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                  file_filter    = |csv (*.csv)\|*.csv\|{ cl_gui_frontend_services=>filetype_all }|
                                                  multiselection = abap_false
                                                CHANGING
                                                  file_table     = it_files
                                                  rc             = lv_rc
                                                  user_action    = lv_action ).

    IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn mind. eine Dateie ausgewählt worden ist
      IF lines( it_files ) = 1.

* Tabelle für Einlesedaten
        DATA(it_strings) = VALUE string_table( ).

* eingelesene Datei zeilenweise als Stringdaten einlesen
        cl_gui_frontend_services=>gui_upload( EXPORTING
                                                filename = CONV #( it_files[ 1 ]-filename )
                                                filetype = 'ASC'             " Dateityp BIN, ASC, DAT
                                              CHANGING
                                                data_tab = it_strings ).           " Übergabetabelle für Datei-Inhalt

        cl_demo_output=>write_data( it_strings ).

* Wenn mit Header, dann ab Zeile 2, sonst gleich ab Zeile 1
        DATA(lv_startzeile) = COND i( WHEN p_head = abap_true THEN 2 ELSE 1 ).

* Je nach Vorhandensein des Headers prüfen, ob genug Zeilen in der Tabelle
        IF ( lines( it_strings ) > lv_startzeile - 1 ).
* Ausgabetabelle mit ausgesplitteten CSV-Daten
          DATA(it_csv) = VALUE ty_it_csv( ).

* Eingelesene Strings durchlaufen, Start bei Zeile 1 (mit Header) oder 2 (mit Header)
          LOOP AT it_strings ASSIGNING FIELD-SYMBOL(<z>) FROM lv_startzeile.
* neue Ausgabezeile
            DATA(lv_csv_line) = VALUE ty_s_csv( ).

* String anhand des Separators aufsplitten
            SPLIT <z> AT p_sep INTO TABLE DATA(it_columns).

* Wenn anzahl der Splitelemente == Anzahl Felder in der CSV-Struktur
            IF lines( it_columns ) = 16.
* gesplittete Daten in die neue CSV-Zeile übernehmen
              lv_csv_line-mandant = it_columns[ 1 ].
              lv_csv_line-fluggesellschaft = it_columns[ 2 ].
              lv_csv_line-flugnummer = it_columns[ 3 ].
              lv_csv_line-land_ab = it_columns[ 4 ].
              lv_csv_line-abflugstadt = it_columns[ 5 ].
              lv_csv_line-startflughafen = it_columns[ 6 ].
              lv_csv_line-land_an = it_columns[ 7 ].
              lv_csv_line-ankunftstadt = it_columns[ 8 ].
              lv_csv_line-zielflughafen = it_columns[ 9 ].
              lv_csv_line-flugdauer = it_columns[ 10 ].
              lv_csv_line-abflug = it_columns[ 11 ].
              lv_csv_line-ankunftszeit = it_columns[ 12 ].
              lv_csv_line-entfernung = it_columns[ 13 ].
              lv_csv_line-einheit = it_columns[ 14 ].
              lv_csv_line-charter = it_columns[ 15 ].
              lv_csv_line-tage = it_columns[ 16 ].
            ENDIF.

* neue CSV-Zeile an Ausgabetabelle anfügen
            APPEND lv_csv_line TO it_csv.
          ENDLOOP.

          cl_demo_output=>write_data( it_csv ).

* HTML-Code vom Demo-Output holen
          DATA(lv_html) = cl_demo_output=>get( ).
* Daten im Inline-Browser im SAP-Fenster anzeigen
          cl_abap_browser=>show_html( EXPORTING
                                        title        = 'Daten aus CSV'
                                        html_string  = lv_html
                                        container    = cl_gui_container=>default_screen ).

* cl_gui_container=>default_screen erzwingen
          WRITE: space.
        ENDIF.
      ENDIF.
    ENDIF.
  CATCH cx_root INTO DATA(e_text).
    MESSAGE e_text->get_text( ) TYPE 'I'.
ENDTRY.

Variante 2 (cl_gui_frontend_services, freier Separator, dynamisches Einlesen von CSV-Daten)

* Überschriften vorhanden
PARAMETERS: p_head AS CHECKBOX DEFAULT 'X'.
* Separator / Trennzeichen
PARAMETERS: p_sep TYPE char1 DEFAULT ';'.

TRY.
    DATA: lv_rc TYPE i.
    DATA: it_files TYPE filetable.
    DATA: lv_action TYPE i.

* FileOpen-Dialog aufrufen
    cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                  file_filter    = |csv (*.csv)\|*.csv\|{ cl_gui_frontend_services=>filetype_all }|
                                                  multiselection = abap_false
                                                CHANGING
                                                  file_table     = it_files
                                                  rc             = lv_rc
                                                  user_action    = lv_action ).

    IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn mind. eine Dateie ausgewählt worden ist
      IF lines( it_files ) = 1.

* Tabelle für Einlesedaten
        DATA(it_strings) = VALUE string_table( ).

* eingelesene Datei zeilenweise als Stringdaten einlesen
        cl_gui_frontend_services=>gui_upload( EXPORTING
                                                filename = CONV #( it_files[ 1 ]-filename )
                                                filetype = 'ASC'             " Dateityp BIN, ASC, DAT
                                              CHANGING
                                                data_tab = it_strings ).     " Übergabetabelle für Datei-Inhalt

        cl_demo_output=>write_data( it_strings ).

        DATA(it_errors) = VALUE string_table( ).

* Wenn mit Header, dann ab Zeile 2, sonst gleich ab Zeile 1
        DATA(lv_startzeile) = COND i( WHEN p_head = abap_true THEN 2 ELSE 1 ).

* Je nach Vorhandensein des Headers prüfen, ob genug Zeilen (für Erstellung des Headers) in der Tabelle
        IF ( lines( it_strings ) > lv_startzeile - 1 ).

* 1. verfügbaren String aus der eingelesenen Tabelle anhand des Separators aufsplitten
          SPLIT it_strings[ 1 ] AT p_sep INTO TABLE DATA(it_cnt_col).

* Komponenten (Spalten) der Tabelle
          DATA(it_components) = VALUE cl_abap_structdescr=>component_table( ).

* Komponententabelle füllen
          LOOP AT it_cnt_col ASSIGNING FIELD-SYMBOL(<c>).
* Standard-Spaltenbezeichner vorbelegen (COL1...COLn)
            DATA(lv_name) = |COL{ sy-tabix }|.

* Wenn Header vorhanden, dann Header übernehmen
            IF p_head = abap_true.
              lv_name = <c>.
* alle Vorkommen, die nicht [a-zA-Z0-9_] entsprechen, durch '_' ersetzen
              REPLACE ALL OCCURRENCES OF REGEX '([^\w]|[äöüÄÖÜß])+' IN lv_name WITH '_'.
            ENDIF.

* Spalte vom Typ String zur Komponententabelle hinzufügen
            APPEND VALUE #( name = lv_name
                            type = cl_abap_elemdescr=>get_string( )
                          ) TO it_components.
          ENDLOOP.

* Strukturdeskriptor für Komponententabelle
          DATA(o_struct_desc) = cl_abap_structdescr=>create( it_components ).

* Tabellendeskriptor erzeugen
          DATA(o_table_desc) = cl_abap_tabledescr=>create(
                                                           p_line_type  = o_struct_desc                          " Spalten
                                                           p_table_kind = cl_abap_tabledescr=>tablekind_std      " Tabellentyp STANDARD TABLE
                                                           p_unique     = abap_false                             " NON-UNIQUE KEY
                                                           p_key_kind   = cl_abap_tabledescr=>keydefkind_default " Benutzerdefinierter Schlüssel
                                                         ).

* Tabellenobjekt anhand des Tabellendeskriptors erstellen
          DATA: o_table TYPE REF TO data.
          CREATE DATA o_table TYPE HANDLE o_table_desc.

* Feldsymbol auf das Tabellenobjekt
          FIELD-SYMBOLS <csv_table> TYPE STANDARD TABLE.
          ASSIGN o_table->* TO <csv_table>.

* Eingelesene Strings durchlaufen, Start bei Zeile 1 (ohne Header) oder 2 (mit Header)
          LOOP AT it_strings ASSIGNING FIELD-SYMBOL(<z>) FROM lv_startzeile.
* neue Ausgabezeile anfügen
            APPEND INITIAL LINE TO <csv_table>.
* neue Ausgabezeile holen
            DATA(lv_lc) = lines( <csv_table> ).
            ASSIGN <csv_table>[ lv_lc ] TO FIELD-SYMBOL(<row>).

* String anhand des Separators aufsplitten
            SPLIT <z> AT p_sep INTO TABLE DATA(it_column_data).

* Wenn anzahl der Splitelemente == Anzahl Felder in der CSV-Struktur
            IF lines( it_column_data ) = lines( it_components ).
* gesplittete Daten in die neue CSV-Zeile übernehmen
              LOOP AT it_components ASSIGNING FIELD-SYMBOL(<comp>).
* n-te Spalte <col> der akt. Tabellenzeile <row> ermitteln
                ASSIGN COMPONENT sy-tabix OF STRUCTURE <row> TO FIELD-SYMBOL(<col>).
* den n-ten gesplitteten Wert der n-ten Spalte <col> der akt. Tabellenzeile <row> zuordnen
                <col> = it_column_data[ sy-tabix ].
              ENDLOOP.
            ELSE.
* Wenn abweichende Spaltenanzahl vom Header
              APPEND |Zeile { sy-tabix }: Spaltenanzahl ({ lines( it_column_data ) }) weicht vom Header ({ lines( it_components ) }) ab.| TO it_errors.
            ENDIF.

          ENDLOOP.

          cl_demo_output=>write_data( it_errors ).

          cl_demo_output=>write_data( <csv_table> ).

* HTML-Code vom Demo-Output holen
          DATA(lv_html) = cl_demo_output=>get( ).
* Daten im Inline-Browser im SAP-Fenster anzeigen
          cl_abap_browser=>show_html( EXPORTING
                                        title        = 'Daten aus CSV'
                                        html_string  = lv_html
                                        container    = cl_gui_container=>default_screen ).

* cl_gui_container=>default_screen erzwingen
          WRITE: space.
        ENDIF.
      ENDIF.
    ENDIF.
  CATCH cx_root INTO DATA(e_text).
    MESSAGE e_text->get_text( ) TYPE 'I'.
ENDTRY.

Variante 3 (cl_gui_frontend_services, TAB-Zeichen als Separator)

* CSV-Datei mit drei Spalten
TYPES: BEGIN OF ty_s_type,
         col1 TYPE string,
         col2 TYPE string,
         col3 TYPE string,
       END OF ty_s_type.

TYPES: ty_it_csv TYPE STANDARD TABLE OF ty_s_type WITH DEFAULT KEY.

DATA: it_csv TYPE ty_it_csv.

TRY.
  * Setzt voraus, dass die CSV-Daten TAB-getrennt sind!
    cl_gui_frontend_services=>gui_upload( EXPORTING
                                            filename            = 'c:\temp\test.csv' " Eingabedatei
                                            filetype            = 'ASC'              " Dateityp BIN, ASC, DAT
                                            has_field_separator = abap_true          " Spalten durch TAB getrennt bei ASCII Upload?
                                          CHANGING
                                            data_tab            = it_csv ).          " Übergabetabelle für Datei-Inhalt

    cl_demo_output=>write_data( it_csv ).

  CATCH cx_root INTO DATA(e_text).
    MESSAGE e_text->get_text( ) TYPE 'I'.
ENDTRY.

Variante 4 (Funktionbaustein, obsolet)

* CSV-Datei mit drei Spalten
TYPES: BEGIN OF ty_s_type,
         col1 TYPE string,
         col2 TYPE string,
         col3 TYPE string,
       END OF ty_s_type.

TYPES: ty_it_csv TYPE STANDARD TABLE OF ty_s_type WITH DEFAULT KEY.

DATA: it_csv TYPE ty_it_csv.

* Funktion ruft eigenen File-Open-Dialog auf
* FuBa UPLOAD ist obsolet
CALL FUNCTION 'UPLOAD'
  EXPORTING
    filename                = 'c:\temp\data.txt' " Dateinamen vorbelegen
    filetype                = 'DAT'              " Eingabedatei-Typ: Datentabelle ASCII mit Spaltentabulator
  TABLES
    data_tab                = it_csv
  EXCEPTIONS
    conversion_error        = 1
    file_open_error         = 2
    file_read_error         = 3
    invalid_table_width     = 4
    invalid_type            = 5
    no_batch                = 6
    unknown_error           = 7
    gui_refuse_filetransfer = 8
    OTHERS                  = 9.
    
IF sy-subrc <> 0.
  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.