BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
MarkATremel
Obsidian | Level 7

I need to input two hexadecimal bytes and manipulate the 4 nibbles that are contained within.  For example, the two bytes in the file contain '13BC'x.  The first nibble is '1', the second nibble is '3', the third nibble is 'B' and the fourth nibble is 'C'.  Due to some *creative* programming, I need to isolate the first nibble and interpret it and then isolate the last three nibbles and interpret them.  If the 4 nibbles can be converted to a string, I can proceed from there.

 

Does anyone know which INFORMAT I should use?   Also, what functions or statements would I use to produce a 4 char string of hex characters that I can then manipulate?  Eg.  SUBSTRING(hex,1,1) for value of '1' etc.

 

Note:  I am a long time mainframe base SAS user, but first time question asker!  Thanks in advance for any hints, suggestions or outright answers!

 

INPUT...
@0553 SUSPCD01 $HEX2.
@0555 SUSPCD02 $HEX2.
@0557 SUSPCD03 $HEX2.

 

NOTE: Invalid data for SUSPCD01 in line 9 553-554.
NOTE: Invalid data for SUSPCD02 in line 9 555-556.
NOTE: Invalid data for SUSPCD03 in line 9 557-558.

 

RULE: ----+----1----+----2----+----3----+----4----+----5----+----6
501 ..................................................... . . ..
ZONE 00000000000000000000000000000000000000000000000000001B1B1B00
NUMR 00000000000000000000000000000000000000000000000000003CBCBC00

 

SUSPCD01= SUSPCD02= SUSPCD03=

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

They don't seem to understand the difference between a bit and nibble.   A bit is one binary digit.  A nibble is 4 binary digits.  There are 8 bits in a bytes and two nibbles in a byte.  There are 16 bits in two bytes and 4 nibbles in 2 bytes.

 

Let's look at the two byte string with the hex code of '2118'x.  If you read that using S370FPIB informat you get the number 8,472. If you take the remainder when dividing by 16**3 you get the 3 least significant digits, which is 280.

If you want the first nibble take integer value of dividing by 16**3.  If you want to treat that as a digit in the 16s place of your hex number then just multiply by 16.  Or subtract the number you found that has the last three nibbles and divide by 2**8 to remove the second byte (which will be all zeros).

102   data test;
103     string='2118'x ;
104     number = input(string,s370fpib2.);
105     number1 = mod(number,16**3);
106     number2 = (number - number1)/256;
107     put (_all_) (=/);
108     format string $hex4.;
109   run;


string=2118
number=8472
number1=280
number2=32

 

View solution in original post

23 REPLIES 23
japelin
Rhodochrosite | Level 12

Is the original data in hexadecimal?
I think you can input it as a character type and then process it.

 

Would the following sample code be helpful?

data sample;
  length n 8 c $8;
  n=12;c='12';output;
  n=255;c='0A';output;
  n=65535;c='FF';output;
run;

data _null_;
  set sample;
  bin_n=put(n,binary16.);
  bin_c=put(c,binary16.);
  hex_n=put(n,hex4.);
  hex_c=put(c,hex4.);
  dec_c=input(c,hex4.);
  put _all_;
run;

results:

n= 12   c=12 bin_n=0000000000001100 bin_c=0011000100110010 hex_n=000C hex_c=3132 dec_c= 18 _ERROR_=0 _N_=1
n=255   c=0A bin_n=0000000011111111 bin_c=0011000001000001 hex_n=00FF hex_c=3041 dec_c= 10 _ERROR_=0 _N_=2
n=65535 c=FF bin_n=1111111111111111 bin_c=0100011001000110 hex_n=FFFF hex_c=4646 dec_c=255 _ERROR_=0 _N_=3
Tom
Super User Tom
Super User

If you want to read 2 bytes using $CHAR2. informat. 

INPUT... @0553 SUSPCD01 $char2. ... ;

If you want to convert two bytes into 4 hexadecimal digits use $HEX4. format.

digits = put(SUSPCD01,$hex4.);

If you want to take one of those digits use SUBSTR(), or CHAR() , function.

first_digit = substr(digits,1,1);

If you want to replace one or more of those digits use SUBSTR() on the left side of an assignment statement .

substr(digits,1,1)='A';

If you need to convert those 4 digits back into two bytes use the $HEX4. informat. 

new_SUSPCD01 = input(digits,$hex4.);

If you want the write the new 2 byte string back to a file use the $CHAR2. format.

put ... new_SUSPCD01 $char2. ... ;

 

Kurt_Bremser
Super User

The $HEX informat expects only digits 0 to 9 and characters A to F (can also be lowercase) in the input string.

0xBC is clearly not one of these characters.

Read the strings with the $CHAR2. informat, then extract the nibbles like this:

length nib1 $1;
nib1 = substr(put(suspcd01,%hex4.),1,1);
MarkATremel
Obsidian | Level 7

