Form character*(*) for character variable generation - fortran

I have a third party script for a subroutine that I need to work with. This subroutine is as follows
Subroutine COpen(io, Name )
Character*(*) Name
Character*1023 NameIn, NameOut
NameIn = Trim(Name)//' '
Call Get_OrMakeFileName( NameIn, NameOut )
Open(io,file=NameOut,access="APPEND")
End
I don't understand the Character*(*) name syntax. Isn't the typical way to declare string variables simply. character :: name*4 with the *4 part designating the number of characters? Can anyone please explain the purpose of this alternate syntax? What kind of object does it generate?

In short: character*(*) declares a character variable of assumed length.
There are a number of ways of declaring the length of a character variable. One, as seen in the question's code, is
character*1023 ...
where a literal constant follows the *. Equivalent to that is
character([len=]1023) ...
(len= being optional). In this case the length needn't be a literal constant.
These two forms declare a variable of a particular length. There are two other forms of a length for a character variable:
assumed length - character([len=]*) ... ;
deferred length - character([len=]:) ....
Like with character*1023 the assumed and deferred length declarations may be written in this style:
character*(*) ... ! Assumed length
character*(:) ... ! Deferred length
character*(1023) ... ! For completeness
Well, what does "assumed length" mean?
For a dummy argument such as Name it's length is taken from the length of the actual argument of the procedure. With character :: Name*4 the argument is of length 4, regardless of the length of the argument to the subroutine (as long as it's of length at least 4). When the dummy is of assumed length it is of length 12 if the argument is of length 12, and so on.
Although not in the question, a character named constant may also assume its length from the defining expression:
character*(*), parameter :: label='This long'
Deferred length is left to other questions.

Related

Get length of HDF5 filename in Fortran?

I'm trying to get the filename of an HDF5 object in Fortran, for which I do not know the length of the filename in advance. The HDF5 documentation says this can be accomplished by passing NULL to h5fget_name:
If the length of the name, which determines the required value of size, is unknown, a preliminary H5Fget_name call can be made by setting name to NULL.
However, it is unclear how to accomplish this from Fortran code. Per the above language in the documentation, something like this should work:
function get_hdf5_filename(obj_id) result(filename)
use hdf5
use iso_c_binding, ONLY: c_null_ptr
! Arguments
integer(HID_T), intent(in)::obj_id
!! Handle of HDF5 object
! Result
character(:), allocatable::filename
integer(SIZE_T)::size = 0
!! Length of filename
integer::hdferr
!! HDF5 error code
call h5fget_name_f(obj_id, c_null_ptr, size, hdferr)
allocate (character(size)::filename)
call h5fget_name_f(obj_id, filename, size, hdferr)
end function get_hdf5_filename
However, gfortran won't compile the above and gives a type mismatch error:
Error: Type mismatch in argument 'buf' at (1); passed TYPE(c_ptr) to CHARACTER(1)
A null can be passed by declaring the filename variable as a pointer and nullifying it before the initial call to h5fget_name_f:
function get_hdf5_filename(obj_id) result(filename)
use hdf5
use iso_c_binding, ONLY: c_null_ptr
! Arguments
integer(HID_T), intent(in)::obj_id
!! Handle of HDF5 object
! Result
character(:), pointer::filename
integer(SIZE_T)::size = 50
!! Length of filename
integer::hdferr
!! HDF5 error code
nullify(filename)
call h5fget_name_f(obj_id, filename, size, hdferr)
! HDF5 needs one more character (probably for the null character terminating the string), so we allocate filename to length size+1
allocate (character(size+1)::filename)
call h5fget_name_f(obj_id, filename, size, hdferr)
! Remove last character from the returned string
filename => filename(1:size)
end function get_hdf5_filename
Note that h5fget_name_f needs an extra character in the passed string (probably for the terminating null character), so the name pointer has to be allocated one character longer than the value set in the size argument. The terminating null can be removed by redirecting the file pointer to a subset that excludes the last character using array slicing syntax (filename => filename(1:size) in the above example).
A possible solution is to create a C function that returns the filename length for a given HDF5 object:
#include "hdf5.h"
#include "H5f90i.h"
int_f get_hdf5_filename_length(hid_t_f *obj_id){
return H5Fget_name((hid_t)*obj_id, NULL, 0);
}
which can be called from Fortran in the following way:
function get_hdf5_filename(obj_id) result(filename)
use hdf5
use iso_c_binding, ONLY: c_null_ptr
interface
function get_hdf5_filename_length(obj_id) bind(c) result(length)
use hdf5
integer(HID_T)::obj_id
integer(SIZE_T)::length
end function get_hdf5_filename_length
end interface
! Arguments
integer(HID_T), intent(in)::obj_id
!! Handle of HDF5 object
! Result
character(:), pointer::filename
integer(SIZE_T)::size = 50
!! Length of filename
integer::hdferr
!! HDF5 error code
integer::i
!! Loop counter
size = get_hdf5_filename_length(obj_id)
! filename has to be allocated to size+1 to allow for the terminating null
! of the filename string in C
allocate (character(size+1)::filename)
! h5fget_name_f uses len_trim to determine the buffer length,
! which requires the buffer be filled with non-whitespace characters
! in order to work correctly
do i = 1, size + 1
filename(i:i) = 'a'
end do
! Get the filename
call h5fget_name_f(obj_id, filename, size, hdferr)
! Remove the null character from the end of the string
filename => filename(1:size)
end function get_hdf5_filename
Note that it is not enough to allocate the string buffer to the correct size; it also needs to be populated with non-whitespace characters before being passed to h5fget_name_f, because h5fget_name_f calls len_trim on the passed buffer and uses the result to determine the maximum permissible filename length.

