How to search/replace string in text-file?

Luc_VanDyckLuc_VanDyck Member, Moderator, Administrator Posts: 3,633
I have a text-file where I need to replace a given string to something else. The problem is that the lines in this text-file are > 1024 characters, so I can't use recFile.READ(txtLine) to read this file line by line.

Is it possible with STREAMS to read this file, search for the string, replace it with another value, and write it back to the file? Can someone post some C/AL Code please?
No support using PM or e-mail - Please use this forum. BC TechDays 2024: 13 & 14 June 2024, Antwerp (Belgium)

Comments

  • DigiTecKidDigiTecKid Member Posts: 46
    Here's code I've used to read thru a file looking for a value using streams:

    Name DataType Length
    FTPUploadLog File
    FTPUpLoadLogIStream InStream
    FTPUploadLogTextLine Text 1024

    //Open the upload log to check the file upload status
    FTPUploadLog.TEXTMODE(TRUE);
    IF NOT FTPUploadLog.OPEN(EDITradingPartnerDoc."Document Folder" + 'FTPUploadLog.txt') THEN BEGIN
    FOR i := 1 TO 10 DO BEGIN
    SLEEP(5000);
    IF FTPUploadLog.OPEN(EDITradingPartnerDoc."Document Folder" + 'FTPUploadLog.txt') THEN
    i := 11;
    END;
    END;
    FTPUploadLog.CREATEINSTREAM(FTPUpLoadLogIStream);
    UploadError := TRUE;
    IF FTPUploadLog.LEN > 0 THEN
    REPEAT
    IF FTPUpLoadLogIStream.READTEXT(FTPUploadLogTextLine) > 0 THEN BEGIN
    FTPLog.WRITE(FTPUploadLogTextLine);
    IF COPYSTR(FTPUploadLogTextLine, 1, 3) = '226' THEN
    UploadError := FALSE;
    END;
    UNTIL FTPUpLoadLogIStream.EOS;
  • Luc_VanDyckLuc_VanDyck Member, Moderator, Administrator Posts: 3,633
    You are still using a variable FTPUploadLogTextLine with length of 1024 to parse your text-file. What happens if the value '226' you are looking for, occurs at position 1023, 1024 of Line 1 and on position 1 of Line 2?
    No support using PM or e-mail - Please use this forum. BC TechDays 2024: 13 & 14 June 2024, Antwerp (Belgium)
  • DigiTecKidDigiTecKid Member Posts: 46
    I'm coding a test scenario for that now. I'll post it in a few minutes. I believe I can just continue to read 1024 characters until the end of the file, replacing the string I'm looking for with whatever I need it to be.
    If it occurs more than once in a line (a "line" is a text string ending in CRLF) you want each occurace replaced correct?
  • Luc_VanDyckLuc_VanDyck Member, Moderator, Administrator Posts: 3,633
    It could occur multiple times in the text-file. The text-fle is saved from a XML DOM object. Don't know if CRLF is used in this file.
    No support using PM or e-mail - Please use this forum. BC TechDays 2024: 13 & 14 June 2024, Antwerp (Belgium)
  • jreynoldsjreynolds Member Posts: 175
    Can you use a BigText variable to get past the 1024 length limit?
  • DaveTDaveT Member Posts: 1,039
    Hi Luc,

    If you have a working routine developed - why not just call it twice with the second call using a seek of let say 500 to capture the strings on the 1023 - 1025 overlap.
    Dave Treanor

    Dynamics Nav Add-ons
    http://www.simplydynamics.ie/Addons.html
  • ReinhardReinhard Member Posts: 249
    here's a function to return the position of a string in a text file:
    variables:
    wordInt, posInt,toReturns (Integers)
    char (Text len 1)
    fiel File
    inStream inStream
    function indexOf(searchStr (Text),targetFile (Text)) returns int
    
    wordInt := 1;
    posInt := 0;
    
    toReturn := -1;
    
    file.OPEN(targetFile);
    file.CREATEINSTREAM(inStream);
    
    WHILE NOT inStream.EOS DO
    BEGIN
    
      inStream.READTEXT(char,1);
    IF NOT (char = '') THEN  // this skips over line breaks
    BEGIN
      IF FORMAT(searchStr[wordInt]) = char THEN
      BEGIN
        IF toReturn < 0 THEN toReturn := posInt;
        wordInt += 1;
      END
      ELSE
      BEGIN
        toReturn := -1;
        wordInt := 1;
      END;
    
      IF wordInt = STRLEN(searchStr) + 1 THEN
      BEGIN
        file.CLOSE;
        EXIT(toReturn);
      END;
    
      posInt += 1;
    END;
    END;
    
    file.CLOSE;
    EXIT(-1);
    


    replacing the string however seems more challenging...
  • DigiTecKidDigiTecKid Member Posts: 46
    Sorry I couldn't get this posted yesterday before I had to pick my son up from school... :) I've included the text file I created for testing as well.
    This needs to be re-written it's very crude, but it has my idea. Plus I'm getting some strange character written in textfile2 - 00h?
    I like jreynolds' idea of pulling it into a bigtext and operating on it. When I get more time I'm going to have to investigate that route.
    Variables
    *****
    TextFile1@1000000003 : File;
    TextFile2@1000000002 : File;
    TextFile1InStream@1000000001 : InStream;
    TextFile2OutStream@1000000009 : OutStream;
    ReadTextLineNew@1000000000 : Text[1024];
    ReadTextLineOld@1000000008 : Text[1024];
    TextToFind@1000000006 : Text[120];
    ReplacementText@1000000011 : Text[240];
    CreatedTextLine@1000000010 : Text[120];
    *****
    
    TextToFind := 'Text Being Sought';
    ReplacementText := 'Text Has Been Replaced';
    
    //File to read from...
    TextFile1.TEXTMODE(TRUE);
    TextFile1.OPEN('C:\TEMP\TextFile1.txt');
    TextFile1.CREATEINSTREAM(TextFile1InStream);
    
    //File to write to...
    TextFile2.TEXTMODE(TRUE);
    TextFile2.WRITEMODE(TRUE);
    TextFile2.CREATE('C:\TEMP\TextFile2.txt');
    TextFile2.CREATEOUTSTREAM(TextFile2OutStream);
    
    REPEAT
      ReadTextLineOld := ReadTextLineNew;
                                                 //I'm trying to account for replacing a shorter text string
                                                 //with a longer text string so I'm reading in less characters
                                                 //than the string can actually handle
      IF TextFile1InStream.READ(ReadTextLineNew, MAXSTRLEN(ReadTextLineNew) - STRLEN(TextToFind)) > 0 THEN BEGIN
    
        IF STRPOS(ReadTextLineNew, TextToFind) > 0 THEN BEGIN
    
          IF STRPOS(ReadTextLineNew, TextToFind) = 1 THEN
            ReadTextLineNew := ReplacementText +
                               COPYSTR(ReadTextLineNew, STRLEN(TextToFind) + 1)
          ELSE
            ReadTextLineNew := COPYSTR(ReadTextLineNew, 1, STRPOS(ReadTextLineNew, TextToFind) - 1) +
                               ReplacementText +
                               COPYSTR(ReadTextLineNew, STRPOS(ReadTextLineNew, TextToFind) + STRLEN(TextToFind));
    
        END;
    
        IF STRLEN(ReadTextLineOld) > 0 THEN BEGIN
          IF STRLEN(ReadTextLineOld) > (STRLEN(ReadTextLineOld) - STRLEN(TextToFind)) THEN BEGIN
    
                      //Create a string from the end of the old string and the beginning of
                      //the new string
            IF STRPOS(COPYSTR(ReadTextLineOld, STRLEN(ReadTextLineOld) - STRLEN(TextToFind)) +
                      COPYSTR(ReadTextLineNew, 1, STRLEN(TextToFind)), TextToFind) > 0 THEN BEGIN
               ReadTextLineOld := COPYSTR(ReadTextLineOld, 1, STRLEN(ReadTextLineOld) - STRLEN(TextToFind) - 1);
               ReadTextLineNew := COPYSTR(ReadTextLineNew, STRLEN(TextToFind) + 1);
               CreatedTextLine := COPYSTR(ReadTextLineOld, STRLEN(ReadTextLineOld) - STRLEN(TextToFind)) +
                                 COPYSTR(ReadTextLineNew, 1, STRLEN(TextToFind));
              IF STRPOS(CreatedTextLine, TextToFind) = 1 THEN
    
                CreatedTextLine := ReplacementText +
                                   COPYSTR(CreatedTextLine, STRLEN(TextToFind) + 1)
              ELSE
                CreatedTextLine := COPYSTR(CreatedTextLine, 1, STRPOS(CreatedTextLine, TextToFind) - 1) +
                                   ReplacementText +
                                   COPYSTR(CreatedTextLine, STRPOS(CreatedTextLine, TextToFind) + STRLEN(TextToFind));
            END ELSE
              CreatedTextLine := '';
    
          END ELSE BEGIN
          END;
    
          TextFile2OutStream.WRITE(ReadTextLineOld);
          IF STRLEN(CreatedTextLine) > 0 THEN
            TextFile2OutStream.WRITE(CreatedTextLine);
    
        END;
    
      END;
    
    UNTIL TextFile1InStream.EOS;
    TextFile2OutStream.WRITE(ReadTextLineNew);
    TextFile1.CLOSE;
    TextFile2.CLOSE;
    
    This is the test file I created...
    1234567890123456789012345678901234567890123456789012345678901234567890123ISA*00*          *00*          *ZZ*TEST BASIC     *01*060018611      *01 0509*1441*U*00401*000000091*0*T*>~GS*PO*TEST BASIC*060018611*20010509*144 1*000000005*X*004010VICS~ST*850*0001~BEG*00*SA*643787**20011020~REF*DP*087~REF*MR*TRYUE~FOB*CC*OR*ORIGIN~CSH*Y~ITD*05******60~DTM*001*20011219~DTM*002*20011204~DTM*010*20011119~DTM*015*20011211~TD5*B***H*SEE PARTNER ROUTING REQUIREMENT BOOK~N9*AH*PO MESSAGES~MSG*THIS IS AN EDI TEST PO, PLEASE DO NOT FILL~N1*BT*XYZ GROUP, INC*92*0069595550000~N3*192 MARKET Text Being SoughtSQUARE~N4*ATLANTA*GA*30117~N1*ST*XYZ GROUP, INC - ATLANTA*9*69590093~N3*450 PEACHTREE STREET~N4*ATLANTA*GA*30312~PO1**25*EA*29.4**UP*072495005099*VC*0509-6*CB*759873~PID*F*08***OVAL ROASTER         18"X12 1/4"X7 1/2" SPCKLD BLK~PO4*6~PO1**25*EA*20.58**UP*072495061064*VC*6106-6*CB*759874~PID*F*08***OVAL ROASTER          13"X8"X5"         SPKLD BLK~PO4*6~CTT*2~SE*1*0001~GE*1*000000001~IEA*1*000000001~ISA*00*          *00*Text Being SoughtT BASIC     *01*060018611      *01 0509*1441*U*00401*000000091*0*T*>~GS*PO*TEST BASIC*060018611*20010509*144 1*000000005*X*004010V
    002*20011204~DTM*010*20011119~DTM*015*20011211~TD5*B***H*SEE PARTNER ROUTING REQUIREMENT BOOK~N9*AH*PO MESSAGES~MSG*THIS IS AN EDI TEST PO, PLEASE DO NOT FILL~N1*BT*XYZ GROUP, INC*92*0069595550000~N3*192 MARKET Text Being SoughtSQUARE~N4*ATLANTA*GA*30117~N1*ST*XYZ GROUP, INC - ATLANTA*9*69590093~N3*450 PEACHTREE STREET~N4*ATLANTA*GA*30312~ ISA*00*          *00*          *ZZ*TEST BASIC     *01*060018611      *01 0509*1441*U*00401*000000091*0*T*>~GS*PO*TEST BASIC*060018611*20010509*144 1*000000005*X*004010VICS~ST*850*0001~BEG*00*SA*643787**20011020~REF*DP*087~REF*MR*TRYUE~FOB*CC*OR*ORIGIN~CSH*Y~ITD*05******60~DTM*001*20011219~DTM*002*20011204~DTM*010*20011119~DTM*015*20011211~TD5*B***H*SEE PARTNER ROUTING REQUIREMENT BOOK~N9*AH*PO MESSAGES~MSG*THIS IS AN EDI TEST PO, PLEASE DO NOT FILL~N1*BT*XYZ GROUP, INC*92*006993484575595550000~N3*192 MARKET Text Being SoughtSQUARE~N4*ATLANTA*GA*30117~N1*ST*XYZ GROUP, INC - ATLANTA*9*69590093~N3*450 PEACHTREE STREET~N4*ATLANTA*GA*30312~PO1**25*EA*29.4**UP*072495005099*VC*0509-Text Being Sought ATLANTA*9*69590093~N3*450 PEACHTREE STREET~N4*ATLANTA*GA*30312~ ISA*00*          *00*          *ZZ*TEST BASIC     *01*060018611      *01 0509*1441*U*00401*000000091*0*T*>~GS*PO*TEST BASIC*060018611*20010509*144 1*000000005*X*004010VICS~ST*850*0001~BEG*00*SA*643787**20011020~REF*DP*087~REF*MR*TRYUE~FOB*CC*OR*ORIGIN~CSH*Y~ITD*05******60~DTM*001*20011219~DTM*002*20011204~DTM*010*20011119~DTM*015*20011211~TD5*B***H*SEE PARTNER ROUTING REQUIREMENT BOOK~N9*AH*PO MESSAGES~MSG*THIS IS AN EDI TEST PO, PLEASE DO NOT FILL~N1*BT*XYZ GROUP, INC*92*0069595550000~N3*192 MARKET Text Being SoughtSQUARE~N4*ATLANTA*GA*30117~N1*ST*XYZ GROUP, INC - ATLANTA*9*69590093~N3*450 PEACHTREE
    
  • jreynoldsjreynolds Member Posts: 175
    The following code streams a file into a BigText variable (InputText), finds and replaces all occurrences of one text string with another moving the text into a second BigText variable (OutputText), and finally streams that BigText variable out to another file.
    PROCEDURE TextReplace@37002000(SourceFileName@37002001 : Text[250];DestinationFileName@37002002 : Text[250];TextToFind@37002003 : Text[250];TextToReplace@37002004 : Text[250]);
        VAR
          TextFile@37002006 : File;
          InStream@37002007 : InStream;
          OutStream@37002008 : OutStream;
          InputText@37002000 : BigText;
          OutputText@37002005 : BigText;
          SubText@37002010 : BigText;
          TextPosition@37002009 : Integer;
        BEGIN
          TextFile.TEXTMODE(FALSE);
          TextFile.OPEN(SourceFileName);
          TextFile.CREATEINSTREAM(InStream);
          InputText.READ(InStream);
          TextFile.CLOSE;
    
          TextPosition := InputText.TEXTPOS(TextToFind);
          WHILE TextPosition <> 0 DO BEGIN
            InputText.GETSUBTEXT(SubText,1,TextPosition - 1);
            OutputText.ADDTEXT(SubText);
            OutputText.ADDTEXT(TextToReplace);
            InputText.GETSUBTEXT(InputText,TextPosition + STRLEN(TextToFind));
            TextPosition := InputText.TEXTPOS(TextToFind);
          END;
          OutputText.ADDTEXT(InputText);
    
          TextFile.TEXTMODE(FALSE);
          TextFile.CREATE(DestinationFileName);
          TextFile.CREATEOUTSTREAM(OutStream);
          OutputText.WRITE(OutStream);
          TextFile.CLOSE;
        END;
    
  • ReinhardReinhard Member Posts: 249
    jreynolds... good point! to work with big text, use a BigText variable! ](*,) =D>
  • DigiTecKidDigiTecKid Member Posts: 46
    jreynolds,

    Genius function! Using the BigText variables is elegant and works like a charm. I had no idea the BigText variable had that functionality.
  • cunnycunny Member Posts: 129
    This is really nice :). But how to replace it with Tab to make it Tab-Delimited :roll: ?
    cunny Lee
    MCP - MBS Navision
    jle@naviworld.com
  • DaveTDaveT Member Posts: 1,039
    Hi Cunny,

    use

    charTab := 9;

    FORMAT(charTab);
    Dave Treanor

    Dynamics Nav Add-ons
    http://www.simplydynamics.ie/Addons.html
  • LewisTiogaLewisTioga Member Posts: 40
    edited 2017-07-19
    Ignore
Sign In or Register to comment.