I really appreciate everyone who chipped in, but so far no luck.  I am probably not describing the problem very well.  The data I am reading in is packed data.  I am using this on an IBM mainframe and the data is stored on tape (cartridge.)  (Also, there is a LOT of this data, so testing is tough.)   I am also using MVS SAS 9.4. 
I am providing this extra detail in case it is helpful.

 

I added this as my first test:

DATA TEST;
NAME='6C6C'X;
NAME2=PUT(NAME,$HEX4.);

PROC PRINT;

 

The output was this:

The SAS System
Obs NAME NAME2
1    %%  6C6C

 

Next, I defined the input specification like this:

@0553 SUSPCD01 $CHAR2.
@0555 SUSPCD02 HEX2.
@0557 SUSPCD03 $PHEX2.

 

All three suspcd values are formatted the same, I am trying three different informats to see which will work. 

 

Then I tried formatting the values as hex characters:

SCTEST1 = PUT(SUSPCD01,$HEX4.);
SCTEST2 = PUT(SUSPCD02,$HEX4.);
WARNING: Variable SUSPCD02 has already been defined as numeric.
SCTEST3 = PUT(SUSPCD03,$HEX4.);

 

The output was this:

The SAS System
Obs      SCTEST1 SCTEST2 SCTEST3 SUSPCD01 SUSPCD02 SUSPCD03
11658

 

The SAS log gives me an error due to the 2nd SUSPCD informat being invalid:

NOTE: Invalid data for SUSPCD02 in line 9 555-556.

 

But that is helpful, because it shows the values in those positions:

501 ..................................................... . . ...........
ZONE 00000000000000000000000000000000000000000000000000001B1B1B00000000000
NUMR 00000000000000000000000000000000000000000000000000003CBCBC00000000000
121 The SAS System

RULE: ----+----1----+----2----+----3----+----4----+----5----+----6----+----

 

The values in the data for the positions defined are packed hexadecimal (eg. 13BC, for the first value) and I want to read them in and process those values like a string.  I really thought that SCTEST1 was going to work.  I feel like I should be reading the data using a numeric informat for packed hex and then writing out that number as a hex string.  I really thought that either SUSPCD01 or SUSPCD03 from my example here was going to work.  I am feeling both lost and pretty close at the same time.  Thanks again for all your help so far.

Tom
Super User Tom
Super User

If you have a value like 13BC then it is not packed decimal as B is not a valid decimal digit.

Do you mean the values are BINARY?

Use the PIB informat to convert the bytes into integers.

Use the PIB format to convert the integers into bytes.

56    data test;
57      string='13BC'x ;
58      number = input(string,pib2.);
59      string2 = put(number,pib2.);
60      put string= $hex. number= comma12. number= hex4. string2= $hex. ;
61    run;

string=13BC number=48,147 number=BC13 string2=13BC
NOTE: The data set WORK.TEST has 1 observations and 3 variables.

You also have to decide whether to treat the values as "bigendian" or "littlendian".

62    data test;
63      string='13BC'x ;
64      number1 = input(string,pib2.);
65      number2 = input(string,s370fpib2.);
66      put string= $hex.
67        / number1= comma12. number1= hex4.
68        / number2= comma12. number2= hex4.
69      ;
70    run;

string=13BC
number1=48,147 number1=BC13
number2=5,052 number2=13BC
NOTE: The data set WORK.TEST has 1 observations and 3 variables.
MarkATremel
Obsidian | Level 7
Well, ultimately *all* values are binary. What I need to do is read in these values either as binary or hexadecimal and then format them or write them to a string variable as a hexadecimal representation of the value stored in the two bytes. Informats in SAS usually do some conversion, so I am fine with whatever informat I use converting it to a decimal value when it is stored in the SAS data vector, but I should be able to then write it out in whatever format I choose. So, I have tried reading it in using: PIB2., IB2., PK2., HEX2., CHAR2., $PHEX2. and probably a few more. Some give errors or warnings and I have taken heed and am not using those again. I have just used PIB2. and IB2. as informats (no warnings) but when I try to "write it out" as a string of characters I am not successful.

@0553 SUSPCD01 PIB2.
220 SCTEST1 = PUT(SUSPCD01,$HEX4.);
WARNING: Variable SUSPCD01 has already been defined as numeric.

Furthermore, I get these results:

PROC PRINT;
FORMAT SUSPCD01 HEX4.;
FORMAT SUSPCD02 BEST4.;
FORMAT SUSPCD03 IB8.;
VAR
SUSPCD01 SUSPCD02 SUSPCD03
SCTEST1 SCTEST2 SCTEST3 ;
-----------------------------------------------------
The SAS System

Obs SUSPCD01 SUSPCD02 SUSPCD03 SCTEST1 SCTEST2 SCTEST3

11658

