File Types and File Handling


Files, as a general rule, can be categorized into two basic types: binary and ASCII . Binary files are highly structured and organized files, the specifications for which are often governed by either a single company, as in the case of .PSD format used by Photoshop and owned by Adobe, or by a consortium of individuals and/or corporations, as in the case of the PNG graphic file format. Binary files are often the preferred type of file for commercial applications due to considerations of space and security. It is very important to note that many binary file format specifications are owned by companies that either forbid their use or require licensing fees from those who want to implement them, such as .GIF. And in some cases involving encrypted file formats, it is illegal in the United States to write software that circumvents these protections , as in the case of Adobe Acrobat files (.PDF). ASCII files are a simpler way of storing data, and due to their ability to be opened in any text editor, are often much more open to public use. Formats are often developed by a company and evolve into an open interchange format, as in the case of Wavefront Object files (.OBJ), or are designed by an individual or consortium to be an open standard from the very start, as in the case of .HTML. One obvious advantage of text files, especially in association with Maya and MEL, is their inherent openness and therefore cross-platform capabilities.

As binary files are a much more complex subject than is suitable for simple scripting, we will only deal with ASCII files in this chapter. MEL does provides the facility to read and write binary files, and those wanting or needing to do so are encouraged to pick up one of the many programming texts that cover file formats for their specifications.

Reading and writing files is, at minimum, a three-step process:

  1. Opening the file.

  2. Reading/writing the file.

  3. Closing the file.

Note that these steps are not dependent on the particular operation the user requested . When we read a file, we open it, read the data in, and then we close the file. When we write a file, we open it, write the data out, and then close the file.

To open a file we use the command fopen . When using fopen , we can use one of four possible options. We can open a file for writing, which destroys any information that previously might have been stored within the file. We can open a file to append data, which is to add data at the end of the file. We can also open the file for purposes of reading. Lastly, we can open the file for both reading and writing simultaneously . The default behavior of the fopen command is to open a file for writing. Note that even when adding information to a pre-existing file, it is best to completely rewrite the file. Appending files is most often used for log files. To close a file, we use the command fclose . This is a much simpler command. We just issue the command, with the file identifier, an integer returned by fopen we capture to a variable, most often called $fileID . The command used to write to a text file is fprint . For binary files, the command fwrite is used.

In Example 12.1, we create a simple one-line text file, using the three commands we have learned to this point.

Example 12.1: Creating a text file with MEL.

  // create a string naming the file.  Place it in   // the users temp directory.  string $fileName = (`internalVar -userTmpDir`                         + "file.txt") ;  // Open the file for writing and capture the file   // indentifier for later use  int $fileID = `fopen $fileName "w"`;  // write the following to a the file  fprint $fileID "Look, I can write a file!!";  // close out the file  fclose $fileID;     print ("Created " + $fileName + "\n"); 

After executing this code, open the resulting file in your text editor to see a single-line text file with the quoted text from the fprint command.

Just being limited to writing out explicit lines of text is slightly less than useful, turning Maya into an extremely expensive and hard-to-use word processor. Rather, we are able to pass the fprint command any variable, which it will then pass into text. By combining this with a for loop, we are able to easily write out large volumes of data. In Example 12.2, we see how a simple loop is used to write out a listing of the scene contents to a file.

Example 12.2: MEL code demonstrating a loop with the fprint command.

  // create a string naming the file.  Place it in   // the users temp directory.  string $fileName = (`internalVar -userTmpDir`                         + "file_02.txt") ;  // Gather scene data  string $sceneListing[] = `ls`;  // Open the file for writing and capture the file   // indentifier for later use  int $fileID = `fopen $fileName "w"`;  // write the name of each item in the array   // to a the file on its own line  for ($eachItem in $sceneListing)         fprint $fileID ($eachItem + "\n");  // close out the file  fclose $fileID;     print ("Created " + $fileName + "\n"); 

Now, if we open the text file we just created, we see a listing of each object in the scene, each on its own line due to the use of the new line escape character \n in the constructed string passed to the fprint command.

Now that we have files on disk, we would likely want to read them into Maya. Two commands are available to read data into Maya, fgetword and fgetline . The differences between the commands are subtle, but important when constructing code to parse read data.

The command fgetword reads the data in one word at a time, with word defined as the collection of characters separated by the character used in the argument of fgetword , by default a space. In Example 12.3, we create a file and then read it in one word at a time. If we then look at the file created by the first section of our example code in a text editor, we can see that each item is separated by a space.

Example 12.3: MEL code demonstrating how to read a file into memory.

  // create a string naming the file.  Place it in   // the users temp directory.  string $fileName = (`internalVar -userTmpDir`                         + "file_03.txt") ;  // Gather scene data  string $sceneListing[] = `ls`;  // Open the file for writing and capture the file   // indentifier for later use  int $fileID = `fopen $fileName "w"`;  // write the name of each item in the array   // to the file separated by " "  for ($eachItem in $sceneListing)         fprint $fileID ($eachItem + " ");  // close out the file  fclose $fileID;     print ("Created " + $fileName + "\n");  // Now read the file in word by word   // open the file  $fileID =`fopen $fileName "r"`;  // Get the first word of the file, and declare an   // array to hold the entirety of the file  string $word = `fgetword $fileID`;     string $fileContents[];  // continue as long as fgetword returns info  while (size($word) > 0)         {         $fileContents[`size $fileContents`] = $word;         $word = `fgetword $fileID`;         }  // close out the file  fclose $fileID;     print ("Contents of " + $fileName + "\n");     print $fileContents; 

