fortran write base64 string of binary to file - fortran

How can I write to a binary file a base64 string representation of data?
program main
implicit none
real :: var1, var2, var3, var4
character(len=:), allocatable :: var
var1 = 1.23
var2 = 2.35
! var1, var2 written to file, should output "1.23, 2.35"
var = 'CAAAAKRwnT9mZhZACAAAAAgAAABSuA5AMzOLQAgAAAA='
open(53, status='scratch', access='stream', action='readwrite', form='unformatted')
write(53) var1, var2 ! write some data
write(53) var1+1., var2+2. ! write some more data
write(53) var ! write the base64 string, I want this line to be the same numbers as the first line I wrote
rewind(53) ! reset for reading each line we wrote
read(53) var3, var4 ! read first line
print *, var3, var4
read(53) var3, var4 ! read second line
print *, var3, var4
read(53) var3, var4 ! read third line, reads as if this was a string representation
print *, var3, var4
close(53)
end program main
outputs:
1.23000002 2.34999990
2.23000002 4.34999990
12.0784330 4.26526825E+33
I know there is a missing step of converting base64 to a binary form, as the file writes binary data of a string rather than the data it represents, how do I go about converting to binary when writing?

Converting binary data to and from base64 is not such a trivial task. The most reasonable thing you can do is to use one available library.
For Fortran I found only one option,
https://github.com/szaghi/BeFoR64
You can also interface to C libraries, there are several ones, here is for example one in the public domain:
https://github.com/zhicheng/base64

Related

SAS: Issue with importing text parameter file using input statement

I'm importing a parameter file in txt form with no title row such as the following:
byvars TEST16X GIO
log_transform N
y_intercept Y
exclude_outliers N
exclude_einmos N
Because it is a parameter file, the length of the two columns will not be fixed. The following is the problematic code I created to import the txt file. The two columns are concatenated instead of splitting into individual columns:
data test1;
infile "files/parameters.txt" DELIMITER='09'x col=Colpoint
length=linelen;
length pname $30 pvalue $10;
input #1 pname $ #;
varlen=linelen - colpoint + 1;
input pvalue $varying1024. varlen;
call symputx('pname', STRIP(pvalue));
run;
Output:
This parameter file defines global macro variables and their values. Such that log_transform is a macro variable with value 'N'.
You seem to be working way too hard. Just use TRUNCOVER and formatted input for the PVALUE field. Use list mode input for the parameter name field.
data parameters;
infile "files/parameters.txt" truncover ;
input pname :$32. pvalue $200. ;
call symputx(pname,pvalue);
run;

How to add commas to a list of variables in SAS

I am new to SAS so please forgive my ignorance if this question seems simple (in other languages it is a rather trivial task).
How can I convert a list of variables within a %Macro:
%Let var_list = var1 var2 var3
Into this
"var1","var2","var3"
You can use the mf_getquotedstr() function from the SASjs Macro Core library and do it in one line:
%Let var_list = var1 var2 var3;
%put %mf_getquotedstr(&var_list,quote=D); /* D for Double quote */
This is a FAQ but I did not find another question to link to.
You can use the TRANWRD() function to convert the spaces into "," (as long as the resulting string is less that data step maximum of 32,767 bytes). Make sure to reduce multiple spaces to one.
%let var_list = var1 var2 var3 ;
%put "%sysfunc(tranwrd(%sysfunc(compbl(&var_list)),%str( ),%str(",")))" ;
Results:
"var1","var2","var3"
Without using the SASjs Macro Core library:
%let var_list = var1 var2 var3;
%let var_list=%bquote(")%qsysfunc(prxchange(s/\s+/%nrbquote(",")/,-1,&var_list))%bquote(");
%put &=var_list.; /* VAR_LIST="var1","var2","var3" */
Let me share a little bit improved version, with pure SAS macro code:
%let var_list = var1 var2 var3;
%put &var_list.; /* var1 var2 var3 */
%let new_var_list = "%sysfunc(prxchange(s/\s+/"%str(,)"/, -1, &var_list.))";
%put &new_var_list.; /* "var1","var2","var3" */
A little bit easier to read/understand than Kermit's proposal which is pretty good of course!

Converting numeric variables to character in SAS

I have two datasets, both with same variable names. In one of the datasets two variables have character format, however in the other dataset all variables are numeric. I use the following code to convert numeric variables to character, but the numbers are changing by 490.6 -> 491.
How can I do the conversion so that the numbers wouldn't change?
data tst ;
set data (rename=(Day14=Day14_Character Day2=Day2_Character)) ;
Day14 = put(Day14_Character, 8.) ;
Day2 = put(Day2_Character, 8.) ;
drop Day14_Character Day2_Character ;
run;
Your posted code is confused. Half of it looks like code to convert from character to numeric and half looks like it is for the other direction.
To convert to character use the PUT() function. Normally you will want to left align the resulting string. You can use the -L modifier on the end of the format specification to left align the value.
So to convert numeric variables DAY14 and DAY2 to character variables of length $8 you could use code like this:
data want ;
set have (rename=(Day14=Day14_Numeric Day2=Day2_Numeric)) ;
Day14 = put(Day14_Numeric, best8.-L) ;
Day2 = put(Day2_Numeric, best8.-L) ;
drop Day14_Numeric Day2_Numeric ;
run;
Remember you use PUT statement or PUT() function with formats to convert values to text. And you use the INPUT statement or INPUT() function with informats to convert text to values.
Change the format to something like Best8.2:
data tst ;
set data (rename=(Day14=Day14_Character Day2=Day2_Character)) ;
Day14 = put(Day14_Character, best8.2) ;
Day2 = put(Day2_Character, best8.2) ;
drop Day14_Character Day2_Character ;
run;
Here is an example:
data test;
input r ;
datalines;
500.04
490.6
;
run;
data test1;
set test;
num1 = put(r, 8.2);
run;
If you do not want to specify the width and number of decimal points you can just use the BEST. informat and SAS will automatically assign the width and decimals based on the input data. However the length of the outcome variable may be large unless you specify it explicitly. This will still retain your numbers as in the original variable.