Again, I appreciate all the advice and support (for a first time poster this is especially nice) and I feel like I am really close, but missing some basic fact or bit of information. (computer nerd humor embedded in that last comment!)
Kurt_Bremser
Super User

When you read 2 characters with the $CHAR2. informat, you get those characters, period. If you get something else, then your positioning is off.

If you need to read 2 characters from position 553, first read a dummy variable with $CHAR552. and then your variable. Next, display the variable in your data step with

put var $hex4.;

so that you see the true content.

MarkATremel
Obsidian | Level 7

I did as you suggested.

 

INPUT...

@0553 SUSPCD01 $CHAR2.
@0555 SUSPCD02 $CHAR2.
@0557 SUSPCD03 $CHAR2.

 

PROC PRINT;
FORMAT SUSPCD01 $HEX4.;
FORMAT SUSPCD02 $HEX4.;
FORMAT SUSPCD03 $HEX4.;
VAR SUSPCD01 SUSPCD02 SUSPCD03 SCTEST1 SCTEST2 SCTEST3;

 

Results:

Obs   SUSPCD01 SUSPCD02 SUSPCD03 SCTEST1 SCTEST2 SCTEST3
11658   4040     4040     4040

 

The values in those 3 suspcd fields are not blanks.  They are hex values that do not correspond to EBCDIC characters.  I think the $CHAR2. informat is confused by the values and is defaulting it to blanks.  So, first off, I think it needs to be a different informat, not $CHAR2., but I still do not know which informat will leave the internal values intact.  Also, the $HEX4. output is fine when I use a character based informat, but I get the feeling I cannot do that.  So, I still find myself looking for the correct informat and correct way to display the hex values as a character string.  I appreciate the thoughtfulness in your response Kurt and I am frustrated with my own inability to figure this out (usually *I* am the one people go to with SAS questions!)

Tom
Super User Tom
Super User

'40'x is EBCDIC code for a space.

Did you get transcoding notes in the log or not?

Are you reading file using RECFM=F?

How are you accessing the file?  Did someone transfer it from the IBM mainframe to your SAS server as a TEXT file instead of a BINARY file?

 

MarkATremel
Obsidian | Level 7

Um, no offense, but I did not accept this as the solution.  This is twice now where I am not even on the page and the question I have posted is marked as solve. The first time I thought I may have done it accidentally, but not this time.

 

Anyway, yes '40'x is a space, which is why I suspect that the informat is translating the hex values.  The field does not contain '40'x.  

No transcoding notes in the SAS log.

Yes, file is fixed FB.

I am accessing it on the mainframe where I am running the program.  No SAS server, just mainframe SAS and my crummy old file on cartridge.

Tom
Super User Tom
Super User

So check in order.  The options on your INFILE statement.  The JCL on the DD statement that is pointing at the file.

 

What do you see in the _INFILE_ automatic variable at those positions?  For example to check the two bytes in columns 533 and 534 for the record use something like:

....
input @ ;
string = substr(_infile_,0533,2);
put string $hex4.;
MarkATremel
Obsidian | Level 7
okay, i did that but the position of the field and the length of the field is not the issue. the problem is the informat and using the values i read in with that informat afterward. i KNOW that the fields contain the values i need because when i specify the informat wrong and SAS gives me errors in the SAS log for invalid data, it prints the values it sees in hex (see posts above.) and in that display at position 553, for 2 bytes i can see '13BC'x, which does not translate into any EBCDIC printable character. btw, as a super user can you mark this question as still open? i have no idea why it got marked as solved. thanks
Tom
Super User Tom
Super User

Try setting the infile option ENCODING='ANY'.

 

Try this test program.  The ENCODING= option does appear to be needed but it does no harm.

filename test temp;
data _null_;
  string='2118'x ;
  file test recfm=f lrecl=2;
  put string $char2.;
run;

data test;
  infile test recfm=f lrecl=2 encoding='any';
  input string $char2. @1 x s370fpib2. ;
  number = input(string,s370fpib2.);
  number1 = mod(number,16**3);
  number2 = (number - number1)/256;
  put (_all_) (=/);
  format string $hex4.;
run;
MarkATremel
Obsidian | Level 7

I added the encoding = 'any' option to the INFILE statement.  It did not seem to affect it one way or another.

I am continuing to try different INFORMATs.  I tried the one you referenced, s370fpib2. because, well because I am getting desperate here!  🙂  

I have also tried PIB2., IBR2. and IB2.

None of these INFORMATs fail (no warnings, no hard errors) but none of them produce characters when I write them out with a HEX4. FORMAT.

 

FWIW, regarding the solved vs unsolved issue, I do not see any button to unselect the answer.  My browser is Chrome.   I am new to the site though, so I expect I will figure that out at some point.

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 23 replies
  • 2814 views
  • 4 likes
  • 4 in conversation