Remove the last char of a text file. - Forum - OpenEdge General - Progress Community

Remove the last char of a text file.

 Forum

Remove the last char of a text file.

This question is answered

is it possible to remove the last char of a text file ?
I am really struggling with this problem. Something like  sending a backspace "chr(08)" also "~b" won't help.
I dont want to read the whole file again and write a new one without the last char.

Verified Answer
  • In my opinion it's not possible to perform this action without overwriting the file.

    You're asking for a function that asks the file system to cut off the last character of a file. That operating-system command doesn't exist, I'm quite sure.

    What you'd have to do is read the file, remove the last character, then overwrite it.

    - copy-lob file to variable

    - update variable

    - copy-lob variable to file

All Replies
  • I don't think there's any way to do it in ABL without making a copy of the file. You will have to use operating system functions (via DLL or shared object calls) or execute a shell script or batch file. What operating system do you want to do this on? 

  • I use windows, "~n" for example worked fine but erase a char want do the right job

  • In my opinion it's not possible to perform this action without overwriting the file.

    You're asking for a function that asks the file system to cut off the last character of a file. That operating-system command doesn't exist, I'm quite sure.

    What you'd have to do is read the file, remove the last character, then overwrite it.

    - copy-lob file to variable

    - update variable

    - copy-lob variable to file

  • Hi Dominik,
     
    How about this?  Technically it does read the file but it works...
     
    DEFINE VARIABLE vSize AS INTEGER  NO-UNDO.
     
    FILE-INFO:FILE-NAME = "testfile".
    ASSIGN vSize = FILE-INFO:FILE-SIZE.
    COPY-LOB FROM FILE "testfile" FOR (vSize - 1) TO FILE "testfile.clipped".
     
    You may also be able to do this using the SEEK statement.
     
    Brian

  • On Windows you can do this programmatically with the SetEndOfFile Win32 API function. To use this function you would first open the file using CreateFile, seek to the position where you want to truncate the file using SetFilePointer, call SetEndOfFile to truncate the file, and then close the file with CloseHandle. You could call these functions using the external program interface (DLL calls) or create a DLL which does all of the work so you only have to make one DLL call from ABL.

    There may also be file system commands which can perform this action. Linux has a truncate command. There may be something similar on Windows.

  • @ Brian:

    I think the intention was to write to the same file (overwrite it), so combining your solution with mine would become:

    define variable vFileContents as longchar no-undo.

    copy-lob file "hello.txt" to vFileContents.

    copy-lob vTest for length(vFileContents) - 1 to file "hello.txt".

    @ Matt:

    Didn't know that, I guess it just depends on how much you want to avoid having to read the file in memory.

  • Hi Peter,
     
    You may need to use the optional parameter to LENGTH to tell it to count raw bytes instead of logical characters --> length(vFileontents,”RAW”) – 1
     
    Brian

  • If you are using a version of Windows which has the fsutil command you can do this using the following....
     
    fsutil file seteof testfile 15 (or whatever)
     
    so code to do this would be...
     
    file-info:file-name = “whatever”.
    os-command silent value(“fsutil file seteof “ + file-info:full-pathname + “ “ + string(file-info:file-size – 1).
     
    The one potential issue is that fsutil needs to run as administrator.  I tested it via a normal command prompt against a file I created and got an access error but running the command with a ‘run as administrator’ command prompt worked.
     
    Cheers, Brian

  • You're right. Depends on whether it's a text-file or not. As Dominic was talking about "backspace", I was assuming it's a text-file.

  • If you do it by reading the file and writing out the contents without the last character, you should write to a different file and then rename the new file to the original file name. Otherwise you risk losing the original file if something goes wrong.

    Read contents of original file into CLOB

    Write contents of file minus one character to tempfile

    Delete original file

    Rename tempfile to original file name

  • Thx for your replies, there are many good suggestions. I hoped i only dont know the right command. With "APPEND" i come straight to the last line, but cant remove something, that's a pity.

    At the moment my file is not so big, but with bigger files it would be horrible to copy paste the whole file only to remove one char

  • Here's code that uses SetEndOfFile to chop one byte off the end of a file.

    &GLOBAL-DEFINE GENERIC_WRITE 0x40000000
    &GLOBAL-DEFINE OPEN_EXISTING 3
    &GLOBAL-DEFINE FILE_ATTRIBUTE_NORMAL 0x80
    &GLOBAL-DEFINE FILE_END 2
    
    PROCEDURE CreateFileA EXTERNAL "kernel32":
        DEFINE INPUT PARAMETER lpFileName AS CHARACTER.
        DEFINE INPUT PARAMETER dwDesiredAccess AS LONG.
        DEFINE INPUT PARAMETER dwShareMode AS LONG.
        DEFINE INPUT PARAMETER lpSecurityAttributes AS LONG.
        DEFINE INPUT PARAMETER dwCreationDisposition AS LONG.
        DEFINE INPUT PARAMETER dwFlagsAndAttributes AS LONG.
        DEFINE INPUT PARAMETER hTemplateFile AS LONG.
        DEFINE RETURN PARAMETER ReturnValue AS LONG.
    END PROCEDURE.
    
    PROCEDURE CloseHandle EXTERNAL "kernel32" :
      DEFINE INPUT  PARAMETER hObject     AS LONG.
      DEFINE RETURN PARAMETER ReturnValue AS LONG.
    END PROCEDURE.
    
    PROCEDURE SetEndOfFile EXTERNAL "kernel32" :
      DEFINE INPUT  PARAMETER hObject     AS LONG.
      DEFINE RETURN PARAMETER ReturnValue AS LONG.
    END PROCEDURE.
    
    PROCEDURE SetFilePointer EXTERNAL "kernel32" :
      DEFINE INPUT  PARAMETER hObject     AS LONG.
      DEFINE INPUT  PARAMETER distanceToMove AS LONG.
    &IF {&PROCESS-ARCHITECTURE} = 64 &THEN
      DEFINE INPUT  PARAMETER distanceToMoveHigh AS INT64.
    &ELSE
      DEFINE INPUT  PARAMETER distanceToMoveHigh AS LONG.
    &ENDIF
      DEFINE INPUT  PARAMETER moveMethd   AS LONG.
      DEFINE RETURN PARAMETER ReturnValue AS LONG.
    END PROCEDURE.
    
    DEFINE VARIABLE lpSecurityAtt AS INTEGER NO-UNDO.
    DEFINE VARIABLE hObject AS INTEGER NO-UNDO.
    DEFINE VARIABLE nReturn AS INTEGER NO-UNDO.
    
    RUN CreateFileA (INPUT "file.txt", 
                     INPUT {&GENERIC_WRITE}, 
                     0, /* no sharing */
                     lpSecurityAtt, 
                     {&OPEN_EXISTING},
                     {&FILE_ATTRIBUTE_NORMAL},
                     0,
                     OUTPUT hObject).
                     
    IF hObject <> -1 THEN
    DO:
        RUN SetFilePointer(hObject, -1, 0, {&FILE_END}, OUTPUT nReturn).
        RUN SetEndOfFile(hObject, OUTPUT nReturn).
        RUN CloseHandle(hObject, OUTPUT nReturn).
    END.
    
  • Depending on your environment you could look at System.IO.FileStream e.g.

    USING System.IO.*.

    DEF VAR ofs AS System.IO.FileStream NO-UNDO.

    DEF VAR cFile AS CHAR NO-UNDO.

    cFile = "c:\temp\myfile.txt".

    ofs = NEW FileStream(cFile, FileMode:Open).

    IF ofs:CanSeek AND ofs:CanWrite THEN

    DO:

       ofs:SetLength(ofs:LENGTH - 1).

       ofs:Close().

    END.

  • on Linux and UNIX you can call the C function ftruncate () (or truncate () if file not open) to reduce the length of the file by 1 byte.

    that might accomplish what you want.