11
$\begingroup$

I've written an import converter for a custom file format, and it all seems to be working as expected, except for the fallback behavior with no arguments:

Import["C:\\Data\\samplefile.xxx"]

(*Import::infer: Cannot infer format of file samplefile.xxx. >> *)

(* Out[1]= $Failed *)

Does anyone know how I go about informing Import of the file extension so that it can process a file using the default converter from the filename alone (i.e., without an argument to specify the type)?

$\endgroup$
  • $\begingroup$ This is covered in a couple of places, but the most relevant is here. See the bottom of the last code listing in the answer. $\endgroup$ – rcollyer Nov 6 '14 at 15:50
  • 1
    $\begingroup$ @rcollyer Thanks for the tip. I've posted the relevant portion of the content that you linked as an answer here to make it easier to find, since it there is a lot of unrelated (but highly interesting) code in the other question. $\endgroup$ – dionys Nov 6 '14 at 16:19
  • $\begingroup$ Glad it works for you. It's unfortunately not pretty, but probably the best for now. It would be nice if adding to the extension mapping system file would work, but it never did for me. BTW @pinging only works if I already contributed to the comments on this page. $\endgroup$ – Sjoerd C. de Vries Nov 6 '14 at 20:26
  • 1
    $\begingroup$ I agree with the reopen vote. It is not covered in detail in @SjoerdC.deVries' "duplicate" answer, in fact it is only given as a snippet of code with some undocumented functions as part of a much larger package concerning a much broader task with the overloading of Import as a nice supplement. This makes the problem hardly searchable, whereas the posed question here is both specific and general enough to be easily searchable and of interest to others. $\endgroup$ – LLlAMnYP Aug 21 '15 at 9:55
  • $\begingroup$ @rcollyer Perhaps you gained some more inside knowledge about this since the question was asked. Is the file format detected purely based on the extension, or also the file contents? I believe the latter is the case. See FileFormat. Finally, would overloading FileFormat be a better solution than overloading Import? $\endgroup$ – Szabolcs Jul 12 '18 at 10:51
7
$\begingroup$

Copy/paste from the excellent BVH package by @Sjoerd C. de Vries:

The code in this question (Registering/detecting an importer by file name extension) did not work. Neither did the answer. Wolfram support could not provide a more elegant solution so far. We use a trick here. In fact we don't have any options, but we need to add the option part to the argument template to be slightly more specific overall than an existing one that would also match. In this way we get to be evaluated before the other one, otherwise we'd be shadowed.

ImportExport`RegisterImport["BVH", BVHImporter`BVHImport];
BVHImporter`BVHImport[filename_String] := BVHImporter`BVHGet[Import[filename, "String"]];

Unprotect[Import];

Import[name_String, opts___?OptionQ] :=
    Import[name, "BVH", opts] /; FileExtension[name] === "bvh";

Protect[Import];

Related tutorial from the documentation: Developing an Import Converter

$\endgroup$
  • $\begingroup$ I've tested this in my own package and it seems to work well enough. $\endgroup$ – dionys Nov 6 '14 at 20:06
3
$\begingroup$

Here's a nice trick. We can simply figure out how FileFormat works and write a little wrapper, which frees us from overloading Import:

withFileFormat[formatName_ -> fileExtensions_, expr_] :=

  Internal`InheritedBlock[{
    FileFormatDump`$FILEFORMATS,
    FileFormatDump`$FILEFORMATMATRIX
    },
   FileFormatDump`$FILEFORMATS =
    Append[FileFormatDump`$FILEFORMATS, formatName];
   FileFormatDump`$FILEFORMATMATRIX[formatName] =
    {
     formatName,
     False, False, False, False, False,
     "*." <> # & /@ fileExtensions,
     {}, None, {}
     };
   expr
   ];
withFileFormat~SetAttributes~HoldRest

Then we check it on FileFormat:

withFileFormat["SHELL" -> {"sh"},
 FileFormat["~/Desktop/Bugs.sh"]
 ]

"SHELL"

And if I had a definition for "SHELL" this should work:

withFileFormat["SHELL" -> {"sh"},
 Import["~/Desktop/Bugs.sh"]
 ]

Import::infer: Cannot infer format of file Bugs.sh.

$Failed

On the plus side this differs from the default behavior in 11.3 which is to import as "Text".

Registering an alias for a new format will probably require more digging.

One noteworthy thing: if you drop the Internal`InheritedBlock this is literally just registering a new FileFormat.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.