BookmarkSubscribeRSS Feed
David_Billa
Rhodochrosite | Level 12

I've the code as below to move the files from source to target. When I tried to execute the code below, I've got error as,

 

ERROR: Unable to copy. source=/var/sasdata/INPUT/GT/IFR_GH_MSTD_0069.xlsx
target=/var/sasdata/INPUT/LH/IFR_GH_MSTD_0069.xlsx rc3=-7440230
msg=WARNING: 3 records were truncated when the FCOPY function read from fileref FROM. 0 records were truncated when the FCOPY functi
on wrote to fileref TO. To prevent the truncation of records in future operations, you can increase the amount of space needed t
NOTE: There were 2 observations read from the data set WORK.FILES_TARGET_CHECK.

What would be likely cause for thsi error? After the execution of the program I want both .xlsx and .csv file to move to target folder but only .csv has moved. Program which I used is,

 

data files_moved;
  set files_target_check ;
  length rc1-rc4 8 msg $256 ;

  /*Copy and delete*/
  if not (scan(lowcase(filename),5,'_') in ('5601','6010','6020')) or index(lowcase(filename),'_copied.csv ')  then do;
  rc1=filename('from',source);
  rc2=filename('to',target);
  rc3=fcopy('from','to');
  if rc3 then do;
    msg=sysmsg(); 
    put 'ERROR: Unable to copy. ' source= target= rc3= msg=;
  end;
  else do;
    rc4=fdelete('from');
  end;
  output;
  end;
  rc1=filename('from');
  rc2=filename('to');
run;

Data from files_target_check is,

filename source target flag
IFR_GH_MSTD_0069.xlsx /var/sasdata/INPUT/GT/IFR_GH_MSTD_0069.xlsx /var/sasdata/INPUT/LH/IFR_GH_MSTD_0069.xlsx 1
IFR_GH_TRND_1_0069_1_20201009T075212.csv /var/sasdata/INPUT/GT/IFR_GH_TRND_1_0069_1_20201009T075212.csv /var/sasdata/INPUT/LH/IFR_GH_TRND_1_0069_1_20201009T075212.csv 1

 

11 REPLIES 11
ballardw
Super User

I suspect the "what" is that the xlsx file format is actually 1) a zip file and 2) multiple compressed XML files.

 

The "why" might be the use of the wrong function. From the documentation:

FCOPY Function

Copies records from one fileref to another fileref, and returns a value that indicates whether the records were successfully copied

Fcopy copies records, which in a simple text file such as CSV, are lines of data. XML is not like that (open  an XML in a text editor and see the difference) so I am not sure what Fcopy may see as a "record" in such.

 

 

David_Billa
Rhodochrosite | Level 12
Can I understand that issue might be with the file and the program is
right?
Peter_C
Rhodochrosite | Level 12
.xlsx files are binary files, so treat them that way - see this example in the online doc at
https://documentation.sas.com/?cdcId=pgmsascdc&cdcVersion=9.4_3.5&docsetId=lefunctionsref&docsetTarg...
David_Billa
Rhodochrosite | Level 12

@Peter_C @Tom can't we move .xlsx and .csv file in one single data step as shown below?

 

data files_moved;
  set files_target_check ;
  length rc1-rc4 8 msg $256 ;

  /*Copy and delete*/
  if not (scan(lowcase(filename),5,'_') in ('5601','6010','6020')) or index(lowcase(filename),'_copied.csv ')  then do;
  rc1=filename('from',source);
  rc2=filename('to',target);
  rc3=fcopy('from','to');
  if rc3 then do;
    msg=sysmsg(); 
    put 'ERROR: Unable to copy. ' source= target= rc3= msg=;
  end;
  else do;
    rc4=fdelete('from');
  end;
  output;
  end;
  rc1=filename('from');
  rc2=filename('to');
run;
Kurt_Bremser
Super User

Maxim 1: Read the Documentation.

Example 2 of the FCOPY Function shows how to copy a binary file. Important: use RECFM=N for the file reference.

 

Edit: changed the RECFM to N, as it is in the documentation (typo, my bad)

David_Billa
Rhodochrosite | Level 12

@Kurt_Bremser Is it recfm=n or recfm=f? How to use RECFM in my program below as I do not have filename statement (used only filename function) due to the fact that filename is dynamic. Sometimes I receive .xlsx and sometime .csv otherewise both CSV and Excel.

 

data files_moved;
  set files_target_check ;
  length rc1-rc4 8 msg $256 ;

  /*Copy and delete*/
  if not (scan(lowcase(filename),5,'_') in ('5601','6010','6020')) or index(lowcase(filename),'_copied.csv ')  then do;
  rc1=filename('from',source);
  rc2=filename('to',target);
  rc3=fcopy('from','to');
  if rc3 then do;
    msg=sysmsg(); 
    put 'ERROR: Unable to copy. ' source= target= rc3= msg=;
  end;
  else do;
    rc4=fdelete('from');
  end;
  output;
  end;
  rc1=filename('from');
  rc2=filename('to');
