ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

Writing to a .txt file from RPGLE

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Writing to a .txt file from RPGLE

    To be upfront, I've never actually needed to create a text file from a RPGLE program before, so I'm going off what I find on the web for most of it.
    I have one statement that is totally stymieing me, and I'm not entirely sure why. (code to follow)


    Code:
    ]d crlf c const(x'0d25')
    d term c const(x'00')
    d pathfile s 50
    d openmode s 100
    d fileptr s *
    d snddata s 32767a
    d i s 3p 0
    *
    dopenfile pr * extproc('_C_IFS_fopen')
    d * value
    d * value
    *
    dwritefile pr 10i 0 extproc('_C_IFS_fputs')
    d * value options(*string)
    d * value
    *
    dclosefile pr extproc('_C_IFS_fclose')
    d * value options(*string)
    
    /free
    if rvr_hold = blank;
    rvr_hold = usr_array(y);
    snddata = 'Hi ' + %trim(%char(usr_array(y))) + ',' + crlf + term;
    writefile(%addr(snddata):fileptr);
    snddata = 'There are open tasks that require your review:' + crlf +
    term;
    writefile(%addr(snddata):fileptr);
    endif;
    
    [B]if rvr_hold = rvr_hold;
    snddata = %trim(ecr_array(y)) + ', ' +
    %trim(%char(seq_array(y))) + ' - ' +
    %trim(dsc_array(y)) + ' has ' +
    %trim(%char(cnt_array(y))) +
    ' tasks open assigned to ';
    if asn_array(y) = *blank;
    snddata = %trim(snddata) + ' no current assignee' +
    crlf;
    else;
    snddata = snddata + %trim(asn_array(y)) + crlf;
    endif;
    writefile(%addr(snddata):fileptr); [/B]
    else;
    ....
    The first pair of writes to the file work perfectly, it's when I get to the third write that I have issues (the bolded section of code). It writes the first line fine, then after 32560-ish spaces writes the next line. If I add the term field to the back of the statement, it doesn't write at all. Thanks ahead of time for taking a look at it, I've been at it for 2 days and don't see the issue.

    Andy




    Last edited by tomholden; September 13, 2018, 07:33 AM.

  • #2
    Maybe missing %trim on this line since snddata is not varying?

    snddata = snddata + %trim(asn_array(y)) + crlf;

    Comment


    • #3
      I'm not sure that's it, I've removed the entire bolded statement replacing it with (something way more simple):
      snddata = 'The current index is ' + %char(y) + crlf;
      writefile(%addr(snddata):fileptr);

      And I get the same result. In fact, I did try changing snddata to varying and ended up with an empty file as well.

      Comment


      • #4
        You are doing this on the end of snddata '+ term; ' on the first 2 but not the others. You need the terminator for it to tell where the string you are sending to writefile() ends.

        Comment


        • #5
          That's where the problem comes in, if I include the '+term;' on the end of that third writefile() I end up with a blank file & I don't understand why.

          Comment


          • #6
            There's a typo in your [CODE] tag in the original post that is making this code really hard to read. I'm going to repost the code, here, since I took the time (about 20 minutes) to reformat it... hopefully this will make it easier for other people as well.

            Code:
                 d crlf            c                   const(x'0d25')
                 d term            c                   const(x'00')
                 d pathfile        s             50
                 d openmode        s            100
                 d fileptr         s               *
                 d snddata         s          32767a
                 d i               s              3p 0
            
                 d openfile        pr              *   extproc('_C_IFS_fopen')
                 d                                 *   value
                 d                                 *   value
            
                 d writefile       pr            10i 0 extproc('_C_IFS_fputs')
                 d                                 *   value options(*string)
                 d                                 *   value
            
                 d closefile       pr                  extproc('_C_IFS_fclose')
                 d                                 *   value options(*string)
            
                  /free
            
                   if rvr_hold = blank;
                      rvr_hold = usr_array(y);
                      snddata = 'Hi ' + %trim(%char(usr_array(y))) + ',' + crlf + term;
                      writefile(%addr(snddata):fileptr);
                      snddata = 'There are open tasks that require your review:' + crlf +
                                 term;
                      writefile(%addr(snddata):fileptr);
                   endif;
            
                   if rvr_hold = rvr_hold;
                      snddata = %trim(ecr_array(y)) + ', ' +
                                %trim(%char(seq_array(y))) + ' - ' +
                                %trim(dsc_array(y)) + ' has ' +
                                %trim(%char(cnt_array(y))) +
                                ' tasks open assigned to ';
                     if asn_array(y) = *blank;
                         snddata = %trim(snddata) + ' no current assignee' +
                                   crlf;
                     else;
                         snddata = snddata + %trim(asn_array(y)) + crlf;
                     endif;
                     writefile(%addr(snddata):fileptr);
                   else;
                   .
                   .

            Comment


            • #7
              This code is more difficult than it needs to be because you're not taking advantage of RPG's options(*string). Because you're not using it (or, in some cases, using it improperly) you have to manually resort to using pointers and null-terminators. So, first I'd like to tell you how to make it work the way you coded it, so that you'll understand what is going on. Next, I'd like to tell you how to make the code better (which I'll do it in a separate message).

              The APIs you are using (fopen, fputs, fclose) are part of the ILE C runtime, and are part of the ANSI C specification, designed to be the same in all C languages across all platforms. Since they are designed for C, we must follow C's rules. In C, a character string has no pre-defined length... you simply give the computer a location in its memory (i.e. a pointer) and tell it that everything after that up until the x'00' character is one string. So the only way to determine the length of the string is by looking for that x'00'.

              So when you do this:
              Code:
                       if asn_array(y) = *blank;
                           snddata = %trim(snddata) + ' no current assignee' +
                                     crlf;
                       else;
                           snddata = snddata + %trim(asn_array(y)) + crlf;
                       endif;
                       writefile(%addr(snddata):fileptr);
              Notice that there's no "term", here... that means that it will write data starting at whereever the "snddata" variable is stored in the computer's memory (that's what %ADDR tells it) and keep going until by chance it happens to find a x'00' character.

              Since a lot of unused memory is set to x'00' by default, most of the time you'll hit a x'00' after the 32767a of the 'snddata' variable. But if by chance the space in memory after 'snddata' happens to be used for something, you'd actually get the contents of whatever that happened to be in your file. It's possible (albeit not very likely) that this one writefile() statement could write megabytes of data. Missing that 'term' is a big problem.

              In addition, you've forgotten to do a %trim() on snddata. That means you'll get all 32767 characters of the string.

              To fix it, simply change the code to look like this

              Code:
                       if asn_array(y) = *blank;
                           snddata = %trim(snddata) + ' no current assignee' +
                                     crlf + term;
                       else;
                           snddata = %trimr(snddata) + ' '
                                   + %trim(asn_array(y)) + crlf + term;
                       endif;
                       writefile(%addr(snddata):fileptr);
              I also concatenated an extra blank between "snddata" and the asn_array since you probably wanted a space there... but that's a little thing.

              I put the whole thing together in a runnable program so that I could verify that my work was correct, and to provide something that others could try. This did mean adding a bunch of missing definitions (which might not match yours) because you did not post a complete example. Please consider posting a complete runnable program in the future, it will save people a lot of time in trying to help you.

              Code:
                   H dftactgrp(*no)
              
                   d crlf            c                   const(x'0d25')
                   d term            c                   const(x'00')
                   d pathfile        s             50
                   d openmode        s            100
                   d fileptr         s               *
                   d snddata         s          32767a
                   d i               s              3p 0
              
                   d openfile        pr              *   extproc('_C_IFS_fopen')
                   d                                 *   value
                   d                                 *   value
              
                   d writefile       pr            10i 0 extproc('_C_IFS_fputs')
                   d                                 *   value options(*string)
                   d                                 *   value
              
                   d closefile       pr                  extproc('_C_IFS_fclose')
                   d                                 *   value options(*string)
              
              
                   D blank           s             10c   inz(*blanks)
                   d rvr_hold        s             10c   inz(*blanks)
                   d y               s             10i 0 inz(1)
                   d usr_array       s             10c   dim(1)
                   d ecr_array       s             10a   dim(1)
                   D seq_array       s              3p 0 dim(1)
                   D dsc_array       s             10a   dim(1)
                   D cnt_array       s              3p 0 dim(1)
                   D asn_array       s             10a   dim(1)
                   D name            s            100a
                   D mode            s             10a
                    /free
              
                     y = 1;
                     usr_array(y) = 'Andy';
                     ecr_array(y) = 'ECR Data';
                     seq_array(y) = 101;
                     dsc_array(y) = 'XXX';
                     cnt_array(y) = 201;
                     asn_array(y) = 'Scott';
              
                     name = 'testfile.txt' + term;
                     mode = 'w,crln=N' + term;
              
                     fileptr = openfile(%addr(name): %addr(mode));
              
                     if rvr_hold = blank;
                        rvr_hold = usr_array(y);
                        snddata = 'Hi ' + %trim(%char(usr_array(y))) + ',' + crlf + term;
                        writefile(%addr(snddata):fileptr);
                        snddata = 'There are open tasks that require your review:' + crlf +
                                   term;
                        writefile(%addr(snddata):fileptr);
                     endif;
              
                     if rvr_hold = rvr_hold;
                        snddata = %trim(ecr_array(y)) + ', ' +
                                  %trim(%char(seq_array(y))) + ' - ' +
                                  %trim(dsc_array(y)) + ' has ' +
                                  %trim(%char(cnt_array(y))) +
                                  ' tasks open assigned to ';
                       if asn_array(y) = *blank;
                           snddata = %trim(snddata) + ' no current assignee' +
                                     crlf + term;
                       else;
                           snddata = %trimr(snddata) + ' '
                                   + %trim(asn_array(y)) + crlf + term;
                       endif;
                       writefile(%addr(snddata):fileptr);
                     endif;
              
                     closefile(fileptr);
              
                     *inlr = *on;

              Comment


              • #8
                Here's a summary of what I would do differently to improve your code.
                1. Do not rename the APIs! These APIs fopen, fputs, fclose are part of worldwide, cross-platform standards. They are used by MILLIONS of people! When someone sees writefile(), for example, how do they know what that is? Is it the write() API? Is it fwrite()? Is it your own routine? Especially in a complex program, taking the time to look up at the prototypes to determine what API is actually called can be a time waster... use the standard names that everyone knows, fopen, fputs, fclose and someone who is familiar with these can save a LOT of time.
                2. Get better prototypes. These prototypes are wrong. fopen() is missing options(*string), fclose() has options string where it SHOULD NOT be. The names of the parameters are missing, etc. Put them in a copybook so that every time you want to use them, you don't have to re-code them (and open up chances for mistakes). You can have my copybook if you want, it's available here: http://www.scottklement.com/rpg/copy...io_h.rpgle.txt
                3. Use options(*string) where appropriate,. This automatically adds the x'00' to the strings, and obviates the need for passing pointers instead of character fields, it makes it much less likely that you'll make an error like the one I showed you in my previous post, and makes your code easier to read. Though, if you use my prototypes, they'll already be there.
                4. fclose() does not accept a character string as a parameter, only a file handle! It does not make sense to code options(*string) here. Again, use my prototypes and this will already be correct.
                5. Use the VARYING keyword (or VARCHAR in free format). Back in the days of punch cards, maybe it made sense for everything to be a fixed length. Not today! Save yourself some coding, make your code easier to read, and also more efficient. No modern programming language uses fixed-length strings anymore. (A few still support them, including RPG, but even those are becoming rare.)
                6. Use symbolic names to help clarify what things mean. Coding the file handle as a pointer will work, but what does it mean? A pointer can be anything stored in memory -- which is basically anything a computer can do. Use a name like pFILE (from my copybook) to make it clear that its a pointer to an instance of a file.
                7. Use names that describe what is stored in the variable, not what data type the variable is. Imagine you had 5 files open, and you saw a reference to "fileptr", that wouldn't tell you which of the files it was referring to. If this is, for example the "assignment file", call it something like 'asnfile'. This is no different than an F-spec. You wouldn't name your F-spec file "FILEFSPEC", you'd name it something like "CUSTMAS" because its more important to know that it's the customer master file than it is that its declared on an F-spec.


                I might also add to use free-format coding... but that has little to do with text files, that's just a general RPG recommendation, and you're probably not using it for a reason, so I left it off the list.

                Code:
                     H dftactgrp(*no)
                
                      /copy STDIO_H
                
                     d crlf            c                   const(x'0d25')
                     d term            c                   const(x'00')
                     d asnfile         s                   like(pFILE)
                     d snddata         s          32767a   varying
                
                     d rvr_hold        s             10c   inz(*blanks)
                     d y               s             10i 0 inz(1)
                     d usr_array       s             10c   dim(1)
                     d ecr_array       s             10a   dim(1)
                     D seq_array       s              3p 0 dim(1)
                     D dsc_array       s             10a   dim(1)
                     D cnt_array       s              3p 0 dim(1)
                     D asn_array       s             10a   dim(1)
                      /free
                
                       y = 1;
                       usr_array(y) = 'Andy';
                       ecr_array(y) = 'ECR Data';
                       seq_array(y) = 101;
                       dsc_array(y) = 'XXX';
                       cnt_array(y) = 201;
                       asn_array(y) = 'Scott';
                
                       asnfile = fopen('assignments_file.txt': 'w,crln=N');
                       if asnfile = *null;
                          // handle error
                       endif;
                
                       if rvr_hold = *blank;
                          rvr_hold = usr_array(y);
                          fputs( 'Hi ' + %trim(%char(usr_array(y))) + ',' + CRLF
                               + 'There are open tasks that require your review:' + CRLF
                               : asnfile );
                       endif;
                
                       if rvr_hold = rvr_hold;
                          snddata = %trim(ecr_array(y)) + ', ' +
                                    %char(seq_array(y)) + ' - ' +
                                    %trim(dsc_array(y)) + ' has ' +
                                    %char(cnt_array(y)) +
                                    ' tasks open assigned to';
                         if asn_array(y) = *blank;
                            snddata += ' no current assignee' + CRLF;
                         else;
                            snddata += ' ' + %trim(asn_array(y)) + CRLF;
                         endif;
                         fputs(snddata: asnfile);
                       endif;
                
                       fclose(asnfile);
                
                       *inlr = *on;

                Comment

                Working...
                X