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

C Null char in format statment

andrew_4619
Honored Contributor II
2,764 Views

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?

0 Kudos
44 Replies
DavidWhite
Valued Contributor II
769 Views

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

0 Kudos
andrew_4619
Honored Contributor II
769 Views

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.

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
769 Views

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

0 Kudos
Vidura_Dhananjaya
New Contributor II
769 Views
1 format(f10.3,'\0')
0 Kudos
Steven_L_Intel1
Employee
769 Views

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.

0 Kudos
Ramith_H_
Beginner
769 Views

 

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

0 Kudos
Steven_L_Intel1
Employee
769 Views

Ramith, what is your question?

0 Kudos
JVanB
Valued Contributor II
769 Views

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

 

0 Kudos
JVanB
Valued Contributor II
769 Views

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.

0 Kudos
JVanB
Valued Contributor II
769 Views

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.

 

0 Kudos
Steven_L_Intel1
Employee
769 Views

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.

0 Kudos
JVanB
Valued Contributor II
769 Views

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.

 

0 Kudos
Steven_L_Intel1
Employee
769 Views

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.

0 Kudos
JVanB
Valued Contributor II
769 Views

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 :)

 

0 Kudos
Steven_L_Intel1
Employee
769 Views

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.

0 Kudos
andrew_4619
Honored Contributor II
769 Views

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??

 

0 Kudos
Steven_L_Intel1
Employee
769 Views

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

0 Kudos
Steven_L_Intel1
Employee
769 Views

C strings in a FORMAT will be disallowed in a future major release of the compiler.

0 Kudos
andrew_4619
Honored Contributor II
769 Views

From purely academic interest what will happen where the non-printing char is embedded in a string used as a format? Will that not always been detected or will it be a run-time issue? 

0 Kudos
Steven_L_Intel1
Employee
744 Views

If the format is a variable that has to be interpreted at run-time, you should get a run-time error when the invalid character is encountered. The problem here had to do with syntax, not an invalid character. I just did a test where I embedded a NUL in a character constant in a run-time format, and that was fine.

character (80) f
f = "('ABC','"//CHAR(0)//"','DEF')"
print  f
end

 

0 Kudos
mecej4
Honored Contributor III
744 Views

Steve, the following modification of your snippet brings out a discrepancy:

character (80) f
f = "('ABC','"//CHAR(0)//"','DEF')"
print  f
print "('ABC','"//CHAR(0)//"','DEF')"
end

The second line of output shows ABCDEF (the NUL is missing). I have not checked what the standard requires in this case.

0 Kudos
Reply