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?
連結已複製
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
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
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.
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.
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.
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.
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??
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
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.