Text File to an Array?



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 09:15, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R12 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    Hi guys,
    I need some advice on BaseFile vs. Hyperfile.

    I'm trying to read text from a text file. Then store each line of text in an array.
    The text in the text file will be listed line-by-line. Think of a list people's last names.

    After reading through the archives about how C4D handles text files. I'm finding myself confused on which class is better suited for something like this.
    I've already tried using a CHAR method which gets each character in the text file and stores it in a string variable. And then I put that string in a string array.
    But now I'm stuck how to "split" that single string element into individual array elements.
    I don't see an array split option in the C++ SDK.

    Example:

        AutoAlloc<BaseFile> bf;  
      if(!bf) return FALSE;  
      Filename file = GeGetC4DPath(C4D_PATH_DESKTOP)+Filename("test.txt");      
      if(!bf->Open(file, FILEOPEN_READ, FILEDIALOG_ANY, BYTEORDER_INTEL, MACTYPE_CINEMA, MACCREATOR_CINEMA)) return FALSE;  
      
      String line = "";  
      LONG fileLength = bf->GetLength();  //Counts how may characters are in the text file  
      CHAR c[2];  
      c[1] = 0;  
      CHAR *pc = &c[0];  
      for (LONG i = 0L; i != fileLength; ++i) //loop once for each character in the text file  
      {  
        bf->ReadChar(pc);          //Line read from the file  
        line += String(pc);        //Get the next character  
      }  
       
      bf->Close();  
      
      GeDynamicArray<String>textarray(0);   //Create the new string type array with 0 blank elements      
      textarray.Push(line);                 //Now I have a string array with all the text in the first array element  
                                            //How do I split the text into individual array elements?
    

    Am I better off using the hyperfile class to do this?
    I've never used that class and I'm finding it a bit confusing to figure out. Because it requires a BaseContainer. And that sounds like the wrong way to go.
    But I'm not sure.

    -ScottA

    P.S.- Why doesn't the SDK have a getline() function for reading text files in it?
            That would probably make doing this kind of thing a whole lot easier.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 09:48, xxxxxxxx wrote:

    If I remember correctly there was the MorphMixer object example in the SDK (not sure if it's still there, too long I didn't look in there) that showed how to handle file reading with the SDK and it also contained a ReadLine function. Check it out and learn.

    Get used to write your own stuff. The deeper you get into programming (not just c4d) the more complex situations you need to handle yourself.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 11:25, xxxxxxxx wrote:

    Thanks.
    I didn't know that code was there. I think I can alter that code example to do what I'm looking for.

    I can read the first line of text with it and push it into the array:

    static String ReadLine(BaseFile *bf, String *v)  
    {  
      CHAR ch,line[1024];  
      LONG len = bf->TryReadBytes(&ch, 1);  
      
      LONG i = 0;  
      while (i<1024 && len == 1 && ch != '\n' && ch != '\r')   
      {  
      line[i++] = ch;  
      len = bf->TryReadBytes(&ch, 1);          
      }  
      
      #ifdef __PC  
      if (ch == '\r')   
      {  
          len = bf->TryReadBytes(&ch, 1);  
          if (len == 1 && ch != '\n') bf->Seek(-1);  
      }  
      #endif  
      
      v->SetCString(line, i);  
      String copy = v->GetCStringCopy(STRINGENCODING_8BIT); //Strip out the C4D encoding gibberish stuff  
      return copy;  
    }  
      
      
    Bool myDialog::InitValues(void)  
    {  
      AutoAlloc<BaseFile> bf;  
      if(!bf) return FALSE;  
      Filename file = GeGetC4DPath(C4D_PATH_DESKTOP)+Filename("test.txt");      
      if(!bf->Open(file, FILEOPEN_READ, FILEDIALOG_ANY, BYTEORDER_INTEL, MACTYPE_CINEMA, MACCREATOR_CINEMA)) return FALSE;  
      
      String line = "";  
      String textPerLine = ReadLine(bf, &line);  
      
      GeDynamicArray<String>textarray(0);       //Create the new string type array with 0 blank elements  
      textarray.Push(textPerLine);              //Fills the array with the first line of text  
      GePrint(textarray[0]);  
      
      return TRUE;  
    }
    

    That gets me pretty close.
    I just need to figure out how to get the next lines of text in a loop. So I can push each line into the textarray.

    -ScottA



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 12:47, xxxxxxxx wrote:

    I think I've got it.

    Here's what I came up with:

    static String ReadLine(BaseFile *bf, String *v)  
    {  
      CHAR ch,line[1024];  
      LONG len = bf->TryReadBytes(&ch, 1);  
      
      LONG i = 0;  
      while (i<1024 && len == 1 && ch != '\n' && ch != '\r')   
      {  
      line[i++] = ch;  
      len = bf->TryReadBytes(&ch, 1);          
      }  
      
      v->SetCString(line, i);  
      String copy = v->GetCStringCopy(STRINGENCODING_8BIT); //Strip out the encoding gibberish stuff  
      return copy;  
    }  
      
      
    .......  
      
      
    Bool myDialog::InitValues(void)  
    {  
      GeDynamicArray<String>textarray(0);     //Create the new string type array with 0 blank elements  
      
      AutoAlloc<BaseFile> bf;  
      if(!bf) return FALSE;  
      Filename file = GeGetC4DPath(C4D_PATH_DESKTOP)+Filename("test.txt");      
      if(!bf->Open(file, FILEOPEN_READ, FILEDIALOG_ANY, BYTEORDER_INTEL, MACTYPE_CINEMA, MACCREATOR_CINEMA)) return FALSE;  
      
      String line = "";  
      LONG fileLength = bf->GetLength();  
      
      for (LONG i = 0L; i != fileLength; ++i)  
      {  
          bf->Seek(i, FILESEEK_RELATIVE);  
          String textPerLine = ReadLine(bf, &line);  
          textarray.Push(textPerLine);  
          GePrint(textarray[i]);  
      }  
      
      textarray.~GeDynamicArray();    //Destructor to free any memory used by the array  
      
      return TRUE;  
    }
    

    It seems to be working good so far.

    -ScottA

    Edit:
    Doh! I spoke too soon.
    It's removing the first characters for each new line. Starting with the third line.
    Still looking for the proper solution.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 16:53, xxxxxxxx wrote:

    You're using i for both character seeking through the file and index into the textarray.  That is bound to create odd behavior.  Send a variable pointer to ReadLine() which gets the number of characters read and use that in your bf->Seek().  You may also want to add code to handle the case where count may be set to 0 by ReadLine().

    static String ReadLine(BaseFile *bf, String *v, LONG* count)  
    {  
      CHAR ch,line[1024];  
      LONG len = bf->TryReadBytes(&ch, 1);  
      
      LONG i = 0;  
      while (i<1024 && len == 1 && ch != '\n' && ch != '\r')   
      {  
      line[i++] = ch;  
      len = bf->TryReadBytes(&ch, 1);          
      }  
      
      *count =            i;  
      v->SetCString(line, i);  
      String copy = v->GetCStringCopy(STRINGENCODING_8BIT); //Strip out the encoding gibberish stuff  
      return copy;  
    }  
      
    Bool myDialog::InitValues(void)  
    {  
      GeDynamicArray<String>textarray(0);     //Create the new string type array with 0 blank elements  
      
      AutoAlloc<BaseFile> bf;  
      if(!bf) return FALSE;  
      Filename file = GeGetC4DPath(C4D_PATH_DESKTOP)+Filename("test.txt");      
      if(!bf->Open(file, FILEOPEN_READ, FILEDIALOG_ANY, BYTEORDER_INTEL, MACTYPE_CINEMA, MACCREATOR_CINEMA)) return FALSE;  
      
      String line = "";  
      LONG fileLength = bf->GetLength();  
      LONG count = 0L;  
      
      for (LONG i = 0L; i != fileLength; ++i)  
      {  
          bf->Seek(count, FILESEEK_RELATIVE);  
          String textPerLine = ReadLine(bf, &line, &count);  
          textarray.Push(textPerLine);  
          GePrint(textarray[i]);  
      }  
      
      textarray.~GeDynamicArray();    //Destructor to free any memory used by the array  
      
      return TRUE;  
    }
    


  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 16:56, xxxxxxxx wrote:

    Also note that TryReadBytes() may be updating the file pointer to the next character to read so that bf->Seek() is unnecessary.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 17:57, xxxxxxxx wrote:

    Thanks a lot Robert.
    That works much better.Beer

    I removed the Seek() stuff because it ended up not being useful.

    The only bad thing I'm seeing now is that the array is being populated with empty elements.
    It looks like the array is inserting a blank element every other index.
    I don't think the code I ripped from the Morph Mixer was intended to be used like I'm using it.

    I think I can still use it this way if nobody posts a more efficient solution.

    Thanks for the help,
    -ScottA



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 18:15, xxxxxxxx wrote:

    You just need to eat the/any additional line-endings...

      
    static String ReadLine(BaseFile *bf, String *v, LONG* count)  
    {  
      CHAR ch,line[1024];  
      LONG len = bf->TryReadBytes(&ch, 1);  
      
      // eat any line-ending characters (potentially left over from previous call ,but also potentially as first line of file)...  
      while (len == 1 && ch == '\n' || ch == '\r')  
      {  
            len = bf->TryReadBytes(&ch, 1);  
      }  
      
      LONG i = 0;  
      while (i<1024 && len == 1 && ch != '\n' && ch != '\r')  
      {  
          line[i++] = ch;  
          len = bf->TryReadBytes(&ch, 1);  
      }  
      
      *count =            i;  
      v->SetCString(line, i);  
      String copy = v->GetCStringCopy(STRINGENCODING_8BIT); //Strip out the encoding gibberish stuff  
      return copy;  
    }  
    


  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 18:19, xxxxxxxx wrote:

    Grrrr.. why does the editor add extra lines?

    EDIT: Ahh - I did not have the WYSIWYG editor enabled in my profile (!).



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/06/2012 at 18:35, xxxxxxxx wrote:

    That's great!
    Thanks Giblet. 🍺

    I think the forum spaces out code copied and pasted directly from VS.
    I usually remove all the tabs from my code and insert spaces in a .txt file before posting it here.
    That usually keeps it nice and tight.

    Nice teamwork guys.
    Thanks again for the help. And have a good night.

    -ScottA



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/06/2012 at 16:43, xxxxxxxx wrote:

    UPDATE:

    I was having memory leaks with my array using the code posted. And I tracked down the problem to the GetCStringCopy function I was using in the ReadLine() method.
    Apparently that was causing the array to not free it's memory. And it turns out that I didn't need it in there anyway.

    Here's the updated ReadLine() method I'm using now:

    static String ReadLine(BaseFile *bf, String *v, LONG* count)  
    {  
      CHAR ch,line[1024];  
      LONG len = bf->TryReadBytes(&ch, 1);  
      
      //This will delete any line-ending characters (potentially left over from previous call ,but also potentially as first line of file)...  
      while (len == 1 && ch == '\n' || ch == '\r')  
      {  
      len = bf->TryReadBytes(&ch, 1);         
      }  
      
      LONG i = 0;  
      while (i<1024 && len == 1 && ch != '\n' && ch != '\r')  
      {  
      line[i++] = ch;  
      len = bf->TryReadBytes(&ch, 1);         
      }  
      
      *count = i;  
      v->SetCString(line, i);  
      String result = *v;  
      return result;  
    }
    

    This appears to have cured my array memory leak problems.

    -ScottA


Log in to reply