A coworker, from a past programming life, called me up last week and asked about Trim Functions in COBOL. I sent her these PEFORMed functions, which I've had lying about and without any professional opportunity to use them.It's been my experience that most Cobologists working within their shop standard don't twiddle with CALLing real subroutines. Instead they PERFORM COPYed code, and refer to it as a subroutine. As such, I don't try to fight the tide.Can anyone offer up any improvements? I know there are Intrinsic TRIM, TRIML, and TRIMR functions in some flavours of COBOL. However, I do not believe IBM Z-series Mainframe COBOL has these, so this is intended to be platform independent.DATA DIVISION.WORKING-STORAGE SECTION.01  STR-VALUE-IN                 PIC X(1000).01  STR-LENGTH-IN                PIC 9(05) BINARY.01  STR-VALUE-OUT                PIC X(1000).01  STR-LENGTH-OUT               PIC 9(05) BINARY.01  STR-HANDLING-COUNTERS.    05  STR-SUB01                PIC 9(05) BINARY.    05  STR-TRAILING-SPACES      PIC 9(05) BINARY.    05  STR-LEADING-SPACES       PIC 9(05) BINARY.    05  STR-START                PIC 9(05) BINARY.01  STR-HANDLING-SWITCHES.    05  GRAPH-FOUND-SW           PIC X(01).        88  GRAPH-FOUND                    VALUE '1'.        88  GRAPH-NOT-FOUND                VALUE '2'.PROCEDURE DIVISION.    MOVE Whatever      TO STR-VALUE-IN.    MOVE FUNCTION LENGTH (Whatever)      TO STR-LENGTH-IN.    PERFORM 5200-RTRIM.    MOVE STR-VALUE-OUT (1:STR-LENGTH-OUT)      TO Wherever.    MOVE Whatever      TO STR-VALUE-IN.    MOVE FUNCTION LENGTH (Whatever)      TO STR-LENGTH-IN.    PERFORM 5100-LTRIM.    MOVE STR-VALUE-OUT (1:STR-LENGTH-OUT)      TO Wherever.    MOVE Whatever      TO STR-VALUE-IN.    MOVE FUNCTION LENGTH (Whatever)      TO STR-LENGTH-IN.    PERFORM 5300-TRIM.    MOVE STR-VALUE-OUT (1:STR-LENGTH-OUT)      TO Wherever.5100-LTRIM.* This resembles the VB function LTrim* It removes any leading value less than or equal to space* ASCII decimal 0 (Null) through ASCII decimal 32 (Space)    PERFORM 5910-COMMON-TRIM-BEGIN.    PERFORM 5930-TRIM-LEADING-SPACES.    PERFORM 5990-COMMON-TRIM-END.5200-RTRIM.* This resembles the VB function RTrim* It removes any trailing value less than or equal to space* ASCII decimal 0 (Null) through ASCII decimal 32 (Space)    PERFORM 5910-COMMON-TRIM-BEGIN.    PERFORM 5950-TRIM-TRAILING-SPACES.    PERFORM 5990-COMMON-TRIM-END.5300-TRIM.* This resembles the VB function Trim* It removes any leading or trailing value less than or equal to space* ASCII decimal 0 (Null) through ASCII decimal 32 (Space)    PERFORM 5910-COMMON-TRIM-BEGIN.    PERFORM 5930-TRIM-LEADING-SPACES.    PERFORM 5950-TRIM-TRAILING-SPACES.    PERFORM 5990-COMMON-TRIM-END.5910-COMMON-TRIM-BEGIN.    MOVE LOW-VALUES      TO STR-HANDLING-COUNTERS.    MOVE SPACES      TO STR-HANDLING-SWITCHES.    MOVE 1      TO STR-START.    MOVE STR-LENGTH-IN      TO STR-LENGTH-OUT.5930-TRIM-LEADING-SPACES.    SET GRAPH-NOT-FOUND TO TRUE.    PERFORM      VARYING STR-SUB01 FROM 1 BY 1        UNTIL STR-SUB01 > STR-LENGTH-IN          OR GRAPH-FOUND            IF (STR-VALUE-IN (STR-SUB01:1) > SPACES)                MOVE STR-SUB01                  TO STR-START                SUBTRACT 1 FROM STR-SUB01                    GIVING STR-LEADING-SPACES                SUBTRACT STR-LEADING-SPACES                    FROM STR-LENGTH-OUT                SET GRAPH-FOUND TO TRUE            END-IF    END-PERFORM5950-TRIM-TRAILING-SPACES.    SET GRAPH-NOT-FOUND TO TRUE.    PERFORM      VARYING STR-SUB01 FROM STR-LENGTH-IN BY -1        UNTIL STR-SUB01 < 1          OR GRAPH-FOUND            IF (STR-VALUE-IN (STR-SUB01:1) > SPACES)                SUBTRACT STR-SUB01 FROM STR-LENGTH-IN                    GIVING STR-TRAILING-SPACES                SUBTRACT STR-TRAILING-SPACES                    FROM STR-LENGTH-OUT                SET GRAPH-FOUND TO TRUE            END-IF    END-PERFORM5990-COMMON-TRIM-END.    MOVE STR-VALUE-IN (STR-START:STR-LENGTH-OUT)      TO STR-VALUE-OUT.
Trim Functions in COBOL
strings;cobol
I'm not sure you can generalise the same routine to EBCDIC on the Mainframe. You'd want two different routines, one for data with sourced from the Mainframe, which would only trim spaces, and another with the source from an ASCII machine which would want to trim more, potentially.We're picky about invalid data. The worst thing you can do with invalid data is ignore it (you miss the chance to find out why it is invalid).What to trim? Why everything from space downwards? Why not just space and tab? Leaving those points aside, since you specifically want what you want:No need for 9(5) if your string is a maximum of 1000. In COBOL the right-trim is questionable, since the MOVE STR-VALUE-OUT (1:STR-LENGTH-OUT) TO Wherever.  is going to space-fill Wherever. The length of the actual data is important if you want to be able to use the trimmed value, other than for ignoring low-non-blank values. Unless Wherever is a variable-length field.If you are using SET to change the values of the field with the 88-levels on, you shouldn't also MOVE SPACE to that field. A better definition is:01  FILLER.    05  FILER                      PIC X(01).        88  GRAPH-FOUND            VALUE '1'.        88  GRAPH-NOT-FOUND        VALUE '2'.Now only the 88-levels, via SET, can amend the value. If you give the group or the field itself a name, someone will come along and use reference-modification. Like this, the field can only be reference by SET, which makes locating where its value changes much simpler.There is no need to initialise it to space anyway, as you SET GRAPH-NOT-FOUND ... before using the field.The MOVE LOW-VALUES will set all the subordinate binary fields to zero, but with this there is some danger that someone adds to the group and can't work out where their value gets clobbered. Yes, the group has got a name, but it happens. MOVE ZERO to the individual items.In fact, looking at the code, there is no need to set any of those four fields to initial values, as they are always first referenced as the target of a verb, so their value prior to that is irrelevant.You have the opportunity to simplify the inline PERFORMs.IF ( STR-VALUE-IN       GREATER THAN SPACE )*> PERFORM Exclude the leading/trailing <= spaceELSE*> PERFORM There is no data presentEND-IFAs well as saving you looking at 1000 bytes per Left/Right Trim, it simplifies...PERFORM  VARYING STR-SUB01 FROM 1 BY 1    UNTIL ( STR-VALUE-IN ( STR-SUB01 : 1 )              GREATER THAN SPACE )END-PERFORMThe PERFORM is just used to find the first character, you know there is at least one because of the test above.MOVE STR-SUB01  TO STR-STARTSUBTRACT 1 FROM STR-SUB01  GIVING STR-LEADING-SPACESSUBTRACT STR-LEADING-SPACES  FROM STR-LENGTH-OUTSimilar for the trailing.Note that the 88s discussed earlier have now disappeared, not needed.You don't need STR-VALUE-OUT. You are using reference-modification, you can just use that on STR-VALUE-IN.Since you are not concerning yourself with COBOL prior to the '85 Standard (although there are '74 Standard compiler still in use today) I'd suggest minimal use of full-stop/period in the PROCEDURE DIVISION.End of PROCEDURE DIVISION header; end of pargraph/SECTION name; a full-stop/period on a line of its own as the last statement in a paragraph/SECTION; a full-stop/period on a line of its own as the last statement the program. (there is overlap here, since you don't have to have any paragraphs/SECTIONs in a program).Prior to reference-modification ('85 Standard) mostly people would do a byte-by-byte MOVE. The alternative to that, on an IBM Mainframe at least, is to use variable-length fields, but the byte-by-byte is the only portable solution across compilers from 1960 onwards. For '85 onwards, reference-modification is a portable solution. From at least 1964, on an IBM Mainframe, you could use variable-length fields. This may have worked from day one of IBM COBOL, but 1964 is the earliest documentation I can find. The code to do this would have changed when IBM dropped its Compiler Extension allowing OCCURS DEPENDING ON with REDEFINES.Being portable, even in a portable language like COBOL, isn't always easy. I'd say that the code you produced, ignoring the EBCDIC point I made earlier, would work on every COBOL compiler to the '85 Standard or later, so it is pretty portable :-) (There are still some ICL, UNISYS and Tandem/HP machines actively using COBOL '74).To generalise your routine further, you'd have to be able to indicate what values need to be trimmed. Which would make your inline-PERFORMs more complicated again, which is why I stuck to your existing spec :-)