index function returning many values [duplicate]

This question already has an answer here:
Difference between "character*10 :: a" and "character :: a(10)"
(1 answer)
Closed 3 years ago.
I am trying to find the position of a character in a string using the following code.
implicit none
character string(10);
string="abcdefghik"
print *, index(string, "c")
end program test
But the output is 10 times 0 instead of 3. The same happens with the scan function.
What am I doing wrong?
The bug:
You defined your string as an array of single characters of length 10:
character :: string(10)
A character string of length 10 is defined as
character(len=10) :: string
These two are two different concepts. See:
What is the difference between a Char Array and a String?
Difference between "character*10 :: a" and "character :: a(10)"
Why do I get 10 zeros?
The reason you obtained 10 times the value zero is because both INDEX and SCAN are elemental functions. This means that they execute for every element of the array. But this is not all, you would imagine that it would at least return ones the number 1 for the third array ellement. Unfortuntately, the assignment
character array(10)
array = "abcdefghik"
print *, array
will assign 10 times the letter a. You notice that this will return aaaaaaaaaa. This is due to array assignments.
Working code:
program test
implicit none
character(len=10) :: string;
string="abcdefghik"
print *, index(string, "c")
end program test

Differences with CFscript calling value methods

I'm working with ColdFusion and CFScript. At the moment I've no problems, but noticed that I can call values in 3 ways:
Value
'Value'
'#Value#'
What are the differences between them? Thanks in advance!
Value
CF searches for a variable called Value (case insensitive) starting with the VARIABLES scope and then progressing through other scopes (like URL and FORM), stopping at the first variable found.
'Value'
A literal string with the characters V, a, l, u and e.
'#Value#'
A string where Value will be evaluated (CF evalautes stuff between #). If the variable Value (case insensitive) is a so called simple value, the variable will be cast to a string. Otherwise, an exception is thrown since non-simple (ie complex ) values are not automatically cast as strings. This is basically equivalent to '' & Value & '' (string concatenation).
Value = 'Hello World !!';
writeOutput(Value);
>> Hello World !!
writeOutput('Value');
>> Value
writeOutput('#Value#');
>> Hello World !!
writeOutput( evaluate('Value') );
>> Hello World !!

Replace comma between quotes in CSV with Regex

We have for example a string like this:
"COURSE",247,"28/4/2016 12:53 Europe/Brussels",1,"Verschil tussen merk, product en leveranciersverantwoordelijke NL","Active Enro"
The Goal is to replace the comma between "merk, product" and to keep the comma like "," and ", & ," so we can split the file correctly.
Any suggestions?
Kind regards
First of all, you should check Understanding CSV files and their handling in ABAP article.
For a one-time job, you can use this regex (but note that with longer strings, it may not work well, use it as a means of last resort):
,(?!(?:[^"]*"[^"]*")*[^"]*$)
See the regex demo
Pattern details:
, - a comma that...
(?! - is not followed with....
(?: -
[^"]* - zero or more chars other than "
" - a double quote
[^"]*" - see above
)* - zero or more sequences of the above grouped patterns
[^"]* - zero or more chars other than "
$ - end of string
) - end of negative lookahead
I've found a better solution than Regex, by using the class CL_RSDA_CSV_CONVERTER. No need of reinventing the wheel.
See code below:
TYPES: BEGIN OF ttab,
rec(1000) TYPE c,
END OF ttab.
TYPES: BEGIN OF tdat,
userid(100) TYPE c,
activeuser(100) TYPE c,
firstname(100) TYPE c,
lastname(100) TYPE c,
middlename(100) TYPE c,
supervisor(100) TYPE c,
supervisor_firstname(100) TYPE c,
supervisor_lastname(100) TYPE c,
supervisor_middle(100) TYPE c,
scheduled_offering_id(100) TYPE c,
description(100) TYPE c,
domain(100) TYPE c,
registration(100) TYPE c,
current_registration(100) TYPE c,
max_registration(100) TYPE c,
item_type(100) TYPE c,
item_id(100) TYPE c,
item_revision_date(100) TYPE c,
revision_number(100) TYPE c,
title(100) TYPE c,
status(100) TYPE c,
start_date(100) TYPE c,
end_date(100) TYPE c,
location(100) TYPE c,
instructor_fistname(100) TYPE c,
instructor_lastname(100) TYPE c,
instructor_middlename(100) TYPE c,
column_number(100) TYPE c,
label(100) TYPE c,
value(100) TYPE c,
description2(100) TYPE c,
start_date_short(100) TYPE c,
begda TYPE begda,
start_time(100) TYPE c,
start_time_24_hour(100) TYPE c,
start_12_hour_type(100) TYPE c,
start_timezone(100) TYPE c,
end_date_short(100) TYPE c,
endda TYPE endda,
end_time(100) TYPE c,
end_time_24_hour(100) TYPE c,
end_12_hour_type(100) TYPE c,
end_timezone(100) TYPE c,
pernr TYPE pernr_d,
END OF tdat.
CONSTANTS: co_delete TYPE pspar-actio VALUE 'DEL',
co_attendance TYPE string VALUE '2002',
co_att_prelp TYPE prelp-infty VALUE '2002',
co_att_subty TYPE string VALUE '3000'.
DATA:
itab TYPE TABLE OF ttab WITH HEADER LINE,
idat TYPE TABLE OF tdat WITH HEADER LINE,
lw_idat LIKE LINE OF idat,
lw_found_training LIKE LINE OF idat,
file_str TYPE string,
lv_uname TYPE syuname,
lo_person TYPE REF TO zhr_cl_pa_person,
lv_input_time TYPE tims,
lv_output_time TYPE tims,
lv_day(2) TYPE c,
lv_month(2) TYPE c,
lv_year(4) TYPE c,
lv_time(6) TYPE c,
lv_abap_date TYPE string,
lv_lock_return LIKE bapireturn1,
ls_attendance LIKE bapihrabsatt_in,
lt_attendance_output TYPE TABLE OF bapiret2,
ls_return LIKE bapireturn,
ls_return1 LIKE bapireturn1,
lt_absatt_data TYPE TABLE OF pprop,
lw_absatt_data LIKE LINE OF lt_absatt_data,
lt_pa2002 TYPE TABLE OF pa2002,
lw_pa2002 LIKE LINE OF lt_pa2002,
lw_msg TYPE bapireturn1,
lt_p2002 TYPE TABLE OF p2002,
lw_p2002 LIKE LINE OF lt_p2002,
lc_pgmid TYPE old_prog VALUE 'ZKA_TEXT_UPDATE',
lr_upd_cluster TYPE REF TO cl_hrpa_text_cluster,
ls_text TYPE hrpad_text,
ls_pskey TYPE pskey,
lt_text_194 TYPE hrpad_text_tab,
lv_text TYPE string,
lo_ref TYPE REF TO cx_hrpa_invalid_parameter,
lw_struct TYPE tdat,
lo_csv TYPE REF TO cl_rsda_csv_converter.
CALL METHOD cl_rsda_csv_converter=>create
RECEIVING
r_r_conv = lo_csv.
CREATE OBJECT lr_upd_cluster.
*--------------------------------------------------*
* selection screen design
*-------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK selection1 WITH FRAME.
PARAMETERS: p_file TYPE localfile.
SELECTION-SCREEN SKIP.
SELECTION-SCREEN BEGIN OF LINE.
SELECTION-SCREEN COMMENT 4(51) text-002.
PARAMETERS p_futatt AS CHECKBOX DEFAULT 'X'.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN BEGIN OF LINE.
SELECTION-SCREEN COMMENT 4(51) text-001.
PARAMETERS p_active AS CHECKBOX DEFAULT 'X'.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN END OF BLOCK selection1.
*--------------------------------------------------*
* at selection screen for field
*-------------------------------------------------*
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
CALL FUNCTION 'KD_GET_FILENAME_ON_F4'
EXPORTING
static = 'X'
CHANGING
file_name = p_file.
*--------------------------------------------------*
* start of selection
*-------------------------------------------------*
START-OF-SELECTION.
file_str = p_file.
CALL FUNCTION 'GUI_UPLOAD'
EXPORTING
filename = file_str
TABLES
data_tab = itab
EXCEPTIONS
file_open_error = 1
file_read_error = 2
no_batch = 3
gui_refuse_filetransfer = 4
invalid_type = 5
no_authority = 6
unknown_error = 7
bad_data_format = 8
header_not_allowed = 9
separator_not_allowed = 10
header_too_long = 11
unknown_dp_error = 12
access_denied = 13
dp_out_of_memory = 14
disk_full = 15
dp_timeout = 16
OTHERS = 17.
*--------------------------------------------------------------------*
* Delete file headers
*--------------------------------------------------------------------*
DELETE itab INDEX 1.
*--------------------------------------------------*
* process and display output
*-------------------------------------------------*
LOOP AT itab .
CLEAR idat.
CALL METHOD lo_csv->csv_to_structure
EXPORTING
i_data = itab-rec
IMPORTING
e_s_data = lw_struct.
MOVE-CORRESPONDING lw_struct TO idat.
APPEND idat.
ENDLOOP.
Read the file using a CSV reader.
Replace the commas in each field.
Write the file using a CSV writer.
You don’t need regular expressions for this task.

FORTRAN function returning an array causes a segfault (calling from C++)

Basically, here's my problem. I'm calling someone else's FORTRAN functions from my C++ code, and it's giving me headaches. Some code:
function c_error_message()
character(len = 255) :: c_error_message
errmsg(1:9) = 'ERROR MSG'
return
end
That's the FORTRAN function. My first question is: Is there anything in there that would cause a segfault?
If not, then second: What does that return? A pointer?
I'm trying to call it with the following C statement:
char *e = c_error_message_();
That causes a segfault.
c_error_message();
That too causes a segfault.
I declared c_error_message_() earlier on with the following code:
extern"C" {
char* c_error_message_();
}
Would declaring a function with a different return type than the actual return type cause a segfault?
I'm at a loss. Thanks for any replies.
Here is a method that works. When I tried to use the ISO C Binding with a function returning a string, the compiler objected. So if instead you use a subroutine argument:
subroutine fort_err_message (c_error_message) bind (C, name="fort_err_message")
use iso_c_binding, only: C_CHAR, C_NULL_CHAR
character (len=1, kind=C_CHAR), dimension (255), intent (out) :: c_error_message
character (len=255, kind=C_CHAR) :: string
integer :: i
string = 'ERROR MSG' // C_NULL_CHAR
do i=1, 255
c_error_message (i) = string (i:i)
end do
return
end subroutine fort_err_message
The Fortran is a bit awkward because technically a C-string is an 1D array of characters.
And example C code to demo that this works:
#include <stdio.h>
void fort_err_message ( char chars [255] );
int main ( void ) {
char chars [255];
fort_err_message ( chars );
printf ( "%s\n", chars );
return 0;
}
Since the second line declares a name that is the same as the function name, it is declaring the type of the function return, namely a scaler character string of 255 characters. But in theory this doesn't tell us the internal API -- that is up the the compiler. I don't know where "errmsg" comes from -- it must be declared elsewhere -- perhaps a global variable as suggested by Michael Anderson. Or maybe this is a mistake and the line should be c_error_message = "ERROR MSG". (There is no need to designate the sub-string range -- the rest of the string will be filled with blanks.) IMO, the best approach to calling Fortran from C (or vice-a-versa) is to use the ISO C Binding, which will cause the Fortran compiler to use a C compatible Interface. I haven't done a function returning a string, but have done strings as arguments.
FORTRAN functions return the value assigned to the function name. The type returned is specified by the function definition, and in this case, it is returning an character string 255 characters long. I think the type mismatch is why you are segfaulting. So what C type should you use? I don't know.
I found this Using C with FORTRAN page, and the author strongly recommends using FORTRAN subtroutines and calling them as C functions returning type void.
Here is an F95 quick reference.