Knobelaufgabe ( Oktober 2021 )

Alles Rund um SAP®.
6 Beiträge • Seite 1 von 1
6 Beiträge Seite 1 von 1

Knobelaufgabe ( Oktober 2021 )

Beitrag von black_adept (Top Expert / 3701 / 78 / 771 ) »
Moin allerseits,
da wir länger keine Knobelaufgabe hatten und ich gerade über ein nettes Thema gestolpert bin, hier das Problem für den Oktober.
Es geht um eine Spoolanalyse: Ihr sollte aus einem Spool, welcher aus einer ALV-Liste erzeugt wurde ( Im echten Leben bei mir ist es die Ausgabe eine SAP-Standardprogramms, welches im Nachtlauf ( sehr lange! ) halt einen Spool erzeugt hat, aus dem gewisse Informationen zu extrahieren sind ) die Originaldaten herausextrahieren.

Wie üblich habe ich ein kleines Demoprogramm erstellt, welches einerseits Testdaten und einen simulierten Spool erzeugt. Desweiteren ein Versuch, die Originaldaten zu rekonstruieren, welcher aber wie immer in meinen Aufgaben grandios fehlschlägt, da diverse Sachen nicht beachtet wurden.
Eure Aufgabe ist es, die Methode "ANALYZE_SPOOL" so zu korrigieren, dass das Programm sowohl in der einfachen als auch in der erweiterten Version beim Ausführen die grüne Meldung "Das ist korrekt" ausgibt. Aktuell kommt bei meinem Versuch leider nur "Originaldatei und Analyse haben unterschiedliche Anzahl von Zeilen" in roter Schrift heraus.

Wie üblich gerne eine PM an mich, wie eure Lösung aussieht und/oder einen Teaser als Antwort hier im Forum. Ich werde dann zum Wochenende wieder eine Zusammenfassung der unterschiedlichen Lösungsansätze veröffentlichen. Ich bin jedenfalls gespannt...

P.S. Eine kreative Lösung wäre, die Erzeugungsmethode aufzurufen - aber diese Art von Lösung werde ich diesmal nicht zulassen. Es soll tatsächlich die Information aus dem Spool geholt werden.

Code: Alles auswählen.

REPORT zknobelufgabe_2021_10.

CLASS lcl_knobel DEFINITION FINAL.
  PUBLIC SECTION.
    TYPES: BEGIN OF mts_data,
             field1 TYPE text40,
             field2 TYPE text40,
             field3 TYPE text40,
             field4 TYPE text40,
           END OF mts_data,
           mtt_data  TYPE STANDARD TABLE OF mts_data WITH NON-UNIQUE DEFAULT KEY,
           mtt_spool TYPE STANDARD TABLE OF text1024 WITH NON-UNIQUE DEFAULT KEY.
    METHODS:
      main,
      build_testdata RETURNING VALUE(rt_data) TYPE mtt_data,
      build_spool IMPORTING it_data         TYPE mtt_data
                  RETURNING VALUE(rt_spool) TYPE mtt_spool,
      analyze_spool IMPORTING it_spool       TYPE mtt_spool
                    RETURNING VALUE(rt_data) TYPE mtt_data.

ENDCLASS.

PARAMETERS: simple AS CHECKBOX DEFAULT 'X'.

END-OF-SELECTION.
  NEW lcl_knobel( )->main( ).


CLASS lcl_knobel IMPLEMENTATION.

  METHOD main.
    DATA: lv_color TYPE i.
    DATA(lt_original_data)  = me->build_testdata( ).
    DATA(lt_spool)          = me->build_spool( lt_original_data ).
    DATA(lt_spool_analysis) = me->analyze_spool( lt_spool ).
    IF lines( lt_original_data ) <> lines( lt_spool_analysis ).
      WRITE 'Originaldatei und Analyse haben unterschiedliche Anzahl von Zeilen' COLOR 6.
    ELSEIF lt_original_data = lt_spool_analysis.
      WRITE 'Das ist korrekt' COLOR 5.
    ELSE.
      WRITE 'Leider noch nicht korrekt' COLOR 6.
    ENDIF.
    SKIP 2.
    LOOP AT lt_spool ASSIGNING FIELD-SYMBOL(<lv_spool>).
      DATA(lv_tabix) = sy-tabix - 3.
      READ TABLE lt_original_data INDEX lv_tabix ASSIGNING FIELD-SYMBOL(<ls_original>).
      IF sy-subrc <> 0. " Mein Fehler oder Anfang oder Ende der LIste
        lv_color = 0.
      ELSE.
        READ TABLE lt_spool_analysis INDEX lv_tabix ASSIGNING FIELD-SYMBOL(<ls_spool_analysis>).
        IF   sy-subrc <> 0
          OR <ls_spool_analysis> <> <ls_original>.
          lv_color = 7. " falsch
        ELSE.
          lv_color = 5. " passt
        ENDIF.
      ENDIF.
      DATA(lv_len) = strlen( <lv_spool> ).
      WRITE: AT /(lv_len) <lv_spool> COLOR = lv_color.
    ENDLOOP.
  ENDMETHOD.

  METHOD analyze_spool.
