Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28446 Discussions

Initialize character constants using hex bytes

Adrian_F_1
Beginner
1,100 Views

Is it possible to initialize a character array using hex values for the bytes?

using character(10) :: ch = 

ie. it may contain non printable bytes such as 0x0D, etc

0 Kudos
14 Replies
Adrian_F_1
Beginner
1,100 Views

ie I'm trying to initialize all the bytes of a long character string using a DATA statement or in the declaration of the character variable.

something like:

      character(10) :: ch
      data ch(1:10) / Z'41', Z'42', Z'4A', Z'45', Z'47', Z'4F', Z'0D', Z'42', Z'42', Z'45' /

which obviously doesn't work.

0 Kudos
andrew_4619
Honored Contributor II
1,100 Views

A little tedious but you can have something like:

    character(len=3), parameter :: gch=achar(int(Z'41'))//achar(int(Z'42'))//achar(int(Z'43'))

The Fortran standard is quite restrictive  on BOZ literal constants and you can only use for integer assignment. The above is standard conforming Fortran 2008 (/stand:f08)

0 Kudos
Adrian_F_1
Beginner
1,100 Views

Thanks, this also works:

      data ch(1:1) / Z'91'/, ch(2:2) / Z'd5'/, ch(3:3) /Z'12'/, ch(4:4) /Z'00'/

But looks a bit messy as well.

0 Kudos
andrew_4619
Honored Contributor II
1,100 Views

warning #6473: Fortran 2008 does not allow boz constant in this context.   [CH]

 warning #6953: Fortran 2008 does not allow  an 8-bit integer ASCII constant to be stored in a CHARACTER constant.   [CH]

 

Your example will work in ifort  but is not standard fortran

0 Kudos
Steven_L_Intel1
Employee
1,100 Views

I would do it like this:

character(10) :: ch = 'ABJEGO' // CHAR(13) // 'BBE'

 

0 Kudos
Adrian_F_1
Beginner
1,100 Views

No that would not work for me.  I have a 64 byte character string which I want to initialize to a defined set of 64 bytes, which can be anything from 00 to FF.  This is what I'm doing now:

      DATA KEY1  (01:01) /Z'48'/, KEY1  (02:02) /Z'35'/, KEY1  (03:03) /Z'3e'/, KEY1  (04:04) /Z'44'/, &
           KEY1  (05:05) /Z'2f'/, KEY1  (06:06) /Z'08'/, KEY1  (07:07) /Z'4d'/, KEY1  (08:08) /Z'6b'/, &

etc.  As I mentioned something like this would be better:

      character(10) :: ch
      data ch(1:10) / Z'41', Z'42', Z'4A', Z'45', Z'47', Z'4F', Z'0D', Z'42', Z'42', Z'45' /

Adrian

0 Kudos
JVanB
Valued Contributor II
1,100 Views

I had success with this:

program P
   implicit none
   character(8) :: key1 = transfer(char(int([ &
       Z'48',Z'35',Z'3E',Z'44',Z'2F',Z'08',Z'4d',Z'6B' &
       ])),repeat('A',8))
   integer i
   write(*,'(*(Z0.2:1x))') (key1(i:i),i=1,len(key1))
end program P

 

0 Kudos
JVanB
Valued Contributor II
1,100 Views

Actually it's a bit of a bug in both ifort and gfortran that they accept the syntax in Quote #8 without warning with standards checking enforced. To see what I mean, what do you think the output of this should be?

write(*,*) real([Z'3F800000',Z'40000000',Z'40400000',Z'40800000'])
end

 

0 Kudos
IanH
Honored Contributor II
1,100 Views

I feel bad for some reason.

  INTEGER, PARAMETER :: char_values(8) = [  &
      INT(Z'48'), INT(Z'35'), INT(Z'3E'), INT(Z'44'),  &
      INT(Z'2F'), INT(Z'08'), INT(Z'4D'), INT(Z'6B') ]
  CHARACTER :: dummy(SIZE(char_values)) = ACHAR(char_values)
  CHARACTER(SIZE(char_values)) :: ch
  EQUIVALENCE(dummy, ch)
  
  INTEGER :: i
  PRINT "(*(Z2.2,:,','))", (IACHAR(ch(i:i)), i=1, LEN(ch))

END

A `JOIN` intrinsic (or whatever) to convert a character array into a scalar would be nice.

0 Kudos
mecej4
Honored Contributor III
1,100 Views

Repeat Offender wrote:

...To see what I mean, what do you think the output of this should be?

write(*,*) real([Z'3F800000',Z'40000000',Z'40400000',Z'40800000'])
end

The output would not be as interesting as "1, 2, 3, 4". To obtain that, you would have to use an intrinsic that is part of the signature of a well-known personality of C.L.F.: TRANSFER() instead of REAL().

0 Kudos
Steven_L_Intel1
Employee
1,100 Views

RO, thanks for the comment about the standards warning - I've passed that on to the developers as issue DPD200359636. As you point out, that line isn't the same as:

write(*,*) [real(Z'3F800000'),real(Z'40000000'),real(Z'40400000'),real(Z'40800000')]

0 Kudos
JVanB
Valued Contributor II
1,100 Views

TRANSFER has a problem in that what 'loose' BOZ means varies by compiler. Thus replacing REAL with TRANSFER is OK on ifort, but in gfortran you get a size-16 array. You would need TRANSFER(INT or even TRANSFER(REAL to provide the appropriate bit size context for TRANSFER to be effective, but that would make TRANSFER redundant.

JOIN would never get off the ground because there is a question of whether you want it to work like MIN and MAX or like SUM, PRODUCT, ANY, ALL, PARITY, IANY, IALL, IPARITY, MINVAL, MAXVAL, MINLOC, and MAXLOC.

But I can't see why CHAR and ACHAR can't directly take BOZ the way INT, REAL, CMPLX, and DBLE do. After all, ASCII is generally presented in hex form.

 

0 Kudos
Steven_L_Intel1
Employee
1,100 Views

I tend to agree - CHAR and ACHAR should be treated the same as INT for this purpose. I have made a note to suggest this for a future standard.

0 Kudos
andrew_4619
Honored Contributor II
1,100 Views

Steve Lionel (Intel) wrote:

I tend to agree - CHAR and ACHAR should be treated the same as INT for this purpose. I have made a note to suggest this for a future standard.

That would be nice, I have had to resort to the shenanigans at #3 to be conforming which is rather tedious and unnecessary...

0 Kudos
Reply