
11-22-2024
FriedEgg
SAS Employee
Member since
06-23-2011
- 1,340 Posts
- 27 Likes Given
- 32 Solutions
- 599 Likes Received
This widget could not be displayed.
-
Latest posts by FriedEgg
Subject Views Posted 5709 06-04-2021 12:11 PM 2152 01-19-2021 05:54 PM 2097 03-04-2020 11:40 AM 10849 01-09-2020 11:46 AM 10772 10-29-2019 11:07 AM 5945 09-24-2019 06:35 PM 2756 09-24-2019 11:02 AM 1346 09-02-2019 09:59 AM 9168 07-12-2019 11:09 AM 6359 07-12-2019 11:05 AM -
Activity Feed for FriedEgg
- Got a Like for Re: Encoding in proc export. 09-25-2024 04:32 PM
- Got a Like for Re: base64 encoding. 02-28-2024 08:40 AM
- Liked Re: macro parameter naming convention for yabwon. 01-08-2024 07:40 PM
- Liked Re: Generate global macro variables and then use them in a do loop for Reeza. 09-06-2023 01:40 PM
- Liked SAS Hackathon - Rapid Application Development a new tool from SAS in the Viya Ecosystem for Peter7. 03-16-2023 08:19 PM
- Got a Like for Re: SAS MACRO CATALOG: How to retrieve the source code of a compiled macro?. 09-02-2022 09:27 AM
- Got a Like for Re: How to get labels in proc means output dataset ?. 06-06-2022 02:00 AM
- Got a Like for How to get labels in proc means output dataset ?. 06-06-2022 01:58 AM
- Got a Like for Accessing/checking sort order in a dataset. 04-13-2022 10:03 AM
- Got a Like for Re: Question vlookup equivalent in SAS. 02-22-2022 06:01 AM
- Got a Like for Re: Encoding in proc export. 12-09-2021 08:44 AM
- Got a Like for Re: Regex prxparse question validating emails. 08-09-2021 01:46 PM
- Got a Like for Starting SAS in batch mode on windows. 06-30-2021 03:41 PM
- Got a Like for External Sources in SAS Studio Process Flow (.CDF) Files. 06-18-2021 07:24 AM
- Posted Re: Allow PROC SORT to output multiple datasets on SAS Product Suggestions. 06-04-2021 12:11 PM
- Got a Like for Add support for Markdown in SAS Studio Notes Detail. 02-03-2021 04:31 PM
- Got a Like for Re: Help converting a CURL command into PROC HTTP. 01-21-2021 05:44 PM
- Posted Re: Help converting a CURL command into PROC HTTP on Developers. 01-19-2021 05:54 PM
- Got a Like for Re: Select top 3 records for each id. 01-12-2021 12:56 PM
- Got a Like for External Sources in SAS Studio Process Flow (.CDF) Files. 11-13-2020 03:31 PM
-
Posts I Liked
Subject Likes Author Latest Post 1 2 8 2 1 -
My Liked Posts
Subject Likes Posted 1 06-27-2013 09:24 AM 1 08-31-2011 11:45 AM 1 08-31-2011 10:56 AM 1 03-16-2012 08:42 PM 1 01-19-2021 05:54 PM -
My Library Contributions
Subject Likes Author Latest Post 0 0
02-02-2018
04:26 PM
2 Likes
* writing this way to a file for ease of posting. you would have it as a real file; filename inc temp;
data _null_;
file inc;
input @;
put _infile_;
cards4;
filename cp temp;
proc groovy classpath=cp;
add sasjar="commons_codec" version="1.7.0.0_SAS_20121211183158"; *version is specific to SAS Installation and may differ from this;
submit parseonly;
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.apache.commons.codec.binary.Base64
class SASHash {
String base64hmacsha256(String key, String message) {
def mac = Mac.getInstance("HmacSHA256")
mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"))
def hash = Base64.encodeBase64String(mac.doFinal(message.getBytes()))
return hash
}
}
endsubmit;
quit;
options set=classpath "%sysfunc(pathname(cp,f))";
;;;;
run;
* general purpose macro can be expanded to include additional methods;
%macro SASHash(action,key=key,message=message,return=hash,include=inc);
%goto &action;
%compile:
%include &include;
%return;
%declare:
if _n_=1 then declare javaobj SASHash('SASHash');
%return;
%base64hmacsha256:
SASHash.callStringMethod("&action",strip(&key),strip(&message),&return);
%return;
%mend;
%let key = sdfoij3242039sdflkj3r23;
%SASHash(compile,include=inc)
data class;
%SASHash(declare)
length hashname $ 128;
set sashelp.class;
%SASHash(base64hmacsha256,key="&key",message=name,return=hashname)
run;
... View more
02-02-2018
12:49 PM
3 Likes
The main problem is that you have not built something with PROC GROOVY that you can call as a function from inside a data step. It would also be good to note here that what you have implemented is HMAC SHA-256 like in my gist. This is very different from SHA-256 and thus the built-in function in SAS 9.4, SHA256 function recommended by @LinusH doesn't apply and neither does the code from Rick on behalf of @ChrisHemedinger. SAS 9.4 does however have a built-in function that duplicates this functionality, which is the SHA256HMACHEX Function. This all leads to the question... Do you actually want HMAC SHA-256 or just SHA-256?
... View more
01-11-2018
04:11 PM
1 Like
@jklaverstijn, At least part of your issue may be related to this. I would submit a ticket to SAS Technical Support to corroborate this.
... View more
01-11-2018
03:22 PM
DATA avgcelsius;
length city $ 17;
infile cards col=col;
input @'2c'x + 4 @;
city = substr(_infile_,1,col-2);
input jan feb mar apr may jun jul aug sep oct nov dec;
DATALINES;
State College, PA -2 -2 2 8 14 19 21 20 16 10 4 -1
Miami, FL 20 20 22 23 26 27 28 28 27 26 23 20
St. Louis, MO -1 1 6 13 18 23 26 25 21 15 7 1
New Orleans, LA 11 13 16 20 23 27 27 27 26 21 16 12
Madison, WI -8 -5 0 7 14 19 22 20 16 10 2 -5
Houston, TX 10 12 16 20 23 27 28 28 26 21 16 12
Phoenix, AZ 12 14 16 21 26 31 33 32 30 23 16 12
Seattle, WA 5 6 7 10 13 16 18 18 16 12 8 6
San Francisco, CA 10 12 12 13 14 15 15 16 17 16 14 11
San Diego, CA 13 14 15 16 17 19 21 22 21 19 16 14
;
RUN;
... View more
09-05-2017
10:49 AM
To use a sas data set view in the DS2 Procedure is must be created by the FEDSQL Procedure statement CREATE VIEW. A view using the SQL Procedure will not be compatible and neither will a view created by the data statement VIEW= argument.
* no access to sashelp from fedsql/ds2;
data class;
set sashelp.class;
run;
proc sql;
create view v_class
as
select *
from class ;
quit;
proc fedsql;
create view v_class2
as
select *
from class ;
quit;
data v_class3 / view=v_class2;
set class;
run;
proc ds2;
* fails... cannot use PROC SQL view;
data class2;
method run();
set v_class;
end;
enddata;
run;
* fails... cannot use DATA Statement view;
data class2(overwrite=yes);
method run();
set v_class3;
end;
enddata;
run;
* success... can use FEDSQL view;
data class2(overwrite=yes);
method run();
set v_class2;
end;
enddata;
run;
quit;
... View more
07-19-2017
11:09 AM
1 Like
filename ft15f001 temp;
data have;
infile ft15f001;
input text $194.;
parmcards;
firm a advises on the volkswagen settlement with us authorities regarding emissions from diesel engines firm a advises the carlyle group on its $3.2bn acquisition of atotech advising coca cola
;
run;
data want;
prxid=prxparse('/\badvis(?:es|ed|ing)\b\s+((\b\w+\b\s*){1,3})/i');
do until (eof);
set have end=eof;
start=1;
stop=length(text);
call prxnext(prxid, start, stop, text, position, length);
do while (position > 0);
call prxposn(prxid, 1, start, length);
match=substr(text, start, length);
output;
call prxnext(prxid, start, stop, text, position, length);
end;
end;
run;
... View more
07-12-2017
11:44 AM
1 Like
Since the OP was about the java version of the interface, and I don't get to use it much lately, here is a full example of a class to test the update vs delete metadata requests with OMI_IGNORE_NOTFOUND
import com.sas.metadata.remote.*;
import org.apache.log4j.Logger;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.RemoteException;
import java.util.Properties;
import static org.apache.log4j.Logger.*;
public class MdExample {
final static Logger logger = getLogger(MdExample.class);
private MdFactory _factory = null;
private String serverName = null;
private String serverPort = null;
private String serverUser = null;
private String serverPass = null;
private boolean serverLog = false;
public MdExample() {
getProperties();
initializeFactory();
}
private void getProperties() {
Properties prop = new Properties();
InputStream input = null;
try {
input = new FileInputStream("config.properties");
prop.load(input);
serverName = prop.getProperty("serverName", "localhost");
serverPort = prop.getProperty("serverPort", "8561");
serverUser = prop.getProperty("serverUser");
serverPass = prop.getProperty("serverPass");
serverLog = Boolean.parseBoolean(prop.getProperty("serverLog", "false"));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void initializeFactory() {
try
{
_factory = new MdFactoryImpl(false);
boolean debug = false;
if (logger.isDebugEnabled()) {
debug = true;
}
_factory.setDebug(debug);
_factory.setLoggingEnabled(serverLog);
_factory.getUtil().setOutputStream(System.out);
_factory.getUtil().setLogStream(System.out);
} catch (Exception e)
{
e.printStackTrace();
}
}
public boolean connectToServer() {
try
{
MdOMRConnection connection = _factory.getConnection();
connection.makeOMRConnection(
serverName,
serverPort,
serverUser,
serverPass
);
} catch (MdException e) {
Throwable t = e.getCause();
if (t != null)
{
String ErrorType = e.getSASMessageSeverity();
String ErrorMsg = e.getSASMessage();
if (ErrorType != null)
{
System.out.println(ErrorType + ": " + ErrorMsg);
}
if (t instanceof org.omg.CORBA.COMM_FAILURE)
{
System.out.println(e.getLocalizedMessage());
}
}
else
{
System.out.println(e.getLocalizedMessage());
}
e.printStackTrace();;
return false;
} catch (RemoteException e) {
e.printStackTrace();
return false;
}
return true;
}
public String getFoundationRepository() {
try
{
MdOMIUtil omiUtil = _factory.getOMIUtil();
return omiUtil.getFoundationReposID();
} catch (MdException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
return null;
}
public boolean doesObjectExist(String reposID, String type, String xmlSelect) {
try
{
MdOMIUtil omiUtil = _factory.getOMIUtil();
int mdObjectCount = omiUtil.doesObjectExist(reposID,type,xmlSelect);
logger.debug("Sent to Metadata Server:\n"
+ "<GetMetadataObjects>\n"
+ " <ReposId>" + reposID + "</ReposId>\n"
+ " <Type>" + type + "</Type>\n"
+ " <Objects/>\n"
+ " <NS>SAS</NS>\n"
+ " <!-- OMI_XMLSELECT -->\n"
+ " <Flags>" + MdOMIUtil.OMI_XMLSELECT + "</Flags>\n"
+ " <Options>\n"
+ " " + xmlSelect + "\n"
+ " </Options>\n"
+ "</GetMetadataObjects>");
logger.debug("Response: " + mdObjectCount);
return mdObjectCount > 0;
} catch (MdException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
}
public void DoRequest(String xml) {
try
{
MdOMIUtil omiUtil = _factory.getOMIUtil();
omiUtil.DoRequestNoReturn(xml,true);
} catch (MdException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MdExample util = new MdExample();
boolean connected = util.connectToServer();
if (connected) {
System.out.println("Connected...");
}
else
{
System.out.println("Error Connecting...");
return;
}
String foundationFQID = util.getFoundationRepository();
String id = foundationFQID.split("\\.")[1] + ".Z0000001";
boolean idExists = util.doesObjectExist(foundationFQID,"SASLibrary","<XMLSELECT search=\"SASLibrary[@Id='" + id + "']\"/>");
int flags = (MdOMIUtil.OMI_TRUSTED_CLIENT | MdOMIUtil.OMI_IGNORE_NOTFOUND);
String request = ""
+ "<UpdateMetadata>"
+ " <Metadata>"
+ " <SASLibrary Id='" + id + "' Name='Foobar'/>"
+ " </Metadata>"
+ " <NS>SAS</NS>"
+ " <Flags>" + flags + "</Flags>"
+ " <Options/>"
+ "</UpdateMetadata>"
;
System.out.println("UpdateMetadata will produce a GenericError Exception");
util.DoRequest(request);
System.out.println("DeleteMetadata will not produce a Exception");
util.DoRequest(request.replace("Update","Delete"));
}
}
Connected...
[main][DEBUG MdExample] - Sent to Metadata Server:
<GetMetadataObjects>
<ReposId>A0000001.A5UL0H0J</ReposId>
<Type>SASLibrary</Type>
<Objects/>
<NS>SAS</NS>
<!-- OMI_XMLSELECT -->
<Flags>128</Flags>
<Options>
<XMLSELECT search="SASLibrary[@Id='A5UL0H0J.Z0000001']"/>
</Options>
</GetMetadataObjects>
[main][DEBUG MdExample] - Response: 0
UpdateMetadata will produce a GenericError Exception
com.sas.iom.SASIOMDefs.GenericError: SASLibrary : A5UL0H0J.Z0000001 cannot be found in the wlibrary container in the REPOSMGR repository.
at com.sas.iom.SASIOMDefs.GenericErrorHelper.read(GenericErrorHelper.java:22)
at com.sas.iom.SASIOMDefs.GenericErrorHelper.extract(GenericErrorHelper.java:47)
at com.sas.meta.SASOMI._portable_stub_IOMI.DoRequest(_portable_stub_IOMI.java:581)
at com.sas.metadata.remote.MdOMIWrapperImpl.DoRequest(MdOMIWrapperImpl.java:742)
at com.sas.metadata.remote.MdOMIUtilImpl.DoRequestNoReturn(MdOMIUtilImpl.java:2639)
at MdExample.DoRequest(MdExample.java:157)
at MdExample.main(MdExample.java:198)
...
DeleteMetadata will not produce a Exception
Process finished with exit code 0
... View more
07-11-2017
08:58 PM
OMI_IGNORE_NOTFOUND with the UpdateMetadata method only ignores objects notfound within the bounds of a remove function.
proc metadata
in='<UpdateMetadata>
<Metadata>
<Index Id="A5UL0H0J.BR000001" Name="foobaridx" Desc="A Index Description">
<Columns Function="Remove">
<Column Id="A5UL0H0J.BG0001ND" Name="Sex"/><!-- exists -->
<Column Id="A5UL0H0J.B0000001" Name="Foo"/><!-- not exists -->
</Columns>
</Index>
</Metadata>
<NS>SAS</NS>
<!-- OMI_TRUSTED_CLIENT -->
<Flags>268435456</Flags>
<Options/>
</UpdateMetadata>';
run;
ERROR: Subelement Column : A5UL0H0J.B0000001 cannot be removed from Index : A5UL0H0J.BR000001.
NOTE: PROCEDURE METADATA used (Total process time):
real time 0.07 seconds
cpu time 0.00 seconds
NOTE: The SAS System stopped processing this step because of errors.
proc metadata
in='<UpdateMetadata>
<Metadata>
<Index Id="A5UL0H0J.BR000001" Name="foobaridx" Desc="A Index Description">
<Columns Function="Remove">
<Column Id="A5UL0H0J.BG0001ND" Name="Sex"/><!-- exists -->
<Column Id="A5UL0H0J.B0000001" Name="Foo"/><!-- not exists -->
</Columns>
</Index>
</Metadata>
<NS>SAS</NS>
<!-- OMI_TRUSTED_CLIENT + OMI_IGNORE_NOTFOUND -->
<Flags>402653184</Flags>
<Options/>
</UpdateMetadata>';
run;
NOTE: Response XML:
<UpdateMetadata><Metadata><Index Id="A5UL0H0J.BR000001" Name="foobaridx" Desc="A Index Description"><Columns
Function="Remove"><Column Id="A5UL0H0J.BG0001NG" Name="Sex"/><Column Id="A5UL0H0J.B0000001"
Name="Foo"/></Columns></Index></Metadata><NS>SAS</NS><Flags>402653184</Flags><Options/></UpdateMetadata>
NOTE: PROCEDURE METADATA used (Total process time):
real time 0.04 seconds
cpu time 0.01 seconds
... View more
05-24-2017
02:15 PM
1 Like
SAS/SHARE is not a supported data source using the 'remote' engine. Oddly, you still should utilize it through ODBC instead.
List of supported data sources (same for FedSQL as DS2):
http://support.sas.com/documentation/cdl/en/fedsqlref/67364/HTML/default/viewer.htm#n07v02kso1fe3vn1qv7cmzyw5gbv.htm
... View more
04-12-2017
01:42 PM
1 Like
#1 I don't feel is surprising. The B macro variable inside the dosubl session is in the global scope and is not defined elsewhere, so it is transferred back into the global scope. The fact that it will transfer the value to a local scope, if it exists also conforms with expectations. It is a departure from the expectations of defining the variable with a %let statement inside a macro without dosubl, which would default to the local scope though.
#2 Like in the first example, we have a disconnect here between expected behavior without dosubl and with it.
I think, ideally, the behavior of the macro variable transfer should respect the behaviors without using dosubl but I think this is a good step forward from previous behavior. I wouldn't think of this as a bug but I do think the further improvements could be made.
I feel that the reasoning behind the issue displayed by your example #2 can be best explained by looking at the %sysmexecdepth function output.
%macro outer;
%put OUTER: %sysmexecdepth %sysmexecname(%sysmexecdepth);
%inner
%let rc=%sysfunc(dosubl(%nrstr(%inner)));
%mend;
%macro inner;
%put INNER: %sysmexecdepth %sysmexecname(%sysmexecdepth);
%mend;
%outer
OUTER:1
INNER:2
INNER:1
As you can see, the execution of the inner macro by the dosubl doesn't respect the nest level of the macro to the OUTER macro. This would be a departure from the behavior without using dosubl and is a reasonable explaination for why the value does not move up in scope.
... View more
03-29-2017
12:19 PM
7 Likes
This made me curious. Where would be the furthest place someone could travel to SGF17 from?
proc fcmp outlib=work.func.geo;
function radians(deg) group='Geospatial' label='Convert degrees to radians';
return ((deg/180)*constant('PI'));
endsub;
function degrees(rad) group='Geospatial' label='Convert radians to degrees';
return ((rad/constant('PI'))*180);
endsub;
function disrad(d, option $) group='Geospatial' label='Convert distance to unit';
if option='nm'
then return ((d*180*60)/constant('PI'));
if option='km'
then return ((d*180*60*1.852)/constant('PI'));
if option='miles'
then return ((d*180*60*1.150779)/constant('PI'));
endsub;
function antipodal(lat1, lon1, lat2, lon2) group='Geospatial' ;
tol=1e-9;
diflat=abs(lat1 - lat2);
diflon=abs(lon1 + lon2);
return ((diflat < tol) & (abs(mod(diflon,360)-180) < tol));
endsub;
function gcDistance(lat1, lon1, lat2, lon2) group='Geospatial' label='Great Circle Distance in Radians';
return (2*arsin(sqrt((sin((lat1-lat2)/2))**2 + cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))**2)));
endsub;
subroutine gcIntermediate(lat1, lon1, lat2, lon2, f, lat, lon) group='Geospatial' label='Calculate intermediate points along a fractional point along a Great Circle route';
outargs lat, lon;
if (antipodal(lat1, lon1, lat2, lon2))
then return;
if (lat1=lat2 and lon1=lon2)
then return;
d=gcDistance(lat1,lon1,lat2,lon2);
A=sin((1-f)*d)/sin(d);
B=sin(f*d)/sin(d);
x=A*cos(lat1)*cos(lon1) + B*cos(lat2)*cos(lon2);
y=A*cos(lat1)*sin(lon1) + B*cos(lat2)*sin(lon2);
z=A*sin(lat1) + B*sin(lat2);
lat=atan2(z,sqrt(x**2+y**2));
lon=atan2(y,x);
endsub;
quit;
options cmplib=(work.func);
data map;
set mapsgfk.world(drop=x y);
where cont^=97 and density<2;
x=radians(long);
y=radians(lat);
run;
proc gproject data=map out=right dupok project=none
longmin=0 longmax=4;
id id;
run;
proc gproject data=map out=left dupok project=none
longmin=-4 longmax=0;
id id;
run;
data right;
set right;
segment=segment+5000;
x=x-constant('PI')*2;
run;
data pmap;
set right left;
run;
proc gproject data=pmap out=pmap parmin=mapsgfk.projparm parmentry=world norangecheck radians parmout=work.projparm;
id id;
run;
data orlando_dist;
set mapsgfk.world_cities(rename=(lat=_lat long=_long) where=(city='Orlando' and MapIDName1='Florida'));
_lat=radians(_lat);
_long=radians(_long);
do until(done);
set mapsgfk.world_cities(where=(prxmatch('/^cities_/o',CtType))) end=done;
lat=radians(lat);
long=radians(long);
dist_km=disrad(gcDistance(_lat,_long,lat,long),'km');
geod_km=geodist(_lat,_long,lat,long,'rk');
output;
end;
run;
ods select none;
ods output ExtremeObs=orlando_dist_max(keep=high city_high MapIDName1_high idname_high lat_high long_high _lat_high _long_high rename=(high=distance lat_high=lat1 long_high=lon1 _lat_high=lat2 _long_high=lon2));
proc univariate data=orlando_dist nextrobs=1;
var dist_km;
id city idname MapIDName1 lat long _lat _long;
run;quit;
ods exclude none;
data orlando_dist;
set orlando_dist_max end=done;
f=0;
y=lat1;
x=lon1;
output;
do f=0.01 to 0.99 by 0.01;
call gcIntermediate(lat1,lon1,lat2,lon2,f,y,x);
output;
end;
f=1;
y=lat2;
x=lon2;
output;
run;
data orlando_dist;
length id $15;
set orlando_dist;
if x>0 then x=x-constant('PI')*2;
id=cats('gcD',int(distance));
run;
proc gproject data=orlando_dist out=orlando_dist parmin=mapsgfk.projparm parmentry=world norangecheck radians;
id id;
run;
data anno;
length function color $8 style $12 html $300;
retain xsys ysys '2' hsys '3' line 1 anno_flag 1 when 'a';
set orlando_dist;
by id notsorted;
if first.id then do;
function='move'; output;
end;
else do;
function='draw'; size=.25; color='red'; output;
end;
run;
proc print data=orlando_dist_max label;
title 'Furthest Great Circle Distance to Orlando, FL';
footnote 'Location data from mapsgfk.world_cities';
id CITY_High IDNAME_High;
var distance;
label IDNAME_High='Country' CITY_High='City' distance='Distance (km)';
run;
proc gmap map=pmap data=pmap anno=anno;
title 'Great Circle Route Map';
id id;
choro id / levels=1 nolegend des='';
run;
quit;
Now, obviously, there are a lot of assumptions here about how people are traveling, etc... but, it's just for fun. The map definitely deserves some better annotation as well, but I've been wanting to write something for displaying great circle routes for a while, so that's what I've done.
City
Country
Distance (km)
Geraldton
Australia
18442.7
... View more
03-22-2017
10:42 AM
The output of the SHA256 function in SAS is the raw bytes. I assume you want the hash string. In which case you just want to combine the SHA256 function call with a PUT function and the $hex64. format. Without specifying what SQL implementation you are referring to, I can only speculate what you are really looking for.
data foo;
set sashelp.class;
hash_name=put(sha256(name),$hex64.);
run;
... View more
03-21-2017
04:03 PM
1 Like
Remove the line:
**%let count=0;
Or change it to a global comment.
I would also %unquote your driver text being passed back to the %window
i.e.
%unquote(# (&val) @15 "&name" attr= underline;)
... View more
03-20-2017
06:24 PM
1 Like
_webout is for stored process
_htmlout is for results windows when using SAS Studio
ods html5(WEB) file=_HTMLOUT stylesheet=(URL="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/css/materialize.min.css");
proc print data=sashelp.class style={class="striped"};
id name / style={fontfamily="Roboto" fontsize=16};
var sex age height weight;
run;
... View more
03-17-2017
01:32 PM
1 Like
You are going to get unwanted results unless you modify the regular expression to look for a word boundary too
data have;
input text_field $10.;
cards;
Expression
Tools
;
run;
data want;
set have;
new_text_field=prxchange('s/([A-Z]\w+)(?=s)/$1''/',-1,text_field);
foo_text_field=prxchange('s/([A-Z]\w+)(?=s\b)/$1''/',-1,text_field);
put (text_field new_text_field foo_text_field) (/=);
run;
text_field=Expression
new_text_field=Expres'sion
foo_text_field=Expression
text_field=Tools
new_text_field=Tool's
foo_text_field=Tool's
... View more