**** Hier eure Analyse!

* Leider nicht so tolle Lösung, da die Prüfung ergibt, dass dies falsch sei.
    LOOP AT it_spool ASSIGNING FIELD-SYMBOL(<lv_spool>) FROM 4.
      APPEND INITIAL LINE TO rt_data ASSIGNING FIELD-SYMBOL(<ls_data>).
      SPLIT <lv_spool> AT sy-vline INTO DATA(lv_dummy) <ls_data>-field1 <ls_data>-field2 <ls_data>-field3 <ls_data>-field4.
    ENDLOOP.


  ENDMETHOD.

  METHOD build_spool.
    DATA: ls_data   LIKE LINE OF it_data,
          lv_length TYPE i.
    DATA(lo_csv) = cl_rsda_csv_converter=>create( i_delimiter = '"' i_separator = cl_abap_char_utilities=>horizontal_tab ).

    LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_data>).
      APPEND INITIAL LINE TO rt_spool ASSIGNING FIELD-SYMBOL(<lv_spool>).
      lo_csv->structure_to_csv( EXPORTING i_s_data = <ls_data> IMPORTING e_data   = <lv_spool>  ).
      <lv_spool> = |{ cl_abap_char_utilities=>horizontal_tab }{ <lv_spool> }{ cl_abap_char_utilities=>horizontal_tab }|. " Anfang und Ende auch
    ENDLOOP.
* Überschrift hinzufügen, damit es wie ein spool aussieht
    lv_length = strlen( <lv_spool> ).
    INSERT sy-uline(lv_length) INTO rt_spool INDEX 1.
    INSERT sy-uline(lv_length) INTO rt_spool INDEX 1.
    APPEND sy-uline(lv_length) TO rt_spool.

    lv_length = strlen( <ls_data>-field1 ).
    ls_data-field1 = 'FIELD1----------------------------------'.
    ls_data-field1 = ls_data-field1(lv_length).

    lv_length = strlen( <ls_data>-field2 ).
    ls_data-field2 = 'FIELD2----------------------------------'.
    ls_data-field2 = ls_data-field2(lv_length).
    lv_length = strlen( <ls_data>-field3 ).
    ls_data-field3 = 'FIELD3----------------------------------'.
    ls_data-field3 = ls_data-field3(lv_length).
    lv_length = strlen( <ls_data>-field4 ).
    ls_data-field4 = 'FIELD4----------------------------------'.
    ls_data-field4 = ls_data-field4(lv_length).
    INSERT INITIAL LINE INTO rt_spool INDEX 2 ASSIGNING FIELD-SYMBOL(<lv_header>).
    lo_csv->structure_to_csv( EXPORTING i_s_data = ls_data IMPORTING e_data   = <lv_header>  ).
    <lv_header> = |{ cl_abap_char_utilities=>horizontal_tab }{ <lv_header> }{ cl_abap_char_utilities=>horizontal_tab }|. " Anfang und Ende auch
    REPLACE ALL OCCURRENCES OF '-' IN <lv_header> WITH ` `.

