How can I get file extensions with JavaScript?

Solution 1:

Newer Edit: Lots of things have changed since this question was initially posted - there's a lot of really good information in wallacer's revised answer as well as VisioN's excellent breakdown


Edit: Just because this is the accepted answer; wallacer's answer is indeed much better:

return filename.split('.').pop();

My old answer:

return /[^.]+$/.exec(filename);

Should do it.

Edit: In response to PhiLho's comment, use something like:

return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename) : undefined;

Solution 2:

return filename.split('.').pop();

Edit:

This is another non-regex solution that I think is more efficient:

return filename.substring(filename.lastIndexOf('.')+1, filename.length) || filename;

There are some corner cases that are better handled by VisioN's answer below, particularly files with no extension (.htaccess etc included).

It's very performant, and handles corner cases in an arguably better way by returning "" instead of the full string when there's no dot or no string before the dot. It's a very well crafted solution, albeit tough to read. Stick it in your helpers lib and just use it.

Old Edit:

A safer implementation if you're going to run into files with no extension, or hidden files with no extension (see VisioN's comment to Tom's answer above) would be something along these lines

var a = filename.split(".");
if( a.length === 1 || ( a[0] === "" && a.length === 2 ) ) {
    return "";
}
return a.pop();    // feel free to tack .toLowerCase() here if you want

If a.length is one, it's a visible file with no extension ie. file

If a[0] === "" and a.length === 2 it's a hidden file with no extension ie. .htaccess

This should clear up issues with the slightly more complex cases. In terms of performance, I think this solution is a little slower than regex in most browsers. However, for most common purposes this code should be perfectly usable.

Solution 3:

The following solution is fast and short enough to use in bulk operations and save extra bytes:

 return fname.slice((fname.lastIndexOf(".") - 1 >>> 0) + 2);

Here is another one-line non-regexp universal solution:

 return fname.slice((Math.max(0, fname.lastIndexOf(".")) || Infinity) + 1);

Both work correctly with names having no extension (e.g. myfile) or starting with . dot (e.g. .htaccess):

 ""                            -->   ""
 "name"                        -->   ""
 "name.txt"                    -->   "txt"
 ".htpasswd"                   -->   ""
 "name.with.many.dots.myext"   -->   "myext"

If you care about the speed you may run the benchmark and check that the provided solutions are the fastest, while the short one is tremendously fast:

Speed comparison

How the short one works:

  1. String.lastIndexOf method returns the last position of the substring (i.e. ".") in the given string (i.e. fname). If the substring is not found method returns -1.
  2. The "unacceptable" positions of dot in the filename are -1 and 0, which respectively refer to names with no extension (e.g. "name") and to names that start with dot (e.g. ".htaccess").
  3. Zero-fill right shift operator (>>>) if used with zero affects negative numbers transforming -1 to 4294967295 and -2 to 4294967294, which is useful for remaining the filename unchanged in the edge cases (sort of a trick here).
  4. String.prototype.slice extracts the part of the filename from the position that was calculated as described. If the position number is more than the length of the string method returns "".

If you want more clear solution which will work in the same way (plus with extra support of full path), check the following extended version. This solution will be slower than previous one-liners but is much easier to understand.

function getExtension(path) {
    var basename = path.split(/[\\/]/).pop(),  // extract file name from full path ...
                                               // (supports `\\` and `/` separators)
        pos = basename.lastIndexOf(".");       // get last position of `.`

    if (basename === "" || pos < 1)            // if file name is empty or ...
        return "";                             //  `.` not found (-1) or comes first (0)

    return basename.slice(pos + 1);            // extract extension ignoring `.`
}

console.log( getExtension("/path/to/file.ext") );
// >> "ext"

All three variants should work in any web browser on the client side and can be used in the server side NodeJS code as well.

Solution 4:

function getFileExtension(filename)
{
  var ext = /^.+\.([^.]+)$/.exec(filename);
  return ext == null ? "" : ext[1];
}

Tested with

"a.b"     (=> "b") 
"a"       (=> "") 
".hidden" (=> "") 
""        (=> "") 
null      (=> "")  

Also

"a.b.c.d" (=> "d")
".a.b"    (=> "b")
"a..b"    (=> "b")

Solution 5:

function getExt(filename)
{
    var ext = filename.split('.').pop();
    if(ext == filename) return "";
    return ext;
}