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
榮譽貢獻者 II
2,975 檢視

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 積分
44 回應
DavidWhite
傑出貢獻者 II
839 檢視

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

andrew_4619
榮譽貢獻者 II
839 檢視

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.

 

jimdempseyatthecove
榮譽貢獻者 III
839 檢視

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

Vidura_Dhananjaya
新貢獻者 II
839 檢視
1 format(f10.3,'\0')
Steven_L_Intel1
839 檢視

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.

Ramith_H_
初學者
839 檢視

 

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

Steven_L_Intel1
839 檢視

Ramith, what is your question?

JVanB
傑出貢獻者 II
839 檢視

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

 

JVanB
傑出貢獻者 II
839 檢視

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.

JVanB
傑出貢獻者 II
839 檢視

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.

 

Steven_L_Intel1
839 檢視

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.

JVanB
傑出貢獻者 II
839 檢視

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.

 

Steven_L_Intel1
839 檢視

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.

JVanB
傑出貢獻者 II
839 檢視

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

 

Steven_L_Intel1
839 檢視

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.

andrew_4619
榮譽貢獻者 II
839 檢視

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

 

Steven_L_Intel1
839 檢視

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

Steven_L_Intel1
839 檢視

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

andrew_4619
榮譽貢獻者 II
839 檢視

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? 

Steven_L_Intel1
814 檢視

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

 

mecej4
榮譽貢獻者 III
814 檢視

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.

回覆