I have the following variable in COBOL program which gets its value from a file, when it is read:
01 Employee-number PIC 09(8) comp
01 Employee-number-x redefines
Employee-number PIC x(04)
I have another variable in the same program:
01 D-element-number PIC 9(04)
Now,
Move Employee-number to D-element-number
Then I write this D-element-number to a file
value read from input file is :
0013
0024
so this value comes to Employee-number and Employee-number-x and I move this value to D-Element-number and write this variable to a output file.
But I get this in the output file:
4660
FFFF
4660
4660 is the decimal equivalent of X'1234'
But I want to see something like:
1234
FFFF
1234
How can I achieve this ?
I am allowed to change the definition of D-element-number but nothing else.
Assuming that when you have X'00001234' you want C'00001234', have a look here. http://ibmmainframes.com/viewtopic.php?p=234231#234231
Ignore the rest of the discussion for now, just concentrate on that post.
This is the key part:
PERFORM UNTIL X-WS-1000 > X-WS-1000-MAX
MOVE WS-1000-BYTE-TBL (X-WS-1000)
TO WS-PACKED-X (1:1)
MOVE WS-PACKED TO WS-DISPLAY
MOVE WS-DISPLAY-X TO WS-2000-BYTE-TBL (X-WS-2000)
SET X-WS-1000 UP BY 1
SET X-WS-2000 UP BY 1
END-PERFORM
You need the exact storage definitions to go with it (don't "correct" anything).
It works by getting the compiler to use the "unpack" instruction (UNPK). This one is working one byte at a time, so simple to explain.
X'12' (an example on-byte field) is put in a two-byte field. X'12ii' (where the value of ii is "irrelevant").
UNPK will then turn this "packed" number into a "zoned" number. A "Zone" is the first four bts of a byte, and for a Zoned number, the four bits are all set to one. So you get an F. Then you get the left-most digit from the first byte. Then you get the second output byte, first four bits set to F, then the second input digit.
Then the UNPK continues with the final byte, which contains one irrelevant digit, and an irrelevant sign. For a Zoned number, the sign and right-most digit occupy the same byte (the sign in the zone) so you get a whole byte of irrelevance.
X'12' -> X'12ii' -> X'F1F1ii'.
The first two bytes of the three-byte output are C'12'.
Nos, what is all fine for numbers, but letters make a mess:
X'AB' -> X'ABii' -> X'FAFBii'
Although F and a digits gives a displayable number, for F and a letter, the result does not mean much directly.
Now the INSPECT ... CONVERTING comes to the rescue: FA gets translated to C'A' (X'C1), and the same for the letters through to F.
Your results after the CONVERTING will be C'AB'.
Should give you enough to work on.
There are other methods, but this is a fair COBOL approximation to the classic Assembler technique with UNPK and a TRANSLATE (TR) and a table of translation values.
If you use your favourite search engine. you should be able to fine more methods, using calculation (more than one), table-lookups, I've even seen a 256-WHEN EVALUATE that "works", but I guess it is a little on the "slow" side.
Thinking further, you actually have a BCD (Binary Coded Decimal) don't you? This is a Packed Decimal, without the sign. You don't have any alphas in your field.
This is even simpler to convert.
01 the-converted-value PACKED-DECIMAL PIC 9(8)V9 VALUE ZERO.
01 FILLER REDEFINES the-converted-value.
05 the-binary-value PIC X(4).
05 FILLER PIC X.
MOVE Employee-number-x TO the-binary-value
MOVE the-converted-value TO D-element-number (with Gilbert's correction to PIC 9(8)).
The "decimal place" is the ii, the ignore value and ignore sign. You no longer need the INSPECT ... CONVERTING ... as you only have numeric digits. If you have a BCD...
A really good way for you to have answered your own question would have been to find out how the number was created in the binary field in the first place.
Didn't try this, but I think it might work:
01 WN-GROUP.
05 Employee-number PIC 9(8) comp.
05 PACKED-ZERO PIC 9(1) COMP-3 VALUE ZERO.
01 WN-PACKED redefines WN-GROUP PIC 9(9) COMP-3.
01 WN-UNPACKED PIC 9(9).
Then in your procedure:
MOVE your-number TO Employee-number
MOVE WN-PACKED TO WN-UNPACKED.
And finally you can move or display WN-PACKED(1:8).
Related
Moving data to COMP-3 fields after UNSTRING.
UNSTRING is working fine but I am not able to move data to COMP-3 fields without an S0C7 data exception abend.
I think it is an issue with storing data.
Below is my COBOL program.
IDENTIFICATION DIVISION.
PROGRAM-ID. ADDPROG.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 VALUEA PIC X(20) VALUE '64.99|64.99'.
01 NOA PIC S9(9)V9(02).
01 NOB PIC S9(9)V9(02).
01 NOC PIC S9(9)V99 COMP-3.
01 NOD PIC S9(9)V99 COMP-3.
PROCEDURE DIVISION.
000-MAIN.
DISPLAY "EARLIER".
DISPLAY 'NOA-' NOA.
DISPLAY 'NOB-' NOB.
DISPLAY "AFTER".
UNSTRING VALUEA
DELIMITED BY '|'
INTO NOA,NOB.
DISPLAY 'NOA-' NOA.
DISPLAY 'NOB-' NOB.
MOVE NOA TO NOC.
MOVE NOB TO NOD.
DISPLAY 'NOC-' NOC.
DISPLAY 'NOD-' NOD.
STOP RUN.
Output I am getting after compiling.
Please let me know is there any other way to move data to COMP-3 fields or to avoid this error.
Your code has two main problems. To see them you first have to understand that your UNSTRING into NOA and NOB works like any other character-to-character MOVE.
So it starts from the left and moves character after character until one field ends and if necessary adds some blanks to fill up the receiving field.
So problem one is that NOA contains the value left-justified while according to your PIC-clause it should be right-justified, so you would need an intermediate PIC X(12) JUSTIFIED RIGHT field that you UNSTRING to.
The second problem that is causing the S0C7 is that your PIC-clause does not include a decimal-point. The V specifies the implied position of the decimal point but it would not show on output nor is it handled correctly when parsing the field-contents. To have a field that correctly handles the decimal point you should have a PIC S9(9)V.9(02).
Please also see this question.
Like piet.t said, the UNSTRING statement is like a alphanumeric move. Therefore my suggestion is to:
make NOA and NOB be alphanumeric (PIC X(12)), and
change your move statements to include WITH CONVERSION so that it will convert the numeric data in the alphanumeric field into COMP-3.
Your decimal data field are wrong just have . Like 01 NOA PIC S9(9).(02) In them! Same with noc variabile then it should run.
To move data from a numeric field to a COMP-3 field, you need to make sure that the data in the numeric field is valid for the COMP-3 field. A COMP-3 field stores packed decimal data, which means that the digits of the number are packed into two-digit decimal fields. The decimal point is not stored, and the sign of the number is stored in the last nibble of the field.
I realize I have asked a similar question before, but the whole thing is more complicated than I thought.
To cut to the chase, I need to convert a string that contains numbers and letters into a string that only contains numbers, while keeping the numbers that were already there, in the right position.
The letters need to be converted to their corresponding position in the Alphabet + 9. So, A = 10, B= 11.... Z = 35.
So, basically, a string that looks like this:
'GB00LOYD1023456789A1B2'
will have to become:
'161100212429131023456789101112'.
I bolded the letters in both examples so you can see the difference more clearly. Depending on the input, the content will be longer or shorter than this example. Letters will be alternated by numbers and vice versa.
What's the best way to do this?
What's the best way to do this?
That is a matter of opinion.
The REPLACING option of the INSPECT verb requires the replacing and replaced character strings to be the same size, so that's right out because you need to replace one character with two. This is true at least for IBM COBOL.
A way to do this would be to loop through your input string and do a class check on each character. Something like...
01 Stuff.
05 in-posn pic s999 packed-decimal value +0.
05 out-posn pic s999 packed-decimal value +1.
05 in-string pic x(022) value 'GB00LOYD1023456789A1B2'.
05 out-string pic x(100) value spaces.
05 replacer pic x(002) value spaces.
perform varying in-posn from 1 by 1
until in-posn > length of in-string
if in-string(in-posn:1) alphabetic
evaluate in-string(in-posn:1)
when 'A' move '10' to replacer
when 'B' move '11' to replacer
.
.
.
when 'Z' move '35' to replacer
end-evaluate
string replacer delimited size
into out-string
pointer out-posn
end-string
else
string in-string(in-posn:1) delimited size
into out-string
pointer out-posn
end-string
end-if
end-perform
There are variations available. You could replace the evaluate with a couple of table lookups. You could store the length of in-string before beginning the loop. You could store in-string(in-posn:1) rather than hoping the compiler will do that for you.
This is just freehand but I think it conveys the idea.
Is it possible to assign a string of hexadecimal to a comp or binary declared variable?
Example:
01 COMP-VAR PIC 9(4) COMP.
MOVE X'04D2' TO COMP-VAR.
should output +1234.
Edited:
Sorry for the lack of the information, I just gave an example. The real scenario is that the data will come from an external source, a dataset. I need to store the data in an alphanumeric variable before I move it to a comp declared variable. My problem is that the data is incorrect when I move the alphanumeric data to the comp variable. Your help is very much appreciated.
I think you are looking for REDEFINES. Redefine the binary value as character, do the assignment
which will not violate any of the assignment rules and then use the binary representation in
subsequent operations. This program illustrates your example:
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01.
02 COMP-VAR PIC 9(4) COMP.
02 COMP-X REDEFINES COMP-VAR PIC X(2).
PROCEDURE DIVISION.
MOVE X'04D2' TO COMP-X
DISPLAY COMP-VAR
GOBACK
.
This displays 1234.
The larger question is why would you need to do this? I suspect that you are attempting to
read a file with multiple record formats in it. Based on some common record identifier you
need to read part of the record as character or as binary. Typically this is done a little
differently in COBOL.
Here is a larger example of what I mean. Suppose you have an input
record that is 3 bytes long. When the first byte is a 'B' it is telling you that the next two bytes should be
treated as a binary (COMP) value. When the first byte is an 'X' you need to read the next two
bytes as text (X) data. As an example this is what two records might look like:
X'E7C1C2'
X'C204D2'
The first record is a text record containing the value 'AB' (EBCDIC). The second record is binary containing
the value 1234. The program to process these records might look something like:
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 INPUT-RECORD.
02 REC-TCD PIC X.
88 REC-TCD-BIN VALUE 'B'.
88 REC-TCD-CHAR VALUE 'X'.
02 REC-DUMMY PIC X(2).
02 REC-COMP-VAR REDEFINES REC-DUMMY PIC 9(4) BINARY.
02 REC-CHAR-VAR REDEFINES REC-DUMMY PIC X(2).
PROCEDURE DIVISION.
*
* THIS IS A CHARACTER RECORD
*
MOVE X'E7C1C2' TO INPUT-RECORD
PERFORM DISPLAY-INPUT-RECORD
*
* THIS IS A BINARY RECORD
*
MOVE X'C204D2' TO INPUT-RECORD
PERFORM DISPLAY-INPUT-RECORD
GOBACK
.
DISPLAY-INPUT-RECORD.
EVALUATE TRUE
WHEN REC-TCD-BIN
DISPLAY 'REC TYPE: ' REC-TCD
' BINARY DATA: ' REC-COMP-VAR
WHEN REC-TCD-CHAR
DISPLAY 'REC TYPE: ' REC-TCD
' CHAR DATA : ' REC-CHAR-VAR
WHEN OTHER
DISPLAY 'UNKNOWN RECORD TYPE: ' REC-TCD
END-EVALUATE
.
The output from this program is:
******************************** Top of Data ***********************************
REC-TYPE: X CHAR DATA : AB
REC-TYPE: B BINARY DATA: 1234
******************************* Bottom of Data *********************************
Look at the INPUT-RECORD definition. The first byte determines how the rest of the
record is to be intrepreted. REC-DUMMY is generally defined as a "generic" buffer area
to be subsequently redefined. In the case of variable length input records, REC-DUMMY
is defined to be as long as the longest record variant so the subsequent REDEFINEs of it
do not "upset" the compiler. All data items following REC-DUMMY begin with the same level
number (02 in the example) and REDEFINE it to the the appropriate format. Subsequent
processing uses whatever record redefinition is appropaiate based on the value in REC-TCD.
IF AWA-REQ-DATE < WS-JULIAN-DATE
MOVE VAR1 to VAR2
The AWA-REQ-DATE is a binary integer i.e. PIC S9(09) COMP, whereas Julian date is PIC X(10) VALUE SPACES.
Both have Julian date inside them like 2013031 & 2013099.
This gives:
ERROR:REQ-DATE (BINARY INTEGER)" was compared with "WS-JULIAN-date (ALPHANUMERIC)". Discarded
Can I compare then with converting one of them to other format right here in code?
All your four-digit-year Julian dates will contain seven digits. They are dates, so naturally are positive.
It is unclear why you have a nine-digit, signed, binary to hold such or date. Nor a 10-byte alphanumeric. It is also unclear why this should have an initial value of SPACE.
01 WS-JULIAN-DATE VALUE SPACE.
05 WS-JULIAN-DATE-NUM PIC 9(7).
05 FILLER PIC XXX.
This assumes all your WS-JULIAN-DATE values are left-aligned.
IF AWA-REQ-DATE < WS-JULIAN-DATE
MOVE VAR1 to VAR2
END-IF
Hopefully VAR1 and VAR2 are just sample names for the question. If not, please make them meaningful, as it will make it much easier for the next person reading the program to understand. And that might be you.
If the values of WS-... are not guaranteed to be NUMERIC, test them for NUMERIC and take appropriate action (according to your spec) if they are not.
The nine-digit binary will potentially generate extra code beyond what is needed.
Another possibility is:
01 WS-AWA-REQ-JULIAN-DATE VALUE SPACE.
05 WS-AWA-REQ-JULIAN-DATE-NUM PIC 9(7).
05 FILLER PIC XXX.
MOVE AWA-REQ-DATE TO WS-AWA-REQ-JULIAN-DATE-NUM
IF WS-AWA-REQ-JULIAN DATE < WS-JULIAN-DATE
MOVE VAR1 to VAR2
END-IF
Which you choose can depend on what else you are doing with the fields.
Also, if one is "invariant", convert that to the same format as the variable one, once only.
In the various ISO/ANSI COBOL standards, comparing alphanumeric (PIC X) and numeric (PIC 9) is like comparing apples and oranges. There are defined rules. Of course, each compiler has different ways of interpreting the standard. Therefore, you are best off converting one of the fields to the other format and comparing those. Bill Woodger has some good comments about which field to convert, and how.
In summary, you should always compare like data types. For numeric items, it's best if you can compare the same COMP format, but sometimes this can't be done. If that is the case, you need to read your compiler documentation to see how comparisons are performed between different computational types, and any gotchas (such as COMP-4 and COMP-5 in IBM's Enterprise COBOL).
The answer probably depends on which compiler you are using. On GNU Cobol, the comparison works after applying FUNCTION NUMVAL to the Julian (text) date.
As the error message is describing: a numeric value PIC S9(9) COMP cannot be compared to a string PIC X(10).
A COMP variable is a system dependent, numeric representation of the value. There are variations to the internal representation (binary, BCD,...) used for COMPutation. The value of your date is,depending on the machine though, internally represented as: 0011110 10110111 01100111
Display variables are storing each position of your variable according to an encoding standard (ASCII, EBCDIC, UNICODE). Again depending on your machine and the encoding table, the representation of your variable might be: 00110010 00110000 00110001 00110011 00110000 00111001 00111001
I do some guessing here in order to address the underlying problem
First guess: The alpha definition PIC X(10) looks like the date is in printing format and should contain separators in order to make it 10 characters long
21.02.2014
123456789A
Such a variable shouldn't hold a julian date after all and you probably need a date comparing function.
Second guess: for some displaying reason, you want to have a variable of 10 characters long, holding the julian date in the first 7 characters. In this case, I'd define a structure and you can keep your comparison as is:
01 WS-JULIAN-DATE-PRINT VALUE SPACE.
05 WS-JULIAN-DATE PIC 9(7).
05 FILLER PIC X(3).
I have a string for which I wish to tally the count of characters till a certain pattern of characters is found.
For example:
Give a string: askabanskarkartikrockstar
I would like to know how many characters are there before the kartik in the string.
In a normal scenario where I need to find the number of characters before, say k, in the given string, I would write the code somewhat as:
INSPECT WS-INPUT-STRING TALLYING CT-COUNTER FOR CHARACTERS BEFORE LT-K
Where
WS-INPUT-STRING is alphanumeric with a value of
askabanskarkartikrockstar,
CT-COUNTER is the counter used to count the number of characters
LT-K is a literal with the value k.
But here, if I wish to do the same for a sub-string, like kartik in the above example, would replacing the value of LT-K with kartik instead of just k work? If yes, is the same applicable for alphanumeric literals that have values in the form of hexadecimal numbers (for example, in a literal X(02) one stores a new-line character as x'0D25')?
I'm trying to implement the above code in zOS IBM mainframe v10. Thanks.
You have pretty much answered your own question... The answer is yes you can do this. Here is a working example program:
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-INPUT-STRING PIC X(80).
01 WS-COUNTER PIC 9(4).
01 WS-TAG PIC X(10).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE 'askabanskarkartikrockstar' TO WS-INPUT-STRING
MOVE ZERO TO WS-COUNTER
MOVE 'kartik' TO WS-TAG
INSPECT WS-INPUT-STRING
TALLYING WS-COUNTER
FOR CHARACTERS BEFORE WS-TAG(1:6)
DISPLAY WS-COUNTER
GOBACK
.
WS-COUNTER displays as 11, there are 11 characters before the WS-TAG string.
Notice that I defined WS-TAG as PIC X(10). This variable is longer than the actual tag value you are looking for. To prevent the INSPECT verb from trying to match on trailing spaces introduced by:
MOVE 'kartik' TO WS-TAG
I had to specify a reference modified value for INSPECT to search for. Had I simply used:
FOR CHARACTERS BEFORE WS-TAG
without reference modification, WS-COUNTER would have been 80 - the length of WS-INPUT-STRING. This is because the string 'kartik ' is not found and the counter tallies the length of the entire input string.
Another approach would be to specify the tag as a literal:
FOR CHARACTERS BEFORE 'kartik'
You can move hexadecimal constants into PIC X fields as follows:
MOVE X'0D25' TO WS-TAG
This occupies 2 characters so you would use WS-TAG(1:2) when INSPECTing it.
If you want to do "a lot" of this at once, then you'll find a PERFORM VARYING will be faster. It is more typing, and you have to think more, and there is more chance for error. But once you have one working, you just have to copy the code to reuse it.