* Aufhübschen, damit es wie eine ALV-Liste aussieht
    REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>horizontal_tab IN TABLE rt_spool WITH  sy-vline.

  ENDMETHOD.

  METHOD build_testdata.
    DATA: lo_rnd  TYPE REF TO cl_abap_random_int,
          lo_char TYPE REF TO cl_abap_random_int.
    lo_rnd  = cl_abap_random_int=>create( seed = CONV #( sy-uzeit ) min = 10 max = 40 ).
    IF simple = 'X'.
      lo_char = cl_abap_random_int=>create( seed = CONV #( sy-uzeit ) min = 65 max = 90 ). " Usual ASCII characters after " ( to avoid csv-shenanigans )
    ELSE.
      lo_char = cl_abap_random_int=>create( seed = CONV #( sy-uzeit ) min = 35 max = 126 ). " Usual ASCII characters after " ( to avoid csv-shenanigans )
    ENDIF.

* 100 Testreihen
    DO 100 TIMES.
      APPEND INITIAL LINE TO rt_data.
    ENDDO.
    DO 4 TIMES.
      DATA(lv_index)  = sy-index.
      DATA(lv_length) = lo_rnd->get_next( ).
      LOOP AT rt_data ASSIGNING FIELD-SYMBOL(<ls_data>).
        DO lv_length TIMES.
          ASSIGN COMPONENT lv_index OF STRUCTURE <ls_data> TO FIELD-SYMBOL(<lv_field>).
          <lv_field> = |{ <lv_field> }{ cl_abap_conv_in_ce=>uccpi( lo_char->get_next( ) ) }|.
        ENDDO.
      ENDLOOP.
    ENDDO.

  ENDMETHOD.

ENDCLASS.

Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag:
a-dead-trousers

live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de


Re: Knobelaufgabe ( Oktober 2021 )

Beitrag von a-dead-trousers (Top Expert / 3815 / 146 / 997 ) »
Vielen Dank Stefan, dass du dir für uns immer solche tollen Aufgaben ausdenkst. 🧐

Ich hab dir 2 Vorschläge geschickt, die sich hauptsächlich in der Performance unterscheiden (Sichwort FIELD-SYMBOL vs. WORKAREA mehr verrate ich nicht)

Highlights:
2 geschachtelte Schleifen
1 Hilfstabelle
1 bzw. 2 Hilfsvariablen
4 bzw. 6 Feldsymbole
38 bzw. 50 Zeilen Code
Theory is when you know something, but it doesn't work.
Practice is when something works, but you don't know why.
Programmers combine theory and practice: Nothing works and they don't know why.

ECC: 6.07
Basis: 7.40

Re: Knobelaufgabe ( Oktober 2021 )

Beitrag von black_adept (Top Expert / 3701 / 78 / 771 ) »
Hmm - nur eine Einsendung bisher. Ferien oder zu schwer oder zu einfach?
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de

Re: Knobelaufgabe ( Oktober 2021 )

Beitrag von Thomas R. (Expert / 723 / 58 / 30 ) »
Hallo Stefan,
simple ist simple
und für das andere habe ich wie eigentlich immer leider keine Zeit :-(

MfG
Thomas R.

Re: Knobelaufgabe ( Oktober 2021 )

Beitrag von black_adept (Top Expert / 3701 / 78 / 771 ) »
Thomas R. hat geschrieben:
21.10.2021 07:06
simple ist simple
Du hast zumindest den Knackpunkt entdeckt. 😛
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de

Re: Knobelaufgabe ( Oktober 2021 )

Beitrag von black_adept (Top Expert / 3701 / 78 / 771 ) »
Moin allerseits,
diesmal erst zum Anfang der Woche die Vorstellung der Lösungsansätze.

Wer sich mit dem Thema beschäftig hat, wird erkannt haben, dass die "simple" Version lediglich als Teaser eingebaut war und die "nicht simple" Version das eigentliche Problem darstellt.
Grundsätzlich ging es darum einen String in Felder aufzuteilen, wobei das Ganze dadurch erschwert wurde, dass das Trennsymbol ( bei ALV-Listen üblicherweise das sy-vline bzw. "|" ) in den Feldern selber vorkommen konnte, so dass der einfach Ansatz "split at Trennzeichen" fehlschlägt.
Alle eingesendeten Lösungen sind dann auf die Idee gekommen, dass es eine Zeile ohne diese "falschen" Trennzeichen gibt - nämlich die Überschrift.
Somit reduziert sich das Problem auf 2 Schritte:
Schritt1: Identifizieren der wahren Trennzeichen anhand der Überschrift.
Schritt2: Split jeder Zeile an den echten Trennpositionen in die einzelnen Felder.

Lösungsansatz 1: Via echter Länge der Felder
Schritt 1: Der FIND-Befehl

Code: Alles auswählen.

FIND ALL OCCURRENCES OF REGEX '[^|]+' IN <ld_spool> IN CHARACTER MODE RESULTS lt_header.
erzeugt eine Tabelle von Offsets und Längen der echten Felder der Überschrift, wobei schlussendlich die letzte Zeile davon noch entsorgt werden muss, da in dieser nur noch die Leerzeichen nach dem Trennzeichen hinter dem letzten Feld kommen.
Schritt2: Für alle Felder einer Listzeile wird jetzt diese Tabelle ausgewertet und der Feldwert damit ermittelt.

Code: Alles auswählen.

pro Listzeile <ld_spool>:
            LOOP AT lt_header ASSIGNING FIELD-SYMBOL(<ls_header>).
              ASSIGN COMPONENT sy-tabix OF STRUCTURE <ls_data>  TO FIELD-SYMBOL(<lv_datafield>).
              ASSIGN <ld_spool>+<ls_header>-offset(<ls_header>-length) TO <ld_field>.
              <lv_datafield> = <ld_field>.

            ENDLOOP.
Lösungsansatz 2: Mittels Befehl OVERLAY und SPLIT
Schritt 1: Auch in diesem Fall müssen zunächst die echten Trennzeichen in der Überschrift erkannt werden, wobei in diesem Fall die Position irrelevant ist.
Dies wird mittels des REPLACE-Befehls ausgeführt, der alle nicht-Trennzeichen aus der Überschrift durch Leerzeichen ersetzt und danach die verbleibenden Trennzeichen durch ein im Text nicht vorkommendes Zeichen ( Tabulator ) ersetzt.

Code: Alles auswählen.

*lv_real_separators vorher:
*|FIELD1          |FIELD2                  |FIELD3                             |FIELD4                        | 
    REPLACE ALL OCCURRENCES OF REGEX '[^\|]' IN lv_real_separators WITH ` `.
*lv_real_separators nachher:"
*|                |                        |                                   |                              |
    REPLACE ALL OCCURRENCES OF '|' IN lv_real_separators WITH cl_abap_char_utilities=>horizontal_tab.
Schritt 2: Hier glänzt jetzt der OVERLAY-Befehl, der ( in seiner verkürzten Form) jedes Leerzeichen eines Feldes durch den Text eines anderen Feldes ersetzt. Dies macht man sich zu Nutze, um in einer Spoolzeile alle Trennzeichen an den "echten" Trennpositionen durch das im Text nicht vorkommende Zeichen zu ersetzen und danach dann den SPLIT-Befehl doch zu verwenden um die Zeile direkt in die Felder zu trennen.

Code: Alles auswählen.

pro Listzeile <lv_spool>:
      DATA(lv_spool) = lv_real_separators. "Kopie der Tabulator-Trennzeichenzeile erhalten
      OVERLAY lv_spool WITH <lv_spool>. " Kominieren der Spoolzeile mit den Tabulatoren
      SPLIT lv_spool AT cl_abap_char_utilities=>horizontal_tab INTO DATA(lv_dummy) <ls_data>-field1 <ls_data>-field2 <ls_data>-field3 <ls_data>-field4 DATA(lv_dummy2).

Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag:
a-dead-trousers

live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de

Seite 1 von 1

Über diesen Beitrag



ABAP & SAP eBook Flatrate von Espresso Tutorials Sponsorlink
Unterstütze die Community und teile den Beitrag für mehr Leser und Austausch

Aktuelle Forenbeiträge

Texte im Rechnungskopf
Gestern von JHM 7 / 65
CO01 BADI
Gestern von L0w-RiDer 13 / 150
Dynpro Eingabe von Zahl mit Komma
Gestern von ewx 17 / 181
Probleme mit CORRESPONDING itab
vor 2 Tagen von ewx gelöst 7 / 87

Vergleichbare Themen

Knobelaufgabe - Advent 2021
von black_adept » 29.11.2021 12:58
Knobelaufgabe ( August 2021 )
von black_adept » 13.08.2021 14:17
Knobelaufgabe zum Wochenbeginn ( Mai 2021 )
von black_adept » 11.05.2021 16:21
Knobelaufgabe zum Wochenbeginn ( Juni 2021 )
von black_adept » 21.06.2021 16:29