SAS Conditional Statement

This:
IF VAR1 ne VAR2 ne VAR3 ne VAR4;
I want this condition to check if:
VAR1 is not equal to VAR2, VAR3, VAR4
VAR2 is not equal to VAR1, VAR3, VAR4
VAR3 is not equal to VAR1, VAR2, VAR4
VAR4 is not equal to VAR2, VAR3, VAR1
Is this possible?
I think for the four variable case the six anded IFs is probably best. However, if you want to do this unbounded, an array solution is evident; it's more work here than needed but is less work than 10 variables -> 45 ifs.
data want;
set have;
match=0;
array vars var:;
do _t = 1 to dim(vars)-1;
do _u = _t+1 to dim(vars);
if vars[_t] = vars[_u] then match=1;
end;
if match=1 then leave;
end;
run;
This does the same thing as the 6 if's (tests 1 vs 2,3,4, tests 2 vs 3,4, tests 3 vs 4), but in array/loop form.
A couple of options. Do it in long-form with and between:
VAR1 ne VAR2 and VAR1 ne VAR3 and VAR1 ne VAR4 and
VAR2 ne VAR3 and VAR2 ne VAR4 and VAR3 ne VAR4
Or use the numerical equivalent of a TRUE value as 1 to test it:
sum(VAR1 = VAR2,
VAR1 = VAR3,
VAR1 = VAR4,
VAR2 = VAR3,
VAR2 = VAR4,
VAR3 = VAR4) = 0
You can use:
if var1=var2=var3=var4 then ...
There's some limitations to this but I can't recall them at the moment. In a straight IF condition I think it's okay.
Alternatively:
if var1 ^in (var2 var3 var4) and
if var2 ^in (var3 var4) and
if var3 ^in (var4);
Or "not in".

do loop on sas but not with a macro

It is a simple one but I'm a struggling a bit.
What I have :
What I want :
I want to remove the v0 , v1 and etc.
I'm using this piece of code
data IndieDay20140704;
set IndieDay20140704;
do i=1 to 5;
VAR1=tranwrd(var1,"v&i","");
end;
run;
It is not working correctly as it is giving me this instead (see below) plus the error
WARNING: Apparent symbolic reference I not resolved.
Questions:
1) Do I need a macro?
2) Why the error?
Many thanks for your insights.
There's an error because you're (unintentionally) using macro variable i, that you did not initialize.
I guess the idea of tranwrd is to remove words in VAR2, VAR3.. from VAR1.
The logical error is to do it also for VAR1 itself.
Check if this helps (using array):
data IndieDay20140704;
length VAR1 VAR2 VAR3 VAR3 VAR5 $10;
VAR1 = 'TEST IT';VAR5 = 'TEST';
output;
VAR1 = 'STEST IT';VAR5 = 'TEST';
output;
run;
data IndieDay20140704_modified / view= IndieDay20140704_modified;
set IndieDay20140704;
array vals VAR1 - VAR5;
do i=1 to dim(vals);
if i ne 1 then VAR1=tranwrd(var1,trim(vals(i)),"");
end;
drop i;
run;
Here I'm creating a SAS view on top of table (not a good idea to overwrite the source).
Also I think you should trim() the values from VAR2,VAR3... depending on what you want to achieve and what's in the data.
EDIT:
here the version with 'v0', 'v1'...'v5' strings:
data IndieDay20140704;
length VAR1$10;
VAR1 = 'TEST v0';
output;
VAR1 = 'TEST v11';
output;
VAR1 = 'TEST v1';
output;
run;
data IndieDay20140704_modified / view= IndieDay20140704_modified;
set IndieDay20140704;
org_var1 = var1;
do i=0 to 5;
var1 =tranwrd(var1, catt('v', put(i, 1. -L)),"");
end;
run;
catt('v', put(i, 1. -L)) concatenates string 'v' and the result of put.
put(i, 1. -L)) converts numeric variable i to text using plain numeric format w.d, 1. used here - enough for single digit numbers, -L left aligns the result
Here's one way, there are many others and this may not work if your data has a lot of variability.
data have;
length VAR1$10;
VAR1 = 'fic19v0.csv';
output;
VAR1 = 'fic19v1.cs';
output;
run;
data want ;
set have;
original_var=var1;
var1=substr(var1, 1, index(var1, ".")-3)||".csv";
run;