While this makes the file slightly more difficult for our human eyes to read, Maya has no problems interpreting the file, differentiating the different words regardless of their contents, regarding only the whitespace. In fact, we could use any separator we want, but should not use the vertical pipe Maya uses for hierarchal name separation, , to avoid any possible confusion that could result.

The other command used to read ASCII files with MEL is fgetline . This command works in a similar fashion to fgetword , but rather than use whitespace or a defined separator to partition the data, fgetline reads in data until it encounters a new line character or reaches the end of file. In Example 12.4, we use the fgetline command, along with other string handling commands we have previously used, to keep information intelligently and logically handled.

Example 12.4: Code to organize file data as it is read into memory.

  // Create some sample data  string $emptyGroups[];     for ($i = 0; $i < 10; $i++)         {         $emptyGroups[$i] = `group empty`;         setAttr ($emptyGroups[$i] + ".translateX")             `rand 10 10`;         setAttr ($emptyGroups[$i] + ".translateY")             `rand 10 10`;         setAttr ($emptyGroups[$i] + ".translateZ")             `rand 10 10`;         }  // create a string naming the file.  Place it in   // the users temp directory.  string $fileName = (`internalVar -userTmpDir`                         + "file_04.txt") ;  // Open the file for writing and capture the file   // indentifier for later use  int $fileID = `fopen $fileName "w"`;  // write the name of each item in the array   // to the file followed by its position  for ($eachItem in $emptyGroups)         {         fprint $fileID ($eachItem + " ");         float $tfa[] = `getAttr  ($eachItem + ".translate")`;          fprint $fileID ($tfa[0] + " ");         fprint $fileID ($tfa[1] + " ");         fprint $fileID ($tfa[2] + "\n");         }  // close out the file  fclose $fileID;     print ("Created " + $fileName + "\n");  // Now read the file in line by line  // open the file     $fileID = `fopen $fileName "r"`;  // Get the first line of the file, and declare an   // array to hold the entirety of the file  string $line = `fgetline $fileID`;     string $fileContents[];  // continue as long as fgetline returns info  while (size($line) > 0)         {         $fileContents[`size $fileContents`] = $line;         $line = `fgetline $fileID`;         }  // close out the file  fclose $fileID;  // parse the read data, and organize it  for ($eachItem in $fileContents)         {         string $tokenBuffer[[];         tokenize $eachItem $tokenBuffer;         print ("Object: " + $tokenBuffer[0] + "\n");          print ("\tTranslate X: " + $tokenBuffer[1] + "\n");         print ("\tTranslate Y: " + $tokenBuffer[1] + "\n");         print ("\tTranslate Z: " + $tokenBuffer[1] + "\n");         } 

Because we have control over the formatting the files we are creating, we are able to easily parse the resulting data. Other common text file formats, such as Wavefront Object or Acclaim Motion Capture, have their own specifications, although these are often easily found within the documentation of the associated software or on the Internet.

When creating custom file formats, it is good practice to both document the format and to construct the format to allow for expansion. A robust format, such as the Renderman Standard, has shown through its over 15 years of existence that an open and robust format can accept the ever-evolving technology that defines modern 3D graphics. Although we used only fgetword and fgetline in our handling of reading in text files, be aware that the command fread exists to read in binary files, much the same way the command fwrite is used to write binary files.

In the previous examples, we used the size command to check for the end of the file. There is, however, a command specifically meant for this purpose, feof . By using the return value from feof , a Boolean integer value, in a conditional statement, we create a more elegant way of reading until the end of file. In Example 12.5, we use the feof command as the condition in the while statement used to read in this file.

On the CD  

Example 12.5 references the file created by Example 12.4. If this file has been deleted, recreate it by rerunning the code in Example 12.4.

Example 12.5: Using the more reliable eof command to aid in the reading of a file.

  // create a string naming the file.  string $fileName = (`internalVar -userTmpDir`                         + "file_04.txt") ;  // open the file  $fileID = `fopen $fileName "r"`;  // Get the first line of the file, and declare an   // array to hold the entirety of the file  string $line = `fgetline $fileID`;     string $fileContents[];  // continue as long as fgetline returns info  while (`feof $fileID` == 0)         {         $fileContents[`size $fileContents`] = $line;         $line = `fgetline $fileID`;         }  // close out the file  fclose $fileID;  // parse the read data, and organize it  for ($eachItem in $fileContents)         {         string $tokenBuffer[[];         tokenize $eachItem $tokenBuffer;         print ("Object: " + $tokenBuffer[0] + "\n");          print ("\tTranslate X: " + $tokenBuffer[1] + "\n");         print ("\tTranslate Y: " + $tokenBuffer[1] + "\n");         print ("\tTranslate Z: " + $tokenBuffer[1] + "\n");         } 

This produces a behavior identical to that using the size command, but only stops at the end of the file, rather than when the returned line is empty, which can happen before the true end of file is reached.




The MEL Companion
The MEL Companion: Maya Scripting for 3D Artists (Charles River Media Graphics)
ISBN: 1584502754
EAN: 2147483647
Year: 2003
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net