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

Initialize character constants using hex bytes

Adrian_F_1
Débutant
3 005 Visites

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 Compliments
14 Réponses
Adrian_F_1
Débutant
3 005 Visites

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 Compliments
andrew_4619
Contributeur émérite III
3 005 Visites

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 Compliments
Adrian_F_1
Débutant
3 005 Visites

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 Compliments
andrew_4619
Contributeur émérite III
3 005 Visites

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 Compliments
Steven_L_Intel1
Employé
3 005 Visites

I would do it like this:

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

 

0 Compliments
Adrian_F_1
Débutant
3 005 Visites

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 Compliments
JVanB
Précieux contributeur II
3 005 Visites

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 Compliments
JVanB
Précieux contributeur II
3 005 Visites

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 Compliments
IanH
Contributeur émérite III
3 005 Visites

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 Compliments
mecej4
Contributeur émérite III
3 005 Visites

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 Compliments
Steven_L_Intel1
Employé
3 005 Visites

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 Compliments
JVanB
Précieux contributeur II
3 005 Visites

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 Compliments
Steven_L_Intel1
Employé
3 005 Visites

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 Compliments
andrew_4619
Contributeur émérite III
3 005 Visites

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 Compliments
Répondre