Max 5 API Reference
00001 /** 00002 @page chapter_files File Handling 00003 00004 Max contains a cross-platform set of routines for handling files. These routines permit you to search for files, show file open and save dialogs, as well as open, read, write, and close them. The file API is based around a "path identifier" -- a number that describes the location of a file. When searching or reading a file, path identifiers can be either a folders or collectives. Path identifiers that are negative (or zero) describe actual folders in the computer's file system, while path identifiers that are positive refer to collectives. 00005 00006 A basic thing you might want to do make your object accept the read message in a manner similar to existing Max objects. If the word read is followed by no arguments, a file dialog appears for the user to choose a file. If read is followed by an argument, your object will search for the file. If a file is found (or chosen), your object will open it and read data from it. 00007 00008 First, make your object accept the read message. The simplest way to make the filename argument optional is to use the #A_DEFSYM argument type specifier. When the symbol argument is not present, Max passes your method the empty symbol. 00009 00010 @code 00011 class_addmethod(c, (method)myobject_read, "read", A_DEFSYM, 0); 00012 @endcode 00013 00014 The next requirement for any method that reads files is that it must defer execution to the low-priority thread, as shown in the following implementation, where the filename argument is passed as the symbol argument to defer. 00015 00016 @code 00017 void myobject_read(t_myobject *x, t_symbol *s) 00018 { 00019 defer(x, (method)myobject_doread, s, 0, NULL); 00020 } 00021 @endcode 00022 00023 The myobject_doread() function compares the filename argument with the empty symbol -- if the argument was not supplied, the open_dialog() is used, otherwise, we call locatefile_extended() to search for the file. This object looks for text files, so we use a four-character code 'TEXT' as our file type to either open or locate. File type codes define a set of acceptable extensions. The file max-fileformats.txt permits contains standard definitions, and you can add your own by creating a similar text file and placing it in the init folder inside the Cycling '74 folder. 00024 00025 @code 00026 void myobject_doread(t_myobject *x, t_symbol *s) 00027 { 00028 long filetype = 'TEXT', outtype; 00029 short numtypes = 1; 00030 char filename[512]; 00031 short path; 00032 00033 if (s == gensym("")) { // if no argument supplied, ask for file 00034 if (open_dialog(filename, &path, &outtype, &filetype, 1)) // non-zero: user cancelled 00035 return; 00036 } else { 00037 strcpy(filename, s->s_name); // must copy symbol before calling locatefile_extended 00038 if (locatefile_extended(filename, &path, &outtype, &filetype, 1)) { // non-zero: not found 00039 object_error(x, "%s: not found", s->s_name); 00040 return; 00041 } 00042 } 00043 // we have a file 00044 myobject_openfile(x, filename, path); 00045 } 00046 @endcode 00047 00048 To open and read files, you can use the cross-platform sysfile API. Files can be opened using a filename plus path identifier. If successfully opened, the file can be accessed using a #t_filehandle. Note that "files" inside collective files are treated identically to regular files, with the exception that they are read-only. 00049 00050 00051 @section chapter_files_reading_text_files Reading Text Files 00052 00053 First, we'll implement reading the text file whose name and path identifier are passed to myobject_openfile() using a high-level routine sysfile_readtextfile() specifically for reading text files that handles text encoding conversion for you. If you are reading text files, using this routine is strongly recommended since converting text encodings is unpleasant to say the least. 00054 00055 @code 00056 void myobject_openfile(t_myobject *x, char *filename, short path) 00057 { 00058 t_filehandle fh; 00059 char **texthandle; 00060 00061 if (path_opensysfile(filename, path, &fh, READ_PERM)) { 00062 object_error(x, "error opening %s", filename); 00063 return 00064 } 00065 // allocate some empty memory to receive text 00066 texthandle = sysmem_newhandle(0); 00067 sysfile_readtextfile(fh, texthandle, 0, 0); // see flags explanation below 00068 post("the file has %ld characters",sysmem_gethandlesize(texthandle)); 00069 sysfile_close(fh); 00070 sysmem_freehandle(texthandle); 00071 } 00072 @endcode 00073 00074 In most situations, you will pass 0 for the final two arguments to sysfile_readtextfile(). The third argument specifies a maximum length to read, but if the value is 0, the entire file is read in, regardless of its size. The final argument is a set of flags specifying options for reading in the text. The options concern the conversion of line breaks, text encoding, and the ability to add a null character to the end of the data returned. 00075 00076 Line breaks are converted on the basis of any line break flags. When reading text files, Max converts line breaks to "native" format, which is 00077 @code \r\n @endcode 00078 on Windows and 00079 @code \n @endcode 00080 on the Mac; this is the behavior you get if you either pass no line break flags or use #TEXT_LB_NATIVE. Other options include #TEXT_LB_MAC, #TEXT_LB_UNIX, or #TEXT_LB_PC. 00081 00082 By default, text files are converted from their source encoding to UTF-8. If you do not want this conversion to occur, you can use the #TEXT_ENCODING_USE_FILE flag. This puts the burden on determining the encoding on you, which is probably not what you want. For example, the source text file might use UTF-16 encoding, which requires very different parsing than an 8-bit encoding. 00083 00084 Finally, you can have the memory returned from sysfile_readtextfile() terminated with a NULL character if you use the #TEXT_NULL_TERMINATE flag. 00085 00086 00087 @section chapter_files_reading_data_files Reading Data Files 00088 00089 To read data files where you do not want to do text encoding conversion or worry about line breaks, you can use the same technique shown above for text files, but write the myobject_openfile function using sysfile_read() instead of sysfile_readtextfile(). This example shows how to read an entire file into a single block of memory. 00090 00091 @code 00092 void myobject_openfile(t_myobject *x, char *filename, short path) 00093 { 00094 t_filehandle fh; 00095 char *buffer; 00096 long size; 00097 00098 if (path_opensysfile(filename, path, &fh, READ_PERM)) { 00099 object_error(x, "error opening %s", filename); 00100 return 00101 } 00102 // allocate memory block that is the size of the file 00103 sysfile_geteof(fh, &size); 00104 buffer = sysmem_newptr(size); 00105 00106 // read in the file 00107 sysfile_read(fh, &size, buffer); 00108 00109 sysfile_close(fh); 00110 00111 // do something with data in buffer here 00112 00113 sysmem_freeptr(buffer); // must free allocated memory 00114 } 00115 @endcode 00116 00117 00118 @section chapter_files_writing_files Writing Files 00119 00120 Some Max objects respond to the write message to save data into a file. If there is no argument present after the word write, a save file dialog is shown and the user specifies a file name and location. If an argument is present, it can either specify a complete path name or a filename. In the filename case, the file is written to the current "default" directory, which is the location where a patcher was last opened. In the full pathname case, the file is written to the location specified by the pathname. 00121 00122 Here's how to implement this behavior. We'll show how to handle the message arguments, then provide text and data file writing examples. 00123 00124 Message and argument handling is very similar to the way we implemented the read message above, including the use of deferred execution. 00125 00126 @code 00127 class_addmethod(c, (method)myobject_write, "write", A_DEFSYM, 0); 00128 00129 void myobject_write(t_myobject *x, t_symbol *s) 00130 { 00131 defer(x, (method)myobject_dowrite, s, 0, NULL); 00132 } 00133 @endcode 00134 00135 The myobject_dowrite() function compares the filename argument with the empty symbol -- if the argument was not supplied, saveasdialog_extended() is used to obtain the user's choice for filename and location. Our first example looks for text files, so we use a four-character code 'TEXT' as our file type for saving. File type codes define a set of acceptable extensions. The file max-fileformats.txt permits contains standard definitions, and you can add your own by creating a similar text file and placing it in the init folder inside the Cycling '74 folder. 00136 00137 @code 00138 void myobject_dowrite(t_myobject *x, t_symbol *s) 00139 { 00140 long filetype = 'TEXT', outtype; 00141 short numtypes = 1; 00142 char filename[512]; 00143 short path; 00144 00145 if (s == gensym("")) { // if no argument supplied, ask for file 00146 if (saveasdialog_extended(filename, &path, &outtype, &filetype, 1)) // non-zero: user cancelled 00147 return; 00148 } else { 00149 strcpy(filename, s->s_name); 00150 path = path_getdefault(); 00151 } 00152 myobject_writefile(x, filename, path); 00153 } 00154 @endcode 00155 00156 Here is the text file variant of myobject_writefile() using the high-level sysfile_writetextfile() routine. We just write a sentence as our "text file" but your object will presumably have some text data stored internally that it will write. The buffer passed to sysfile_writetextfile() must be NULL-terminated, and will be assumed to be UTF-8 encoded. 00157 00158 Note that path_createsysfile() can accept a full path in the filename argument, in which case, the path argument is ignored. This means your object's write message can either accept a filename or full pathname and you needn't do anything special to accept both. 00159 00160 @code 00161 void myobject_writefile(t_myobject *x, char *filename, short path) 00162 { 00163 char *buf = "write me into a file"; 00164 long err; 00165 t_filehandle fh; 00166 00167 err = path_createsysfile(filename, path, 'TEXT', &fh); 00168 if (err) 00169 return; 00170 err = sysfile_writetextfile(fh, &buf, TEXT_LB_NATIVE); 00171 sysfile_close(fh); 00172 } 00173 @endcode 00174 00175 Here is a data file variant of myobject_writefile(). It writes a small buffer of ten numbers to a file. 00176 00177 @code 00178 void myobject_writefile(t_myobject *x, char *filename, short path) 00179 { 00180 char *buf[10]; 00181 long count, i; 00182 long err; 00183 t_filehandle fh; 00184 00185 // create some data 00186 00187 for (i = 0; i < 10; i++) 00188 buf[i] = i + 1; 00189 00190 count = 10; 00191 00192 err = path_createsysfile(filename, path, 'TEXT', &fh); 00193 if (err) 00194 return; 00195 err = sysfile_write(fh, &count, buf); 00196 sysfile_close(fh); 00197 } 00198 @endcode 00199 00200 00201 */
Copyright © 2008, Cycling '74