Extract file extension: Difference between revisions

m
 
(39 intermediate revisions by 27 users not shown)
Line 1:
{{draft task}}
 
[[wp:Filename extension|Filename extensions]] are a rudimentary but commonly used way of identifying files types.
 
{{task heading}}
 
;Task:
Write a function or program that
* takes one string argument representing the path/URL to a file
Line 10 ⟶ 9:
 
 
If your programming language  (or standard library)  has built-in functionality for extracting a filename extension, show how it would be used and how exactly its behavior differs from this specification.
<br>show how it would be used and how exactly its behavior differs from this specification.
 
{{task heading|Specification}}
 
For the purposes of this task, a filename extension
 
;Specification:
For the purposes of this task, a filename extension:
* occurs at the very end of the filename
* consists of a period, followed solely by one or more ASCII letters or digits (A-Z, a-z, 0-9)
 
{{task heading|Test cases}}
 
;Test cases:
{| class="wikitable"
::::{| class="wikitable"
|-
! Input
Line 52 ⟶ 51:
|}
 
 
<hr>
{{Template:Strings}}
<br><br>
 
=={{header|11l}}==
 
{{trans|Python}}
<langsyntaxhighlight lang="11l">F extract_ext(path)
V m = re:‘\.[A-Za-z0-9]+$’.search(path)
R I m {m.group(0)} E ‘’
Line 69 ⟶ 70:
 
L(path) paths
print(path.rjust(max(paths.map(p -> p.len)))‘ -> ’extract_ext(path))</langsyntaxhighlight>
{{out}}
<pre>
Line 78 ⟶ 79:
document.txt_backup ->
/etc/pam.d/login ->
</pre>
 
=={{header|Action!}}==
{{libheader|Action! Tool Kit}}
<syntaxhighlight lang="action!">INCLUDE "D2:CHARTEST.ACT" ;from the Action! Tool Kit
 
PROC FileExt(CHAR ARRAY path,ext)
BYTE pos,c
 
pos=path(0)
ext(0)=0
WHILE pos>0
DO
c=path(pos)
IF c='. THEN
EXIT
ELSEIF IsDigit(c)=0 AND IsAlpha(c)=0 THEN
RETURN
FI
pos==-1
OD
IF pos=0 THEN
RETURN
FI
SCopyS(ext,path,pos,path(0))
RETURN
 
PROC Test(CHAR ARRAY path)
CHAR ARRAY ext(10)
 
FileExt(path,ext)
PrintF("""%S"":%E""%S""%E%E",path,ext)
RETURN
 
PROC Main()
Put(125) PutE() ;clear the screen
 
Test("http://example.com/download.tar.gz")
Test("CharacterModel.3DS")
Test(".desktop")
Test("document")
Test("document.txt_backup")
Test("/etc/pam.d/login")
RETURN</syntaxhighlight>
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Extract_file_extension.png Screenshot from Atari 8-bit computer]
<pre>
"http://example.com/download.tar.gz":
".gz"
 
"CharacterModel.3DS":
".3DS"
 
".desktop":
".desktop"
 
"document":
""
 
"document.txt_backup":
""
 
"/etc/pam.d/login":
""
</pre>
 
=={{header|Ada}}==
===As originally specified===
<syntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
use Ada.Strings;
with Ada.Characters.Handling; use Ada.Characters.Handling;
 
procedure Main is
function extension (S : in String) return String is
P_Index : Natural;
begin
P_Index :=
Index (Source => S, Pattern => ".", From => S'Last, Going => Backward);
if P_Index = 0 then
return "";
else
for C of S (P_Index + 1 .. S'Last) loop
if not Is_Alphanumeric (C) then
return "";
end if;
end loop;
return S (P_Index .. S'Last);
end if;
end extension;
F1 : String := "http://example.com/download.tar.gz";
F2 : String := "CharacterModel.3DS";
F3 : String := ".desktop";
F4 : String := "document";
F5 : String := "document.txt_backup";
F6 : String := "/etc/pam.d/login:";
begin
Put_Line (F1 & " -> " & extension (F1));
Put_Line (F2 & " -> " & extension (F2));
Put_Line (F3 & " -> " & extension (F3));
Put_Line (F4 & " -> " & extension (F4));
Put_Line (F5 & " -> " & extension (F5));
Put_Line (F6 & " -> " & extension (F6));
end Main;
</syntaxhighlight>
{{output}}
<pre>
http://example.com/download.tar.gz -> .gz
CharacterModel.3DS -> .3DS
.desktop -> .desktop
document ->
document.txt_backup ->
/etc/pam.d/login: ->
</pre>
 
===In response to problem discussions===
<syntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
use Ada.Strings;
with Ada.Characters.Handling; use Ada.Characters.Handling;
 
procedure Main is
function extension (S : in String) return String is
P_Index : Natural;
begin
P_Index :=
Index (Source => S, Pattern => ".", From => S'Last, Going => Backward);
if P_Index < 2 or else P_Index = S'Last then
return "";
else
for C of S (P_Index + 1 .. S'Last) loop
if not Is_Alphanumeric (C) then
return "";
end if;
end loop;
return S (P_Index .. S'Last);
end if;
end extension;
F1 : String := "http://example.com/download.tar.gz";
F2 : String := "CharacterModel.3DS";
F3 : String := ".desktop";
F4 : String := "document";
F5 : String := "document.txt_backup";
F6 : String := "/etc/pam.d/login:";
F7 : String := "filename.";
F8 : String := ".";
begin
Put_Line (F1 & " -> " & extension (F1));
Put_Line (F2 & " -> " & extension (F2));
Put_Line (F3 & " -> " & extension (F3));
Put_Line (F4 & " -> " & extension (F4));
Put_Line (F5 & " -> " & extension (F5));
Put_Line (F6 & " -> " & extension (F6));
Put_Line (F7 & " -> " & extension (F7));
Put_Line (F8 & " -> " & extension (F8));
end Main;
</syntaxhighlight>
{{output}}
<pre>
http://example.com/download.tar.gz -> .gz
CharacterModel.3DS -> .3DS
.desktop ->
document ->
document.txt_backup ->
/etc/pam.d/login: ->
filename. ->
. ->
</pre>
 
Line 83 ⟶ 251:
 
{{works with|ALGOL 68G|Any - tested with release 2.8.win32}}
<langsyntaxhighlight lang="algol68"># extracts a file-extension from the end of a pathname. The file extension is #
# defined as a dot followed by one or more letters or digits #
OP EXTENSION = ( STRING pathname )STRING:
Line 131 ⟶ 299:
; test extension( "document.txt_backup", "" )
; test extension( "/etc/pam.d/login", "" )
)</langsyntaxhighlight>
{{out}}
<pre>
Line 143 ⟶ 311:
=={{header|ALGOL W}}==
 
<langsyntaxhighlight lang="algolw">begin
% extracts a file-extension from the end of a pathname. %
% The file extension is defined as a dot followed by one or more letters %
Line 219 ⟶ 387:
testExtension( "/etc/pam.d/login", "" );
 
end.</langsyntaxhighlight>
{{out}}
<pre>
Line 239 ⟶ 407:
 
===Vanilla===
<langsyntaxhighlight lang="applescript">on getFileNameExtension from txt given underscores:keepingUnderscores : true, dot:includingDot : false
set astid to AppleScript's text item delimiters
-- Extract the file or bundle name from the path.
Line 267 ⟶ 435:
end repeat
 
return output</langsyntaxhighlight>
 
{{output}}
Line 276 ⟶ 444:
AppleScriptObjectiveC makes the task a little easier, but not necessarily more efficient.
 
<langsyntaxhighlight lang="applescript">use AppleScript version "2.4" -- Mac OS X 10.10 (Yosemite) or later.
use framework "Foundation"
 
Line 294 ⟶ 462:
end repeat
 
return output</langsyntaxhighlight>
 
{{output}}
<pre>{{"http://example.com/download.tar.gz", ".gz"}, {"CharacterModel.3DS", ".3DS"}, {".desktop", ".desktop"}, {"document", ""}, {"document.txt_backup", ""}, {"/etc/pam.d/login", ""}}</pre>
 
=={{header|ArturoAutoHotkey}}==
<syntaxhighlight lang="autohotkey">data := ["http://example.com/download.tar.gz"
 
,"CharacterModel.3DS"
<lang arturo>files: #("http://example.com/download.tar.gz" "CharacterModel.3DS" ".desktop" "document" "document.txt_backup" "/etc/pam.d/login")
,".desktop"
 
,"document"
loop files {
,"document.txt_backup"
ext: if [not|contains [pathExtension &] "_"] { pathExtension & } { "" }
,"/etc/pam.d/login"]
print & + " => extension: " + ext
}</lang>
 
for i, file in data{
RegExMatch(file, "`am)\.\K[a-zA-Z0-9]+$", ext)
result .= file " --> " ext "`n"
}
MsgBox % result</syntaxhighlight>
{{out}}
<pre>http://example.com/download.tar.gz --> gz
 
CharacterModel.3DS --> 3DS
<pre>http://example.com/download.tar.gz => extension: .gz
.desktop --> desktop
CharacterModel.3DS => extension: .3DS
document -->
.desktop => extension:
document.txt_backup =--> extension:
/etc/pam.d/login --> </pre>
document.txt_backup => extension:
/etc/pam.d/login => extension: </pre>
 
=={{header|AWK}}==
Line 323 ⟶ 494:
The first one was provided by an earlier contributor and shows a little more awk syntax and builtins (albeit with a bug fixed: it was testing for underscores in the extension but not other characters such as hyphens). It can be adjusted to allow any character in the extension other than /, \, : or . by replacing <code>[^a-zA-Z0-9]</code> with <code>[\\/\\\\:\\.]</code>.
 
<syntaxhighlight lang="awk">
<lang AWK>
BEGIN {
arr[++i] = "picture.jpg"
Line 350 ⟶ 521:
return(fn)
}
</syntaxhighlight>
</lang>
 
The second method is shorter and dispenses with the need to search for and remove the path components first. It too can be modified to allow all valid extensions (not just those described in the specification), by replacing <code>\\.[A-Za-z0-9]+$</code> with <code>\\.[^\\/\\\\:\\.]+$</code>.
 
<syntaxhighlight lang="awk">
<lang AWK>
BEGIN {
arr[++i] = "picture.jpg"
Line 376 ⟶ 547:
}
}
</syntaxhighlight>
</lang>
<p>Both examples give the output:</p>
<pre>
Line 388 ⟶ 559:
 
=={{header|Batch File}}==
<langsyntaxhighlight lang="dos">@echo off
 
:loop
Line 394 ⟶ 565:
echo File Path: "%~1" ^| File Extension "%~x1"
shift
goto loop</langsyntaxhighlight>
{{out}}
<pre>File Path: "http://example.com/download.tar.gz" | File Extension ".gz"
Line 402 ⟶ 573:
File Path: "document.txt_backup" | File Extension ".txt_backup"
File Path: "/etc/pam.d/login" | File Extension ""</pre>
 
=={{header|BCPL}}==
<syntaxhighlight lang="bcpl">get "libhdr"
 
// Find filename extension, store at `v'
let extension(s, v) = valof
$( let loc = valof
$( for i=s%0 to 1 by -1
$( let c = s%i
if c = '.'
resultis i
unless 'A'<=c<='Z' | 'a'<=c<='z' | '0'<=c<='9'
resultis 0
$)
resultis 0
$)
test loc=0 do
v%0 := 0
or
$( v%0 := s%0-loc+1
for i=1 to v%0 do
v%i := s%(i+loc-1)
$)
resultis v
$)
 
let show(s) be
$( let v = vec 32
writef("*"%S*": *"%S*"*N", s, extension(s, v))
$)
 
let start() be
$( show("http://example.com/download.tar.gz")
show("CharacterModel.3DS")
show(".desktop")
show("document")
show("document.txt_backup")
show("/etc/pam.d/login")
$)</syntaxhighlight>
{{out}}
<pre>"http://example.com/download.tar.gz": ".gz"
"CharacterModel.3DS": ".3DS"
".desktop": ".desktop"
"document": ""
"document.txt_backup": ""
"/etc/pam.d/login": ""</pre>
 
=={{header|C}}==
 
<langsyntaxhighlight Clang="c">#include <assert.h>
#include <ctype.h>
#include <string.h>
Line 449 ⟶ 666:
}
return exitcode;
}</langsyntaxhighlight>
 
=={{header|C sharp|C#}}==
 
<langsyntaxhighlight lang="[[Cc sharp|Cc#]]">public static string FindExtension(string filename) {
int indexOfDot = filename.Length;
for (int i = filename.Length - 1; i >= 0; i--) {
Line 469 ⟶ 686:
//so if the last character is a dot, return the empty string
return indexOfDot + 1 == filename.Length ? "" : filename.Substring(indexOfDot);
}</langsyntaxhighlight>
 
'''Using regular expressions (C# 6)'''
<langsyntaxhighlight lang="[[Cc sharp|Cc#]]">public static string FindExtension(string filename) => Regex.Match(filename, @"\.[A-Za-z0-9]+$").Value;</langsyntaxhighlight>
 
=={{header|C++}}==
 
<langsyntaxhighlight lang="cpp">#include <stringiostream>
#include <algorithmfilesystem>
#include <iostream>
#include <vector>
#include <regex>
 
int main() {
std::string findExtension ( const std::string & filename ) {
for (std::filesystem::path file : { "picture.jpg",
auto position = filename.find_last_of ( '.' ) ;
"http://mywebsite.com/picture/image.png",
if ( position == std::string::npos )
"myuniquefile.longextension",
return "" ;
"IAmAFileWithoutExtension",
else {
"/path/to.my/file",
std::string extension ( filename.substr( position + 1 ) ) ;
"file.odd_one",
if (std::regex_search (extension, std::regex("[^A-Za-z0-9]") ))
return "thisismine." ;}) {
std::cout << file << " has extension : " << file.extension() << '\n' ;
else
}
return extension ;
}</syntaxhighlight>
}
}
 
int main( ) {
std::vector<std::string> filenames {"picture.jpg" , "http://mywebsite.com/picture/image.png" ,
"myuniquefile.longextension" , "IAmAFileWithoutExtension" , "/path/to.my/file" ,
"file.odd_one", "thisismine." } ;
std::vector<std::string> extensions( filenames.size( ) ) ;
std::transform( filenames.begin( ) , filenames.end( ) , extensions.begin( ) , findExtension ) ;
for ( int i = 0 ; i < filenames.size( ) ; i++ )
std::cout << filenames[i] << " has extension : " << extensions[i] << " !\n" ;
return 0 ;
}
</lang>
{{out}}
<samp><pre>"picture.jpg" has extension : ".jpg !"
"http://mywebsite.com/picture/image.png" has extension : ".png !"
"myuniquefile.longextension" has extension : ".longextension !"
"IAmAFileWithoutExtension" has extension : !""
"/path/to.my/file" has extension : !""
"file.odd_one" has extension : !".odd_one"
"thisismine." has extension : !"."</pre></samp>
</pre>
 
 
=={{header|Clojure}}==
<syntaxhighlight lang="clojure">
<lang Clojure>
(defn file-extension [s]
(second (re-find #"(\.[a-zA-Z0-9]+)$" s)))
</syntaxhighlight>
</lang>
 
{{out}}
Line 534 ⟶ 733:
(".gz" ".3DS" ".desktop" nil nil nil)
</pre>
 
=={{header|CLU}}==
CLU contains a built-in filename parser, which behaves slightly differently than
the task specification. It returns the <em>first</em>, rather than last dotted part,
and also accepts non-alphanumeric characters in the extension. Furthermore, it does
not include the dot itself in its output.
 
<syntaxhighlight lang="clu">% Find the extension of a filename, according to the task specification
extension = proc (s: string) returns (string)
for i: int in int$from_to_by(string$size(s), 1, -1) do
c: char := s[i]
if c>='A' & c<='Z'
| c>='a' & c<='z'
| c>='0' & c<='9' then continue end
if c='.' then return(string$rest(s,i)) end
break
end
return("")
end extension
% For each test case, show both the extension according to the task,
% and the extension that the built-in function returns.
start_up = proc ()
po: stream := stream$primary_output()
tests: sequence[string] := sequence[string]$[
"http://example.com/download.tar.gz",
"CharacterModel.3DS",
".desktop",
"document",
"document.txt_backup",
"/etc/pam.d/login"
]
stream$putleft(po, "Input", 36)
stream$putleft(po, "Output", 10)
stream$putl(po, "Built-in")
stream$putl(po, "---------------------------------------------------------")
for test: string in sequence[string]$elements(tests) do
stream$putleft(po, test, 36)
stream$putleft(po, extension(test), 10)
% Using the built-in filename parser
stream$putl(po, file_name$parse(test).suffix)
except when bad_format:
stream$putl(po, "[bad_format signaled]")
end
end
end start_up</syntaxhighlight>
{{out}}
<pre>Input Output Built-in
---------------------------------------------------------
http://example.com/download.tar.gz .gz tar
CharacterModel.3DS .3DS 3DS
.desktop .desktop desktop
document
document.txt_backup txt_backup
/etc/pam.d/login</pre>
 
=={{header|Common Lisp}}==
 
<langsyntaxhighlight Lisplang="lisp">(pathname-type "foo.txt")
=>
"txt"</langsyntaxhighlight>
 
=={{header|D}}==
=== Variant 1===
<syntaxhighlight lang="d">
<lang D>
import std.stdio;
import std.path;
Line 560 ⟶ 819:
}
</syntaxhighlight>
</lang>
 
{{out}}
Line 573 ⟶ 832:
 
=== Variant 2===
<syntaxhighlight lang="d">
<lang D>
import std.stdio;
import std.string;
Line 603 ⟶ 862:
}
 
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 613 ⟶ 872:
/etc/pam.d/login ->
</pre>
=={{header|Delphi}}==
{{libheader| System.SysUtils}}
{{libheader| System.Character}}
<syntaxhighlight lang="delphi">
program Extract_file_extension;
 
{$APPTYPE CONSOLE}
 
uses
System.SysUtils,
System.Character;
 
const
TEST_CASES: array[0..5] of string = ('http://example.com/download.tar.gz',
'CharacterModel.3DS', '.desktop', 'document', 'document.txt_backup',
'/etc/pam.d/login');
 
function GetExt(path: string): string;
var
c: char;
begin
// Built-in functionality, just extract substring after dot char
Result := ExtractFileExt(path);
 
// Fix ext for dot in subdir
while (Result.IndexOf('/') > -1) do
begin
Result := Result.Substring(Result.IndexOf('/'), MaxInt);
Result := ExtractFileExt(Result);
end;
 
// Ignore empty or "." ext
if length(result) < 2 then
exit('');
 
// Ignore ext with not alphanumeric char (except the first dot)
for var i := 2 to length(result) do
begin
c := result[i];
if not c.IsLetterOrDigit then
exit('');
end;
end;
 
begin
for var path in TEST_CASES do
Writeln(path.PadRight(40), GetExt(path));
 
{$IFNDEF UNIX} readln; {$ENDIF}
end.</syntaxhighlight>
{{out}}
<pre>http://example.com/download.tar.gz .gz
CharacterModel.3DS .3DS
.desktop .desktop
document
document.txt_backup
/etc/pam.d/login</pre>
 
=={{header|EasyLang}}==
<syntaxhighlight>
func isalphanum c$ .
c = strcode c$
return if c >= 65 and c <= 90 or c >= 97 and c <= 122 or c >= 48 and c <= 57
.
func$ exext path$ .
for i = len path$ downto 1
c$ = substr path$ i 1
if isalphanum c$ = 1
ex$ = c$ & ex$
elif c$ = "."
return ex$
else
break 1
.
.
.
for s$ in [ "http://example.com/download.tar.gz" "CharacterModel.3DS" ".desktop" "document" "document.txt_backup" "/etc/pam.d/login" ]
print s$ & " -> " & exext s$
.
</syntaxhighlight>
 
=={{header|Emacs Lisp}}==
 
<langsyntaxhighlight Lisplang="lisp">(file-name-extension "foo.txt")
=>
"txt"</langsyntaxhighlight>
 
No extension is distinguished from empty extension but an <code>(or ... "")</code> can give <code>""</code> for both if desired
 
<langsyntaxhighlight Lisplang="lisp">(file-name-extension "foo.") => ""
(file-name-extension "foo") => nil</langsyntaxhighlight>
 
An Emacs backup <code>~</code> or <code>.~NUM~</code> are not part of the extension, but otherwise any characters are allowed.
 
<langsyntaxhighlight Lisplang="lisp">(file-name-extension "foo.txt~") => "txt"
(file-name-extension "foo.txt.~1.234~") => "txt"</langsyntaxhighlight>
 
=={{header|Factor}}==
Factor's <tt>file-extension</tt> word allows symbols to be in the extension and omits the dot from its output.
<langsyntaxhighlight lang="factor">USING: assocs formatting kernel io io.pathnames math qw
sequences ;
IN: rosetta-code.file-extension
Line 648 ⟶ 987:
"Path" "| Extension" "%-35s%s\n" printf
47 [ "-" write ] times nl
[ "%-35s| %s\n" vprintf ] each</langsyntaxhighlight>
{{out}}
<pre>
Line 663 ⟶ 1,002:
=={{header|Forth}}==
 
<langsyntaxhighlight lang="forth">: invalid? ( c -- f )
toupper dup [char] A [char] Z 1+ within
swap [char] 0 [char] 9 1+ within or 0= ;
Line 689 ⟶ 1,028:
s" document" test
s" document.txt_backup" test
s" /etc/pam.d/login" test ;</langsyntaxhighlight>
{{out}}
<pre>cr tests
Line 705 ⟶ 1,044:
The source incorporates a collection of character characterisations via suitable spans of a single sequence of characters. Unfortunately, the PARAMETER statement does not allow its constants to appear in EQUIVALENCE statements, so the text is initialised by DATA statements, and thus loses the protection of read-only given to constants defined via PARAMETER statements. The statements are from a rather more complex text scanning scheme, as all that are needed here are the symbols of GOODEXT.
 
The text scan could instead check for a valid character via something like <code> ("a" <= C & C <= "z") | ("A" <= C & C <= "Z") | (0 <= C & C <= "9")</code> but this is not just messy but unreliable - in EBCDIC for example there are gaps in the sequence of letters that are occupied by other symbols. So instead, a test via INDEX into a sequence of all the valid symbols. If one was in a hurry, for eight-bit character codes, an array GOODEXT of 256 logical values could be indexed by the numerical value of the character. <langsyntaxhighlight Fortranlang="fortran"> MODULE TEXTGNASH !Some text inspection.
CHARACTER*10 DIGITS !Integer only.
CHARACTER*11 DDIGITS !With a full stop masquerading as a decimal point.
Line 778 ⟶ 1,117:
WRITE (6,*) FEXT("/etc/pam.d/login")
WRITE (6,*) "Approved characters: ",GOODEXT
END</langsyntaxhighlight>
The output cheats a little, in that trailing spaces appear just as blankly as no spaces. The result of FEXT could be presented to TRIM (if that function is available), or the last non-blank could be found. With F2003, a scheme to enable character variables to be redefined to take on a current length is available, and so trailing spaces could no longer appear. This facility would also solve the endlessly annoying question of "how long is long enough", manifested in parameter MEXT being what might be a perfect solution. Once, three was the maximum extension length (not counting the period), then perhaps six, but now, what?
<pre>
Line 801 ⟶ 1,140:
 
=={{header|FreeBASIC}}==
<langsyntaxhighlight lang="freebasic">' FB 1.05.0 Win64
 
Function isAlphaNum(s As String) As Boolean
Line 843 ⟶ 1,182:
Print
Print "Press any key to quit"
Sleep</langsyntaxhighlight>
 
{{out}}
Line 856 ⟶ 1,195:
document.txt_backup (empty string)
/etc/pam.d/login (empty string)
</pre>
 
 
=={{header|Frink}}==
<syntaxhighlight lang="frink">fileExtension[str] :=
{
if [ext] = str =~ %r/(\.[A-Za-z0-9]+)$/
return ext
else
return ""
}
 
files = ["http://example.com/download.tar.gz",
"CharacterModel.3DS",
".desktop",
"document",
"document.txt_backup",
"/etc/pam.d/login"]
 
r = new array
for f = files
r.push[[f, "->", fileExtension[f]]]
 
println[formatTable[r, "right"]]</syntaxhighlight>
{{out}}
<pre>
http://example.com/download.tar.gz -> .gz
CharacterModel.3DS -> .3DS
.desktop -> .desktop
document ->
document.txt_backup ->
/etc/pam.d/login ->
</pre>
 
=={{header|FutureBasic}}==
The underscores is a valid extension character in macOS and extensions are returned without leading dots.
<syntaxhighlight lang="futurebasic">include "NSLog.incl"
 
void local fn DoIt
CFArrayRef paths = @[@"http://example.com/download.tar.gz",@"CharacterModel.3DS",@".desktop",@"document",@"document.txt_backup",@"/etc/pam.d/login"]
CFStringRef path
for path in paths
NSLog(@"%@",fn StringPathExtension( path ))
next
end fn
 
fn DoIt
 
HandleEvents</syntaxhighlight>
 
{{Out}}
<pre>
gz
3DS
desktop
null
txt_backup
null
</pre>
 
Line 862 ⟶ 1,259:
'''[https://gambas-playground.proko.eu/?gist=d52464fe8c05c857311d49184299814a Click this link to run this code]'''
<langsyntaxhighlight lang="gambas">Public Sub Main()
Dim sDir As String = "/sbin"
Dim sFileList As String[] = Dir(sDir)
Line 874 ⟶ 1,271:
Next
 
End</langsyntaxhighlight>
Output:
<pre>
Line 894 ⟶ 1,291:
=={{header|Go}}==
 
<langsyntaxhighlight lang="go">package main
 
import "fmt"
Line 936 ⟶ 1,333:
}
}
}</langsyntaxhighlight>
 
=={{header|Haskell}}==
 
<langsyntaxhighlight Haskelllang="haskell">module FileExtension
where
 
Line 950 ⟶ 1,347:
where
extension = reverse ( takeWhile ( /= '.' ) $ reverse s )
</syntaxhighlight>
</lang>
{{out}}
<pre>map myextension ["http://example.com/download.tar.gz", "CharacterModel.3DS", ".desktop", "document", "document.txt_backup", "/etc/pam.d/login"]
Line 959 ⟶ 1,356:
On Unix systems, the penultimate file extension would be recognised, so using the Haskell library function '''takeExtension''':
 
<langsyntaxhighlight lang="haskell">import System.FilePath.Posix (FilePath, takeExtension)
 
fps :: [FilePath]
fps =
[ "http://example.com/download.tar.gz",
, "CharacterModel.3DS",
, ".desktop",
, "document",
, "document.txt_backup",
, "/etc/pam.d/login"
]
 
main :: IO ()
main = mapM_ (print $. takeExtension <$>) fps</langsyntaxhighlight>
{{Out}}
<pre>".gz"
Line 985 ⟶ 1,382:
'''Implementation:'''
 
<langsyntaxhighlight Jlang="j">require'regex'
ext=: '[.][a-zA-Z0-9]+$'&rxmatch ;@rxfrom ]</langsyntaxhighlight>
 
Obviously most of the work here is done by the regex implementation ([[wp:Perl Compatible Regular Expressions|pcre]], if that matters - and this particular kind of expression tends to be a bit more concise expressed in [[Perl|perl]] than in J...).
Line 996 ⟶ 1,393:
 
'''Alternative non-regex Implementation'''
<langsyntaxhighlight Jlang="j">ext=: (}.~ i:&'.')@(#~ [: -. [: +./\. -.@e.&('.',AlphaNum_j_)</langsyntaxhighlight>
 
 
'''Task examples:'''
<langsyntaxhighlight Jlang="j"> ext 'http://example.com/download/tar.gz'
.gz
ext 'CharacterModel.3DS'
Line 1,009 ⟶ 1,406:
┌───┬────┬────────┬┬┬┐
│.gz│.3DS│.desktop││││
└───┴────┴────────┴┴┴┘</langsyntaxhighlight>
 
=={{header|Java}}==
<syntaxhighlight lang ="java">public class Test {
import java.io.File;
</syntaxhighlight>
public static void main(String[] args) {
<syntaxhighlight lang="java">
String[] filenames = { "http://example.com/download.tar.gz",
public static void main(String[] args) {
"CharacterModel.3DS",
String[] strings = {
".desktop",
"http://example.com/download.tar.gz",
"document",
"documentCharacterModel.txt_backup3DS",
"/etc/pam.d/logindesktop",
};"document",
"document.txt_backup",
"/etc/pam.d/login",
};
for (String string : strings)
System.out.println(extractExtension(string));
}
 
static String for extractExtension(String filename : filenamesstring) {
/* we can use the 'File' class to Stringextract extthe =file-name "null";*/
File file = new File(string);
int idx = filename.lastIndexOf('.');
String filename = file.getName();
if (idx != -1) {
int String tmpindexOf = filename.substringlastIndexOf(idx'.');
if (indexOf != -1) {
if (tmp.matches("\\.[a-zA-Z0-9]+")) {
String extextension = tmpfilename.substring(indexOf);
/* and use a regex to match only }valid extensions */
if (extension.matches("\\.[A-Za-z\\d]+"))
}
System.out.println(filenamereturn + " -> " + ext)extension;
}
}
return "";
}</lang>
}
</syntaxhighlight>
{{Out}}
<pre>
<pre>http://example.com/download.tar.gz -> .gz
.gz
CharacterModel.3DS -> .3DS
.3DS
.desktop -> .desktop
.desktop
document -> null
 
document.txt_backup -> null
 
/etc/pam.d/login -> null</pre>
</pre>
 
=={{header|javascript}}==
<langsyntaxhighlight lang="javascript">let filenames = ["http://example.com/download.tar.gz", "CharacterModel.3DS", ".desktop", "document", "document.txt_backup", "/etc/pam.d/login"];
let r = /\.[a-zA-Z0-9]+$/;
filenames.forEach((e) => console.log(e + " -> " + (r.test(e) ? r.exec(e)[0] : "")));</langsyntaxhighlight>
{{Out}}
<pre>http://example.com/download.tar.gz -> .gz
Line 1,061 ⟶ 1,466:
One approach is to define a more general curried function, from which we can obtain various simpler and OS-specific functions by specialisation:
 
<langsyntaxhighlight lang="javascript">(() => {
'use strict';
 
Line 1,214 ⟶ 1,619:
// MAIN ---
return main();
})();</langsyntaxhighlight>
{{Out}}
<pre>
Line 1,244 ⟶ 1,649:
 
{{works with|jq| 1.4}}
<langsyntaxhighlight lang="jq">def file_extension:
def alphanumeric: explode | unique
| reduce .[] as $i
Line 1,257 ⟶ 1,662:
end
else ""
end;</langsyntaxhighlight>
 
{{works with|jq|1.5}}
<langsyntaxhighlight lang="jq">def file_extension:
(match( "(\\.[a-zA-Z0-9]*$)" ) | .captures[0].string)
// "" ;</langsyntaxhighlight>
 
'''Examples''':
 
Using either version above gives the same results.
<langsyntaxhighlight lang="jq">"http://example.com/download.tar.gz",
"CharacterModel.3DS",
".desktop",
Line 1,273 ⟶ 1,678:
"document.txt_backup",
"/etc/pam.d/login"
| "\(.) has extension: \(file_extension)"</langsyntaxhighlight>
 
<langsyntaxhighlight lang="sh">$ jq -r -n -f Extract_file_extension.jq</langsyntaxhighlight>
{{out}}
<pre>http://example.com/download.tar.gz has extension: .gz
Line 1,286 ⟶ 1,691:
 
=={{header|Jsish}}==
<langsyntaxhighlight lang="javascript">#!/usr/bin/env jsish
/* Extract filename extension (for a limited subset of possible extensions) in Jsish */
function extractExtension(filename) {
Line 1,309 ⟶ 1,714:
/etc/pam.d/login ""
=!EXPECTEND!=
*/</langsyntaxhighlight>
 
{{out}}
Line 1,324 ⟶ 1,729:
 
=={{header|Julia}}==
<langsyntaxhighlight lang="julia">extension(url::String) = try match(r"\.[A-Za-z0-9]+$", url).match catch "" end
 
@show extension("http://example.com/download.tar.gz")
Line 1,331 ⟶ 1,736:
@show extension("document")
@show extension("document.txt_backup")
@show extension("/etc/pam.d/login")</langsyntaxhighlight>
 
{{out}}
Line 1,342 ⟶ 1,747:
 
=={{header|Kotlin}}==
<langsyntaxhighlight lang="scala">// version 1.0.6
 
val r = Regex("[^a-zA-Z0-9]") // matches any non-alphanumeric character
Line 1,371 ⟶ 1,776:
println("${path.padEnd(37)} -> ${if (ext.isEmpty()) "(empty string)" else ext}")
}
}</langsyntaxhighlight>
 
{{out}}
Line 1,386 ⟶ 1,791:
 
=={{header|Lua}}==
<langsyntaxhighlight Lualang="lua">-- Lua pattern docs at http://www.lua.org/manual/5.1/manual.html#5.4.1
function fileExt (filename) return filename:match("(%.%w+)$") or "" end
Line 1,399 ⟶ 1,804:
for _, example in pairs(testCases) do
print(example .. ' -> "' .. fileExt(example) .. '"')
end</langsyntaxhighlight>
{{out}}
<pre>http://example.com/download.tar.gz -> ".gz"
Line 1,408 ⟶ 1,813:
/etc/pam.d/login -> ""</pre>
 
=={{header|Mathematica}}/{{header|Wolfram Language}}==
FileExtension is a built-in function:
<langsyntaxhighlight Mathematicalang="mathematica">FileExtension /@ {"http://example.com/download.tar.gz", "CharacterModel.3DS", ".desktop", "document","document.txt_backup","/etc/pam.d/login"}</langsyntaxhighlight>
 
{{out}}
{"gz", "3DS", "", "", "txt_backup", ""}
Line 1,417 ⟶ 1,821:
=={{header|Nanoquery}}==
The File object type in Nanoquery has a built-in method to extract the file extension from a filename, but it treats all characters as potentially valid in an extension and URLs as not being. As a result, the .txt_backup extension is included in the output.
<langsyntaxhighlight Nanoquerylang="nanoquery">import Nanoquery.IO
 
filenames = {"http://example.com/download.tar.gz", "CharacterModel.3DS"}
Line 1,424 ⟶ 1,828:
for fname in filenames
println new(File, fname).getExtension()
end</langsyntaxhighlight>
{{out}}
<pre>.gz
Line 1,432 ⟶ 1,836:
.txt_backup
</pre>
 
=={{header|Nim}}==
As can be seen in the examples, Nim standard library function <code>splitFile</code> detects that a file such as <code>.desktop</code> is a special file. But, on the other hand, it considers that an underscore is a valid character in an extension.
<syntaxhighlight lang="nim">import os, strutils
 
func extractFileExt(path: string): string =
var s: seq[char]
for i in countdown(path.high, 0):
case path[i]
of Letters, Digits:
s.add path[i]
of '.':
s.add '.'
while s.len > 0: result.add s.pop()
return
else:
break
result = ""
 
for input in ["http://example.com/download.tar.gz", "CharacterModel.3DS",
".desktop", "document", "document.txt_backup", "/etc/pam.d/login"]:
echo "Input: ", input
echo "Extracted extension: ", input.extractFileExt()
echo "Using standard library: ", input.splitFile()[2]
echo()</syntaxhighlight>
 
{{out}}
<pre>Input: http://example.com/download.tar.gz
Extracted extension: .gz
Using standard library: .gz
 
Input: CharacterModel.3DS
Extracted extension: .3DS
Using standard library: .3DS
 
Input: .desktop
Extracted extension: .desktop
Using standard library:
 
Input: document
Extracted extension:
Using standard library:
 
Input: document.txt_backup
Extracted extension:
Using standard library: .txt_backup
 
Input: /etc/pam.d/login
Extracted extension:
Using standard library: </pre>
 
=={{header|Objeck}}==
<langsyntaxhighlight lang="objeck">use Query.RegEx;
 
class FindExtension {
Line 1,465 ⟶ 1,919:
return ext;
}
}</langsyntaxhighlight>
 
{{output}}
Line 1,479 ⟶ 1,933:
=={{header|OCaml}}==
Since OCaml 4.04 there is a function '''[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Filename.html#VALextension Filename.extension]''':
<langsyntaxhighlight lang="ocaml">let () =
let filenames = [
"http://example.com/download.tar.gz";
Line 1,490 ⟶ 1,944:
List.iter (fun filename ->
Printf.printf " '%s' => '%s'\n" filename (Filename.extension filename)
) filenames</langsyntaxhighlight>
differs a little bit from the specification of this task.
{{out}}
Line 1,507 ⟶ 1,961:
Easy to change if "" is required.
 
<langsyntaxhighlight Oforthlang="oforth">: fileExt( s -- t )
| i |
s lastIndexOf('.') dup ->i ifNull: [ null return ]
s extract(i 1+, s size) conform(#isAlpha) ifFalse: [ null return ]
s extract(i, s size)
;</langsyntaxhighlight>
 
{{out}}
Line 1,533 ⟶ 1,987:
null ok
></pre>
=={{header|Pascal}}==
==={{header|Free Pascal}}===
<syntaxhighlight lang="pascal">
Program Extract_file_extension;
 
{FreePascal has the built-in function ExtractFileExt which returns the file extension.
* it does need charachters before the period to return the proper extension and it returns
* the extension including the period}
 
Uses character,sysutils;
 
Const arr : array of string = ('http://example.com/download.tar.gz','CharacterModel.3DS','.desktop',
'document','document.txt_backup','/etc/pam.d/login');
 
Function extractextension(fn: String): string;
Var
i: integer;
Begin
fn := 'prefix' + fn; {add charachters before the period}
fn := ExtractFileExt(fn);
For i := 2 to length(fn) Do {skip the period}
If Not IsLetterOrDigit(fn[i]) Then exit('');
extractextension := fn;
End;
 
Var i : string;
Begin
For i In arr Do
writeln(i:35,' -> ',extractextension(i))
End.
 
</syntaxhighlight>
{{out}}
<pre>
http://example.com/download.tar.gz -> gz
CharacterModel.3DS -> 3DS
.desktop -> desktop
document ->
document.txt_backup ->
/etc/pam.d/login ->
</pre>
 
=={{header|Perl}}==
 
{{trans|Raku}}
<langsyntaxhighlight lang="perl">sub extension {
my $path = shift;
$path =~ / \. [a-z0-9]+ $ /xi;
$& // '';
}</langsyntaxhighlight>
 
Testing:
<langsyntaxhighlight lang="perl">printf "%-35s %-11s\n", $_, "'".extension($_)."'"
for qw[
http://example.com/download.tar.gz
Line 1,552 ⟶ 2,047:
document.txt_backup
/etc/pam.d/login
];</langsyntaxhighlight>
 
{{out}}
Line 1,565 ⟶ 2,060:
 
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<lang Phix>function getExtension(string filename)
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
for i=length(filename) to 1 by -1 do
<span style="color: #008080;">function</span> <span style="color: #000000;">getExtension</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">filename</span><span style="color: #0000FF;">)</span>
integer ch = filename[i]
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">filename</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">1</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
if ch='.' then return filename[i..$] end if
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">filename</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
if find(ch,"\\/_") then exit end if
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'.'</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #000000;">filename</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..$]</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end for
<span style="color: #008080;">if</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\\/_"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
return ""
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
end function
<span style="color: #008080;">return</span> <span style="color: #008000;">""</span>
 
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
constant tests = {"mywebsite.com/picture/image.png",
"http://mywebsite.com/picture/image.png",
<span style="color: #008080;">constant</span> <span style="color: #000000;">tests</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"mywebsite.com/picture/image.png"</span><span style="color: #0000FF;">,</span>
"myuniquefile.longextension",
<span style="color: #008000;">"http://mywebsite.com/picture/image.png"</span><span style="color: #0000FF;">,</span>
"IAmAFileWithoutExtension",
<span style="color: #008000;">"myuniquefile.longextension"</span><span style="color: #0000FF;">,</span>
"/path/to.my/file",
<span style="color: #008000;">"IAmAFileWithoutExtension"</span><span style="color: #0000FF;">,</span>
"file.odd_one",
<span style="httpcolor: #008000;">"/path/exampleto.commy/download.tar.gzfile"</span><span style="color: #0000FF;">,</span>
<span style="CharacterModelcolor: #008000;">"file.3DSodd_one"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"http://example.com/download.tar.gz"</span><span style="color: #0000FF;">,</span>
".desktop",
<span style="color: #008000;">"CharacterModel.3DS"</span><span style="color: #0000FF;">,</span>
"document",
<span style="color: #008000;">".desktop"</span><span style="color: #0000FF;">,</span>
"document.txt_backup",
<span style="color: #008000;">"document"</etc/pam.d/loginspan><span style="}color: #0000FF;">,</span>
<span style="color: #008000;">"document.txt_backup"</span><span style="color: #0000FF;">,</span>
for i=1 to length(tests) do
<span style="color: #008000;">"/etc/pam.d/login"</span><span style="color: #0000FF;">}</span>
printf(1,"%s ==> %s\n",{tests[i],getExtension(tests[i])})
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
end for</lang>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s ==&gt; %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">getExtension</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
Line 1,605 ⟶ 2,103:
</pre>
The builtin get_file_extension() could also be used, however that routine differs from the task description in that "libglfw.so.3.1" => "so", and all results are lowercase even if the input is not.
 
=={{header|PHP}}==
<syntaxhighlight lang="php">
$tests = [
['input'=>'http://example.com/download.tar.gz', 'expect'=>'.gz'],
['input'=>'CharacterModel.3DS', 'expect'=>'.3DS'],
['input'=>'.desktop', 'expect'=>'.desktop'],
['input'=>'document', 'expect'=>''],
['input'=>'document.txt_backup', 'expect'=>''],
['input'=>'/etc/pam.d/login', 'expect'=>'']
];
 
foreach ($tests as $key=>$test) {
$ext = pathinfo($test['input'], PATHINFO_EXTENSION);
// in php, pathinfo allows for an underscore in the file extension
// the following if statement only allows for A-z0-9 in the extension
if (ctype_alnum($ext)) {
// pathinfo returns the extension without the preceeding '.' so adding it back on
$tests[$key]['actual'] = '.'.$ext;
} else {
$tests[$key]['actual'] = '';
}
}
foreach ($tests as $test) {
printf("%35s -> %s \n", $test['input'],$test['actual']);
}</syntaxhighlight>
{{out}}
<pre>
http://example.com/download.tar.gz -> .gz
CharacterModel.3DS -> .3DS
.desktop -> .desktop
document ->
document.txt_backup ->
/etc/pam.d/login ->
</pre>
 
=={{header|PicoLisp}}==
<langsyntaxhighlight PicoLisplang="picolisp">(de extension (F)
(and
(fully
Line 1,622 ⟶ 2,155:
(println (extension "document"))
(println (extension "document.txt_backup"))
(println (extension "/etc/pam.d/login"))</langsyntaxhighlight>
{{out}}
<pre>
Line 1,631 ⟶ 2,164:
NIL
NIL
</pre>
 
=={{header|Plain English}}==
The 'Extract' imperative extracts parts of a path. When extracting an extension, it starts from the last period (.) in the path string and goes until the end of the string.
<syntaxhighlight lang="text">
To run:
Start up.
Show the file extension of "http://example.com/download.tar.gz".
Show the file extension of "CharacterModel.3DS".
Show the file extension of ".desktop".
Show the file extension of "document".
Show the file extension of "document.txt_backup".
Show the file extension of "/etc/pam.d/login".
Wait for the escape key.
Shut down.
 
To show the file extension of a path:
Extract an extension from the path.
Write the extension to the console.
</syntaxhighlight>
{{out}}
<pre>
.gz
.3DS
.desktop
 
.txt_backup
.d/login
</pre>
 
=={{header|PowerShell}}==
<langsyntaxhighlight PowerShelllang="powershell">function extension($file){
$ext = [System.IO.Path]::GetExtension($file)
if (-not [String]::IsNullOrEmpty($ext)) {
Line 1,646 ⟶ 2,207:
extension "document"
extension "document.txt_backup"
extension "/etc/pam.d/login"</langsyntaxhighlight>
<b>Output:</b>
<pre>.gz
Line 1,659 ⟶ 2,220:
Uses [https://docs.python.org/3/library/re.html#re.search re.search].
 
<langsyntaxhighlight lang="python">import re
def extractExt(url):
m = re.search(r'\.[A-Za-z0-9]+$', url)
return m.group(0) if m else ""
</syntaxhighlight>
</lang>
 
and one way of allowing for OS-specific variations in the character sets permitted in file extensions is to write a general and reusable curried function, from which we can obtain simpler OS-specific functions by specialisation:
 
<langsyntaxhighlight lang="python">'''Obtaining OS-specific file extensions'''
 
import os
Line 1,749 ⟶ 2,310:
# MAIN ---
if __name__ == '__main__':
main()</langsyntaxhighlight>
{{Out}}
<pre>takePosixExtension :: FilePath -> String:
Line 1,766 ⟶ 2,327:
document.txt_backup ->
/etc/pam.d/login -> </pre>
 
=={{header|Quackery}}==
 
<syntaxhighlight lang="quackery"> [ bit
[ 0
$ "abcdefghijklmnopqrstuvwxyz"
$ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
$ "1234567890." join join
witheach [ bit | ] ] constant
& 0 > ] is validchar ( c --> b )
[ dup $ "" = if done
dup -1 peek char . = iff
[ drop $ "" ] done
$ "" swap
reverse witheach
[ dup dip join
dup validchar iff
[ char . = if
[ reverse conclude ] ]
else
[ 2drop $ "" conclude ] ]
dup $ "" = if done
dup 0 peek char . != if
[ drop $ "" ] ] is extension ( $ --> $ )
[ cr dup echo$ say " --> "
extension
dup $ "" = iff
[ drop say "no extension" ]
else echo$
cr ] is task ( $ --> )
$ "http://example.com/download.tar.gz" task
$ "CharacterModel.3DS" task
$ ".desktop" task
$ "document" task
$ "document.txt_backup" task
$ "/etc/pam.d/login" task</syntaxhighlight>
 
{{out}}
 
<pre>http://example.com/download.tar.gz --> .gz
 
CharacterModel.3DS --> .3DS
 
.desktop --> .desktop
 
document --> no extension
 
document.txt_backup --> no extension
 
/etc/pam.d/login --> no extension
</pre>
 
=={{header|Racket}}==
 
<syntaxhighlight lang="racket">
<lang Racket>
#lang racket
 
Line 1,790 ⟶ 2,405:
(for ([x (in-list examples)])
(printf "~a | ~a\n" (~a x #:width 34) (string-extension x)))
</langsyntaxhighlight>
 
{{out}}
Line 1,807 ⟶ 2,422:
The built-in <code>IO::Path</code> class has an <code>.extension</code> method:
 
<syntaxhighlight lang="raku" perl6line>say $path.IO.extension;</langsyntaxhighlight>
Contrary to this task's specification, it
* doesn't include the dot in the output
Line 1,815 ⟶ 2,430:
Here's a custom implementation which does satisfy the task requirements:
 
<syntaxhighlight lang="raku" perl6line>sub extension (Str $path --> Str) {
$path.match(/:i ['.' <[a..z0..9]>+]? $ /).Str
}
Line 1,829 ⟶ 2,444:
document.txt_backup
/etc/pam.d/login
>;</langsyntaxhighlight>
 
{{out}}
Line 1,846 ⟶ 2,461:
 
a legal file extension &nbsp; ''only'' &nbsp; consists of mixed-case Latin letters and/or decimal digits.
<langsyntaxhighlight lang="rexx">/*REXX pgm extracts the file extension (defined above from the RC task) from a file name*/
@.= /*define default value for the @ array.*/
parse arg fID /*obtain any optional arguments from CL*/
Line 1,866 ⟶ 2,481:
else x= . || x /*prefix the extension with a period. */
say 'file extension=' left(x, 20) "for file name=" @.j
end /*j*/ /*stick a fork in it, we're all done. */</langsyntaxhighlight>
'''output''' &nbsp; when using the default (internal) inputs:
<pre>
Line 1,878 ⟶ 2,493:
 
=={{header|Ring}}==
<langsyntaxhighlight lang="ring">
# Project : Extract file extension
 
Line 1,919 ⟶ 2,534:
next
return cStr2
</syntaxhighlight>
</lang>
Output:
<pre>
Line 1,931 ⟶ 2,546:
 
=={{header|Ruby}}==
<langsyntaxhighlight lang="ruby">names =
%w(http://example.com/download.tar.gz
CharacterModel.3DS
Line 1,938 ⟶ 2,553:
/etc/pam.d/login)
names.each{|name| p File.extname(name)}
</syntaxhighlight>
</lang>
'''output'''
<pre>
Line 1,950 ⟶ 2,565:
 
=={{header|Rust}}==
<langsyntaxhighlight Rustlang="rust">use std::path::Path;
 
fn main() {
Line 1,978 ⟶ 2,593:
.filter(|ext| ext.chars().skip(1).all(|c| c.is_ascii_alphanumeric()))
.unwrap_or("")
}</langsyntaxhighlight>
The built-in method requires a filename before the extension, allows any non-period character to appear in the extension, and returns <code>None</code> if no extension is found.
{{out}}
Line 1,991 ⟶ 2,606:
 
=={{header|Scala}}==
<langsyntaxhighlight lang="scala">package rosetta
 
object FileExt {
Line 2,051 ⟶ 2,666:
println("Url: " + url + " -> Extension: " + FileExt.extractExt(url))
}
}</langsyntaxhighlight>
'''output'''
<pre>
Line 2,076 ⟶ 2,691:
=={{header|sed}}==
 
<langsyntaxhighlight lang="sed">-n -reEne 's:.*(\.[A-Za-z0-9]+)$:\1:p'</langsyntaxhighlight>
 
Example of use:
<langsyntaxhighlight lang="bash">for F in "http://example.com/download.tar.gz" "CharacterModel.3DS" ".desktop" "document" "document.txt_backup" "/etc/pam.d/login"
do
EXT=`echo $F | sed -n -reEne 's:.*(\.[A-Za-z0-9]+)$:\1:p'`
echo "$F: $EXT"
done
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 2,096 ⟶ 2,711:
 
=={{header|Sidef}}==
<langsyntaxhighlight lang="ruby">func extension(filename) {
filename.match(/(\.[a-z0-9]+)\z/i).to_s
}
Line 2,111 ⟶ 2,726:
files.each {|f|
printf("%-36s -> %-11s\n", f.dump, extension(f).dump)
}</langsyntaxhighlight>
{{out}}
<pre>
Line 2,121 ⟶ 2,736:
"/etc/pam.d/login" -> ""
</pre>
 
=={{header|Smalltalk}}==
The Filename class has a convenient suffix method for that; so we convert the string to a filename and ask it:
<syntaxhighlight lang="smalltalk">names := #(
'http://example.com/download.tar.gz'
'CharacterModel.3DS'
'.desktop'
'a.desktop'
'document'
'document.txt_backup'
'/etc/pam.d/login'
).
names do:[:f |
'%-35s -> %s\n' printf:{ f . f asFilename suffix } on:Stdout
]</syntaxhighlight>
{{out}}
<pre>http://example.com/download.tar.gz -> gz
CharacterModel.3DS -> 3DS
.desktop ->
a.desktop -> desktop
document ->
document.txt_backup -> txt_backup
/etc/pam.d/login -> </pre>
Note: the task's description seems wrong to me; on a Unix machine, files beginning with "." are treated as hidden files (eg. in ls) and the suffix can be considered to be empty. As opposed to "a.desktop".
 
=={{header|SNOBOL4}}==
{{works with|SNOBOL4, SPITBOL for Linux}}
<syntaxhighlight lang="snobol4">
* Program: extract_extension.sbl
* To run: sbl extract_extension.sbl
* Description: Extract file extension
* Comment: Tested using the Spitbol for Linux version of SNOBOL4
 
filenames =
+ "http://example.com/download.tar.gz,"
+ "CharacterModel.3DS,"
+ ".desktop,"
+ "document,"
+ "document.txt_backup,"
+ "/etc/pam.d/login"
 
epat = ((span(&lcase &ucase '0123456789') ".") | "") . ext
p0
filenames ? (break(',') . s ',') | (len(1) rem) . s = "" :f(end)
reverse(s) ? epat
ext = reverse(ext)
output = ""
output = "Extension from file '" s "' is '" ext "'"
:(p0)
END
</syntaxhighlight>
{{out}}
<pre>
Extension from file 'http://example.com/download.tar.gz' is '.gz'
 
Extension from file 'CharacterModel.3DS' is '.3DS'
 
Extension from file '.desktop' is '.desktop'
 
Extension from file 'document' is ''
 
Extension from file 'document.txt_backup' is ''
 
Extension from file '/etc/pam.d/login' is ''
</pre>
 
=={{header|Standard ML}}==
 
This just demonstrates how to functionally extend the built-in function to the alpha-numeric restriction. Since file names starting with '.' are supposed to be "hidden" files in Unix, they're not considered as an extension.
<syntaxhighlight lang="sml">fun fileExt path : string =
getOpt (Option.composePartial (Option.filter (CharVector.all Char.isAlphaNum), OS.Path.ext) path, "")
 
val tests = [
"http://example.com/download.tar.gz",
"CharacterModel.3DS",
".desktop",
"document",
"document.txt_backup",
"/etc/pam.d/login"
]
 
val () = app (fn s => print (s ^ " -> \"" ^ fileExt s ^ "\"\n")) tests</syntaxhighlight>
{{out}}
<pre>http://example.com/download.tar.gz -> "gz"
CharacterModel.3DS -> "3DS"
.desktop -> ""
document -> ""
document.txt_backup -> ""</pre>
 
=={{header|Tcl}}==
Line 2,126 ⟶ 2,829:
Tcl's built in [http://wiki.tcl.tk/10072 file extension] command already almost knows how to do this, except it accepts any character after the dot. Just for fun, we'll enhance the builtin with a new subcommand with the limitation specified for this problem.
 
<langsyntaxhighlight Tcllang="tcl">proc assert {expr} { ;# for "static" assertions that throw nice errors
if {![uplevel 1 [list expr $expr]]} {
set msg "{$expr}"
Line 2,155 ⟶ 2,858:
set res ""
assert {[file ext $file] eq $ext}
}</langsyntaxhighlight>
 
=={{header|TUSCRIPT}}==
<langsyntaxhighlight lang="tuscript">
$$ MODE DATA
$$ testcases=*
Line 2,189 ⟶ 2,892:
PRINT testcase, " has extension ", extension
ENDLOOP
</syntaxhighlight>
</lang>
Output:
<pre>
Line 2,201 ⟶ 2,904:
 
=={{header|VBScript}}==
<langsyntaxhighlight lang="vb">Function fileExt(fname)
Set fso = CreateObject("Scripting.FileSystemObject")
Set regex = new regExp
Line 2,226 ⟶ 2,929:
Wscript.Echo "NAME:",name
Wscript.Echo " EXT:","<" & fileExt(name) & ">"
Next</langsyntaxhighlight>
 
{{Out}}
Line 2,244 ⟶ 2,947:
=={{header|Visual Basic}}==
{{works with|Visual Basic|6}}
<langsyntaxhighlight lang="vb">Option Explicit
'-----------------------------------------------------------------
Function ExtractFileExtension(ByVal Filename As String) As String
Line 2,289 ⟶ 2,992:
s = "a.b.1~2"
Debug.Assert ExtractFileExtension(s) = ""
End Sub</langsyntaxhighlight>
 
=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-pattern}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./pattern" for Pattern
import "./fmt" for Fmt
 
var p = Pattern.new("/W") // matches any non-alphanumeric character
 
var extractFileExtension = Fn.new { |path|
if (path.isEmpty) return ""
var fileName = path.split("/")[-1]
if (path == fileName) fileName = path.split("\\")[-1]
var splits = fileName.split(".")
if (splits.count == 1) return ""
var ext = splits[-1]
return p.isMatch(ext) ? "" : "." + ext
}
 
var paths = [
"http://example.com/download.tar.gz",
"CharacterModel.3DS",
".desktop",
"document",
"document.txt_backup",
"/etc/pam.d/login",
"c:\\programs\\myprogs\\myprog.exe", // using back-slash as delimiter
"c:\\programs\\myprogs\\myprog.exe_backup" // ditto
]
for (path in paths) {
var ext = extractFileExtension.call(path)
Fmt.print("$-37s -> $s", path, ext.isEmpty ? "(empty string)" : ext)
}</syntaxhighlight>
 
{{out}}
<pre>
http://example.com/download.tar.gz -> .gz
CharacterModel.3DS -> .3DS
.desktop -> .desktop
document -> (empty string)
document.txt_backup -> (empty string)
/etc/pam.d/login -> (empty string)
c:\programs\myprogs\myprog.exe -> .exe
c:\programs\myprogs\myprog.exe_backup -> (empty string)
</pre>
 
=={{header|XPL0}}==
<syntaxhighlight lang="xpl0">func Ext(Str); \Return address of extension
char Str; int I, C, End;
string 0;
[I:= 0;
while Str(I) do I:= I+1;
End:= I;
loop [I:= I-1;
if Str(I) = ^. then return @Str(I);
if I = 0 then return @Str(End); \no dot found, return null
C:= Str(I);
if C>=^A & C<=^Z ! C>=^a & C<=^z ! C>=^0 & C<=^9 then \OK
else return @Str(End); \illegal char, return null
];
];
 
[Text(0, Ext("http://example.com/download.tar.gz")); CrLf(0);
Text(0, Ext("CharacterModel.3DS")); CrLf(0);
Text(0, Ext(".desktop")); CrLf(0);
Text(0, Ext("document")); CrLf(0);
Text(0, Ext("document.txt_backup")); CrLf(0);
Text(0, Ext("/etc/pam.d/login")); CrLf(0);
]</syntaxhighlight>
 
{{out}}
<pre>
.gz
.3DS
.desktop
 
 
 
</pre>
 
=={{header|zkl}}==
 
The File object has a method splitFileName that does just that, returning a list of the parts. The method knows about the OS it was compiled on (Unix, Windows).
<langsyntaxhighlight lang="zkl">fcn extractFileExtension(name){
var [const] valid=Walker.chain(".",["a".."z"],["A".."Z"],["0".."9")).pump(String);
ext:=File.splitFileName(name)[-1];
if(ext - valid) ext="";
ext
}</langsyntaxhighlight>
<langsyntaxhighlight lang="zkl">foreach nm in (T("http://example.com/download.tar.gz","CharacterModel.3DS",
".desktop","document",
"document.txt_backup","/etc/pam.d/login")){
println("%35s : %s".fmt(nm,extractFileExtension(nm)));
}</langsyntaxhighlight>
{{out}}
Note: on Unix, .desktop is a hidden file, not an extension.
2,012

edits