C Null char in format statment

C Null char in format statment

Hopefully an easy question, consider the code snip:

        character(20) :: gbuf
        write(gbuf,1) flt,char(0)
        1 format(f10.3,A)

Given format 1 is used many times it would be neater if the null character termination was in the format rather than the write. I just spend a few minutes scratching my head and googleing.... Any ideas?

38 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Answering my own question.....     

        1 format(f10.3,'\0')

Seems to do the trick......

Quote:

app4619 wrote:

Answering my own question.....     

        1 format(f10.3,'\0')

Seems to do the trick......

I think the above may be a bug. However

1 format(f10.3, '\0'C)

would be correct.

as well as

1 format(f10.3, ''C)

(two ' characters)

Jim Dempsey

www.quickthreadprogramming.com

A good spot Jim but we are both wrong the '\0'  literally a \ and a 0 whilst if you make it a C string with the \0'C the complier throws its teddy out of the pram - error #6186: This character is not valid in a format list.   [C]

Any more ideas anyone????

 

 

You left us in the dark as to what the purpose of the null is, but here is my attempt:

character(7) :: fmt = '(f10.3)'
character(20) :: gbuf
real :: flt = 3.142
integer :: i
gbuf(11:11)=char(0)
write(gbuf(1:10),fmt)flt
write(*,'(20(1x,Z2))')(gbuf(i:i),i=1,len(gbuf))
end

 

What I was doing was putting numeric data into strings to throw at Windows sdk routines for dialog boxes. Generally these must be null terminated as the sdk is setup for C++. I was looking for a neater way rather than writing a char(0) or concatenating to the string  gchar(1:n)//char(0). My earlier post is wrong - see below

1 format(f10.3,'\0'C)
write(gchar,1) flt ! works
write(gchar,"(f10.3,'\0'C)") flt  !fails-error #6186: This character is not valid in a format list.[C]

 

As you may have noted, I placed the null into gbuf(11:11) and exposed only the substring gbuf(:10) to the Fortran I/O runtime; this was intended to keep the null out of reach of any format string checking code in the runtime.

Yes I noted that, for my purposes the 1 format.... style works fine and suits my purposes for being short and clear. 

thanks

 

Quote:

app4619 wrote:

A good spot Jim but we are both wrong the '\0'  literally a \ and a 0 whilst if you make it a C string with the \0'C the complier throws its teddy out of the pram - error #6186: This character is not valid in a format list.   [C]

Any more ideas anyone????

program NULL
  implicit none
  character(10) :: funk
  funk='abc\0def'
  print *,funk
  funk='abc\0def'C
  print *,funk
end program NULL

 abc\0def
 abc def

 

I see no teddy

Jim Dempsey

www.quickthreadprogramming.com

Curious:

program NULL
    implicit none
    character(10) :: funk
    funk='abc\0def'
    print *,funk
    funk='abc\0def'C
    print *,funk
    print 1,'abc', 'def'
1   format(a3,'\0',a3)
    print 2,'abc', 'def'
2   format(a3,'\0'C,a3)
end program NULL

 abc\0def
 abc def
abc\0def
abcdef

Space between in 2nd line, not in 4th line

Jim Dempsey

www.quickthreadprogramming.com

Quote:

jimdempseyatthecove wrote:

I see no teddy

Jim Dempsey

My post was misleading I corrected myself at #6

    program Console3
    implicit none
    integer           :: ival, l1
    character(len=8)  :: gbuf1, gbuf2
    1 format(i4,'\0'C)    
    2 format(i4,A)    
    do l1=1,len(gbuf1) ! fill strings with something
        gbuf1(l1:l1)='@'
        gbuf2(l1:l1)='@'
    enddo
    ival=1234
    write(gbuf1,1) ival
    write(gbuf2,2) ival,char(0)
    !now check the results
    open(1,file='test.txt',status='unknown')
    write(1,*) iachar('@')
    do l1=1,len(gbuf1)
      write(1,*) l1,iachar(gbuf1(l1:l1)),iachar(gbuf2(l1:l1)) 
    enddo
    !      64
    !       1          49          49
    !       2          50          50
    !       3          51          51
    !       4          52          52
    !       5          32           0 space char, null char
    !       6          32          32
    !       7          32          32
    !       8          32          32
    end program Console3

So the Gbuf2 version works and the Gbuf1 version fails. Is there a valid/working option for format 1? To me it is better to have the null char in the format rather then remembering at add a char(0) to the end of every buffer write in this instance.

Do you have to use a format statement?  Format specifications in character strings are much better, in that they can be built using constant expressions (that ultimately result in something of character type) and can be given a name that is a bit more descriptive.

Unfortunately, I think the compiler is being a bit overly critical of character edit descriptors that have NULL's in them (I think this is a compiler bug - because it is useful to do this occasionally - your use case for example.  But you can work around that.

PROGRAM FormatSpecWithNull
  IMPLICIT NONE
  ! You should be able to do this, but the compiler (erroneously, IMHO) 
  ! incorrectly complains.  Note the ACHAR putting a NULL in the 
  ! character string edit desscriptor.
  CHARACTER(*), PARAMETER :: fmt_spec_ = "(F10.3,'" // ACHAR(0) // "')"
  
  ! So we'll obsfuscate the format spec such that the compiler can't see 
  ! what we are doing at compile time.
  CHARACTER(:), ALLOCATABLE :: fmt_spec
  
  ! For testing.
  CHARACTER(11) :: string
  
  !*****************************************************************************
  
  fmt_spec = fmt_spec_
  
  ! Just for checking - note the NULL won't show properly.
  PRINT "('The format spec is ""',A,'""')", fmt_spec
  
  ! Define string so that we can see what it changes to.
  string = REPEAT('x', LEN(string))
  WRITE (string, fmt_spec) 9.0        ! 6661
  
  ! Is there a NULL where we expect?
  PRINT "('The character at position 11 has IACHAR of ',I0)",  &
      IACHAR(string(11:11))
  
END PROGRAM FormatSpecWithNull

 

No I do not need to have a format statement, I did some examples using formats built into strings but they all failed. Your sample is quite interesting, I think the key in your example is the allocate, to cheat the compile time behaviour.

This seems like a sledge hammer to crack a nut though, I am not sure that the Intel compiler is being very  friendly in this instance.

 

 

App4619,

Experimenting with your program a bit more yields

program NULL
  implicit none
  integer           :: ival, l1,ival2
  character(len=8)  :: gbuf1, gbuf2
1 format(i4,'A\0A'C,i1)    
2 format(i4,A)    
  do l1=1,len(gbuf1) ! fill strings with something
    gbuf1(l1:l1)='@'
    gbuf2(l1:l1)='@'
  enddo
  ival=1234
  ival2 = 5
  write(gbuf1,1) ival, ival2
  write(gbuf2,2) ival,char(0)
  !now check the results
  open(1,file='test.txt',status='unknown')
  write(1,*) iachar('@')
  do l1=1,len(gbuf1)
    write(1,*) l1,iachar(gbuf1(l1:l1)),iachar(gbuf2(l1:l1)) 
  enddo
end program NULL
          64
           1          49          49
           2          50          50
           3          51          51
           4          52          52
           5          65           0
           6          53          32
           7          32          32
           8          32          32

Note, that not only is the null omitted (2nd character of embedded text), but also the 3rd character of the embedded text of the format is omitted. It would seem that the code writer of the affected piece of code is using the string as if it were a C string. I guess one could argue that "you said it was a C string", and took that to interpret the string is terminated by non-inclusive NULL. Changing 1 format to..., yields

...
1 format(i4,'A'\\''C\\'A',i1)    
...
          64
           1          49          49
           2          50          50
           3          51          51
           4          52          52
           5          65           0
           6          65          32
           7          53          32
           8          32          32

In the above, the NULL is stripped (but 2nd A is present).

It mat be time for someone familiar with the standards with respect to the edit descriptors containing unusual characters, such as NULL, TAB, Escape Sequences, other...

Jim Dempsey

www.quickthreadprogramming.com

I don't recommend trying to force a NUL into a format string. Find another solution.

Steve - Intel Developer Support

I'm not sure if somewhere above is the suggestion to initialize your
format with CHAR(0), but that would appear to work only if you specify the
exact number of characters to write to, or else the unwritten-to storage
bytes finish up filled with codes for blank characters. It would appear much
simpler to bite the bullet and just concatenate the CHAR(0) when needed.

	CHARACTER(256) MYSTRING
        REAL*8 NUMBER
	INTEGER(4) I

	DO I=1,256
	MYSTRING(I:I)=CHAR(0)
	END DO

	NUMBER=1234.567D+89
	WRITE(MYSTRING(1:12),'(D12.3)') NUMBER

	PAUSE  ! at this point, MYSTRING still has CHAR(0) in all places except the first 12

	DO I=1,256
	MYSTRING(I:I)=CHAR(0)
	END DO

	NUMBER=1234.567D+89
	WRITE(MYSTRING,'(D12.3)') NUMBER

        PAUSE ! At this point, MYSTRING has code for blank (02) in all places after the first 12

 

 

 

@Anthony:

You are correct! The 2 methods I already use are //char(0) or to write char(0) to an A format as the last i/o item for an internal write. It just happens I am converting some window dialogs from IFLOGM to using the Windows sdk direct and there were many strings created with formatted writes of real data. Adding a terminator NUL to the format would seem a logical step i.e. the change in one place rather than many. The reality is that adding a NUL within a format is not practical and indeed the Ifort compiler does not seem to work in a consistent way IMO.

@Steve "I don't recommend trying to force a NUL into a format string. Find another solution."

Based on the results above it is clearly foolish to try!!! Other solutions do exist, however, looking at the range of examples from a number of contributors, is the compiler behaving correctly and/or in a useful manner in this respect?

 

 

Well, it should not be allowing a format item that looks like a C string. I'll file a bug report about that.

Steve - Intel Developer Support

I'd suggest creating a generic function call to the sdk library functions. One taking CHARACTER*(*) arguments, and the other taking C_PTR arguments. The C_PTR one need only have the attribute declarations to match the signature of the sdk library function.

The CHARACTER*(*) functions can make a copy, LENTRIM and append the null, then"call" the library function using the LOC of the temporary copy(s) of the arg(s).

Jim Dempsey

www.quickthreadprogramming.com

Quote:

Steve Lionel (Intel) wrote:

I don't recommend trying to force a NUL into a format string. Find another solution.

 

:(

Re: Anthony's post (#17):

This is exactly the behaviour that we would expect - writing to a string without specifying the length should blank pad any unused characters.  To leave them unchanged would cause other undesirable consequences.

After writing to the string, if the length is not known, why not use TRIM and concatenate CHAR(0)?

David

Quote:

David White wrote:

After writing to the string, if the length is not known, why not use TRIM and concatenate CHAR(0)?

 

Indeed that is the option but my point was that I fancied just changing the format (not happening :-( ! ) in one place rather than adding two operations in several places.

 

How about this:

module foo
contains
    function C_STR(F_STR) result (ret)
        character*(*) :: F_STR
        character(len=MIN(LEN_TRIM(F_STR)+1,LEN(F_STR))) :: ret
        integer :: I
        I = LEN_TRIM(F_STR)
        if(I .lt. LEN(F_STR)) then
            I = I + 1
        else
            write(*,*) "Insufficient room in string - truncating last character"
        endif
        
        F_STR(I:I) = CHAR(0)
        ret = F_STR
    end function C_STR

    integer(4) function Win32function(gbuf)
       character*(*) :: gbuf
        write(1,*) iachar('@')
        do l1=1,len(gbuf)
          write(1,*) l1,iachar(gbuf(l1:l1)) 
        enddo
        Win32function = 0
    end function Win32function
end module foo

program NULL
    use foo
    implicit none
    integer           :: ival, l1
    character(len=8)  :: gbuf
1   format(i4,A)    
    do l1=1,len(gbuf) ! fill string with something
        gbuf(l1:l1)='@'
    enddo
    ival=1234
    write(gbuf,1) ival
    !now check the results
    open(1,file='test.txt',status='unknown')
    ! without NULL
    if(Win32function(gbuf) .ne. 0) then
        write(*,*) 'Error'
    endif
    ! with NULL
    if(Win32function(C_STR(gbuf)) .ne. 0) then
        write(*,*) 'Error'
    endif
    close(1)
end program NULL

          64
           1          49
           2          50
           3          51
           4          52
           5          32
           6          32
           7          32
           8          32
          64
           1          49
           2          50
           3          51
           4          52
           5           0

Jim Dempsey

www.quickthreadprogramming.com

1
1 format(f10.3,'\0')

DikoYow

Quote:

Vidura D. wrote:
1 1 format(f10.3,'\0')

That doesn't work. '\0' is simply those two characters. This isn't C. While you can, at present, put '\0'C in a FORMAT statement, that's a bug that will be fixed.

Steve - Intel Developer Support

 

Im sorry could you please tell me the language in the problem. I havent used c in this way.

Ramith, what is your question?

Steve - Intel Developer Support

Now that this old thread is revived, note that a NUL character is ''C, not '\0'C -- the latter is two NULs. But the solution has been found:

program p
   implicit none
   character(20) gbuf
   character(11) fmt
   real flt
   integer i
   1 format(f10.3,1H1)
   fmt = '1234567890'//achar(0)
   read(fmt,1) flt
   flt = 1234.567
   write(gbuf,1) flt
   do i = 1, len(gbuf)
      write(*,'(i2,1x,a1,1x,i2)') i, merge(gbuf(i:i),'.', &
         iachar(gbuf(i:i))>=32),iachar(gbuf(i:i))
   end do
end program p

Output with ifort:

 1   32
 2   32
 3 1 49
 4 2 50
 5 3 51
 6 4 52
 7 . 46
 8 5 53
 9 6 54
10 7 55
11 .  0
12   32
13   32
14   32
15   32
16   32
17   32
18   32
19   32
20   32

 

My above solution lacked elegance in that it required the user to painstakingly adjust the string to be read into the hollerith descriptor whenever he changed the format. This can be fixed:

program p
   implicit none
   character(20) gbuf
   character(80) fmt
   real flt
   integer i
   1 format(f10.3,1H~)
   flt = 1.0
   write(fmt,1) flt
   forall(i=1:len(fmt),fmt(i:i)=='~') fmt(i:i) = achar(0)
   read(fmt,1) flt
   flt = 1234.567
   write(gbuf,1) flt
   do i = 1, len(gbuf)
      write(*,'(i2,1x,a1,1x,i2)') i, merge(gbuf(i:i),'.', &
         iachar(gbuf(i:i))>=32),iachar(gbuf(i:i))
   end do
end program p

Same output as before, but the Fortran processor is now automatically taking care of replacing any infrequently used character, such as '~', with ASCII NUL in the format.

Well, the solution of reading into a hollerith edit descriptor doesn't work in gfortran, which seems to me to be a lack of understanding of hollerith edit descriptors by the gfortran developers. It turns out that in a format statement like 1 FORMAT(1H#) gfortran allows the programmer to use any character where instead of the # except for 0,10,13, and 26. I guess 0 and 10 throw parsers written in C for a loop, and 13 being carriage return also could interfere with parsing, and 26 is DOS end of file. Similar for ifort, except the list is 0, 3, 10, and 26. I don't know why 3 should be such a problem.

Both gfortran and ifort have no problem with format strings such as character(*), parameter :: fmt0 = '(1H'//achar(0)//')' the hollerith edit descriptor works in both compilers for all 256 characters, unlike "('"//achar(0)//"')" so it looks like inserting the objectionable character by concatenation into the hollerith edit descriptor of a format string is the easiest way to do it.

 

You can insert the NUL character that way but the results may be unpredictable. If you need to write a NUL, put it in the I/O list and transfer using an A format.

Steve - Intel Developer Support

The point of the thread was to somehow avoid having to append the ASCII NUL by hand every time and have the FORMAT do it. Of course, the ifort docs (in the old days ifort put the documentation somewhere under the Start menu, but now I have to search for it in my installation. Did I miss something in the install phase?) state that the string following the H edit descriptor consists of printable ASCII characters, so I am relying on undocumented behavior. Reading the page on H editing, one might almost think that this program:

program p
   implicit none
   character(5) fmt
   character(5) data
   fmt = '(1H1)'
   write(data,fmt)
   data(1:1) = '2'
   read(data,fmt)
   write(*,fmt)
 1 format(1H1)
   write(data,1)
   data(1:1) = '2'
   read(data,1)
   write(*,1)
end program p

would print '2' on both lines, but it prints '1' on the first line and '2' on the second. Possibly the '2' character is written into some address on line 8 of the program, but the address must then be a temporary address and not a pointer into the fmt variable.

 

The documentation is still under the Start menu (Intel Parallel Studio XE 2015 > Documentation), or you can get it through Help in Visual Studio.

RO, you shouldn't be encouraging bad habits...  Reading into an H format is FORTRAN 66 and is nonstandard from F77 onward. In your example, the first READ modifies the internal compiled version of the format derived from fmt, but not into the variable fmt itself. As you note, it is thrown away afterward. We do still support reading into an H format in a FORMAT, as was allowed in F66, as the second case illustrates.

There is no standard-conforming way to embed a NUL (or any other nonprinting character) in a format.

Steve - Intel Developer Support

Under Start > Intel Parallel Studio XE 2015 all I've got is 'Intel Premier Support', 'Intel Software Manager', 'Compiler and Performance Libraries', and 'Visual Studio Integrations'. No 'Documentation'. How can I fix this situation? If the documentation noted that on input, the characters are read into the H edit descriptor if it appeared in a FORMAT statement, and are simply skipped over otherwise, it may more effectively inform the user on what should be expected with my example from quote #33.

I hope you anticipate a certain amount of giggling from the back of the classroom when you admonish a user who calls himself 'Repeat Offender' not to encourage bad habits :)

 

Interesting. Documentation should be there. And under that would be links to the various component documentation files, including "C:\Program Files (x86)\Intel\Composer XE 2015\Documentation\en_US\compiler_f\index.htm"  You could create a shortcut on your desktop with this if nothing else. I will ask the writers to add "when it appears in a FORMAT statement" to that text.

And yes, I definitely had my tongue in cheek when I wrote that.

Steve - Intel Developer Support

RO thanks for the interest in looking for a solution. I long back concluded that there was no working/legal solution and just adopted the working (but more tedious) solution I was trying to shortcut in the original post.

I have to say I did not consider Holleriths, however H editing disappeared from my repertoire many years back, it surely must have been deprecated 20+ years ago??

 

Reading into an H edit descriptor disappeared in F77. The H edit descriptor itself was deprecated in F90.

Steve - Intel Developer Support

Leave a Comment

Please sign in to add a comment. Not a member? Join today