run;
PaulCanals
Obsidian | Level 7

Hi, for copying files I generally use the byte-for-byte technique including setting the chunksize to speed up the copying process

 

   %*-------------------------------------------------------------------------;
   %* Set source and target (copy) path and file name values:                 ;
   %*-------------------------------------------------------------------------;

   %let infile = %str();
   %let outfile = %str();

   %*-------------------------------------------------------------------------;
   %* Set copy mode and chunksize variables:                                  ;
   %*-------------------------------------------------------------------------;

   %let mode = S ; /* I (chunksize is 1) / S (use chunksize value) */
   %let chunksize = 32767 ; /* 1 - 32767 */

   %*-------------------------------------------------------------------------;
   %* Copy INFILE to OUTFILE using the byte-for-byte method:                  ;
   %*-------------------------------------------------------------------------;

   filename in "&infile.";
   filename out "&outfile.";

   data _null_;

      length 
         ifile  8 
         ofile  8
         fmtlen 8
         bytes  8
         outfmt $ 32
         rec    $ &chunksize.
         ;

      ifile = fopen('in',"&mode.",&chunksize.,'B');
      ofile = fopen('out','O',&chunksize.,'B');
      bytes = 0;
      rec   = '20'x;

      do while(fread(ifile) eq 0);

         call missing(outfmt, rec);
         rc = fget(ifile,rec,&chunksize.);

         if "%upcase(&mode.)" eq "I" then do;
            fmtlen = 1;
            outfmt = 1;
         end;
         else do;
            fcolin = fcol(ifile);
            if (fcolin - &chunksize.) eq 1 then do;
              fmtlen = &chunksize.;
            end;
            else do;
              fmtlen = fcolin - 1;
            end;
            outfmt = cats("$char", fmtlen, ".");
         end;

         bytes + fmtlen;

         rc = fput(ofile,putc(rec,outfmt));
         rc = fwrite(ofile);

      end;

      call symputx('size', bytes);

      rc = fclose(ifile);
      rc = fclose(ofile);

   run;

   filename in clear;
   filename out clear;

 

   %put File %scan(&infile.,-1,/\) copied to %scan(&outfile.,-1,/\).;
   %put Transferred &size. Bytes.;

 

Best regards,

Paul

Kurt_Bremser
Super User

Sorry about the confusion, the RECFM has to be N (when there's a contradiction between me and the documentation, the documentation is most probably correct).

You need to supply the options in a separate parameter of the FILENAME function, and you can copy text files as binary without problems:

proc export
  data=sashelp.class
  file="/folders/myfolders/class.xlsx"
  dbms=xlsx
  replace
;
run;

proc export
  data=sashelp.class
  file="/folders/myfolders/class.csv"
  dbms=csv
  replace
;
run;

data _null_;
rc = filename("from","/folders/myfolders/class.xlsx","","recfm=n");
put rc=;
rc = filename("to","/folders/myfolders/test/class.xlsx","","recfm=n");
put rc=;
rc = fcopy("from","to");
put rc=;
rc = filename("from","/folders/myfolders/class.csv","","recfm=n");
put rc=;
rc = filename("to","/folders/myfolders/test/class.csv","","recfm=n");
put rc=;
rc = fcopy("from","to");
put rc=;
run;

proc import
  datafile="/folders/myfolders/test/class.xlsx"
  dbms=xlsx
  out=work.class1
  replace
;
run;

proc import
  datafile="/folders/myfolders/test/class.csv"
  dbms=csv
  out=work.class2
  replace
;
run;

This code runs without ERRORs or WARNINGs, and creates two (nearly) identical datasets in WORK.

The values are identical, but some attributes differ (formats, informats) because of PROC IMPORT.

David_Billa
Rhodochrosite | Level 12

@Kurt_Bremser what will happen if I use recfm=n for CSV files as I shown below? Because in real life I may transfer CSV or Excel file or both  as files are dynamic.

 

data _null_;
rc = filename("from","/folders/myfolders/class.csv","","recfm=n");
run;
Kurt_Bremser
Super User

The answer is in my previous post. Please read the code closely, you will find that I used the "binary" method for a csv file without problems.

AllanBowe
Barite | Level 11

For reference we recently published a macro that will (binary) copy ALL files in a particular directory, and recursively so:  https://core.sasjs.io/mp__copyfolder_8sas.html

/Allan
MacroCore library for app developers
Data Workflows, Data Contracts, Data Lineage, Drag & drop excel EUCs to SAS 9 & Viya - Data Controller
DevOps and AppDev on SAS 9 / Viya / Base SAS - SASjs

hackathon24-white-horiz.png

The 2025 SAS Hackathon has begun!

It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.

Latest Updates

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 11 replies
  • 4730 views
  • 5 likes
  • 6 in conversation