需要在Javascript中实现一个basename函数。

86

我需要一个短的基本名称函数(一行代码?)用于Javascript:

basename("/a/folder/file.a.ext") -> "file.a"
basename("/a/folder/file.ext") -> "file"
basename("/a/folder/file") -> "file"

这将会剥离路径和任何扩展名。

更新: 对于文件名开头的点应该视为特殊文件。

basename("/a/folder/.file.a.ext") -> ".file.a"
basename("/a/folder/.file.ext") -> ".file"
basename("/a/folder/.file") -> ".file" # empty is Ok
basename("/a/folder/.fil") -> ".fil"  # empty is Ok
basename("/a/folder/.file..a..") -> # does'nt matter

basename('.foo') 应该是什么? - bobince
1
@bobince - 我相信是空字符串。 - Vilx-
我刚刚到达了/^(?:.*\/(?=[^/]))?([^/]*)\/*$/.exec(pathname)[1],这是一个更Unixy或有用的基本名称:任何尾随斜杠都将被忽略,如果输入为空或仅由斜杠组成,则为空字符串,否则为最右侧的非斜杠单词。欢迎评论(请@我)! - mirabilos
19个回答

118
function basename(path) {
   return path.split('/').reverse()[0];
}

将路径分解为组成目录和文件名的部分,然后返回最后一部分(即文件名),该部分是数组的最后一个元素。

200
path.split(/[\\/]/).pop(); - 可用于两种分隔符,无需反转,返回最后一个元素。 - 3DFace
我认为你应该在函数内部使用 this 而不是变量名,这样不仅能选择第一个类元素。 - Salem
@3DFace 在你的代码中,pop(). 之间似乎有几个奇怪的字符 (‌​)。 - Lars Nyström
1
@LarsNyström,很奇怪,因为只有ASCII字符。这是给你的URL编码版本:
path.split(%2F%5B%5C%5C%2F%5D%2F).pop()%3B
希望你能解码 :)
- 3DFace
11
有时候第一条评论的票数比答案和被采纳的答案都高,这种情况让人感到尴尬。0_o +1 @3DFace - Frits
显示剩余3条评论

83
function baseName(str)
{
   var base = new String(str).substring(str.lastIndexOf('/') + 1); 
    if(base.lastIndexOf(".") != -1)       
        base = base.substring(0, base.lastIndexOf("."));
   return base;
}

如果你的分隔符既可以是/也可以是\,你需要修改代码并添加一行。


@Sibidharan 你能详细说明一下吗? - Nivas
为什么需要 new String(str) 而不是只用 str.substring(...) - bluenote10

41
任何上述方法都可行,尽管它们不注重速度/内存 :-).
一个更快/更简单的实现应该尽可能使用较少的函数/操作。RegExp 是一个糟糕的选择,因为它在实际使用中会消耗大量资源,而我们可以更轻松地实现相同的结果。
当您想要包括扩展名的文件名时(实际上这是基本名称的真正定义)的一种实现:
function basename(str, sep) {
    return str.substr(str.lastIndexOf(sep) + 1);
}

如果您需要一个定制的basename实现,它还需要去除扩展名,我建议您使用针对该情况的特定扩展名剥离函数,您可以在需要时调用它。
function strip_extension(str) {
    return str.substr(0,str.lastIndexOf('.'));
}

使用示例:

basename('file.txt','/'); // real basename
strip_extension(basename('file.txt','/')); // custom basename

他们被分开,这样你就可以将它们结合起来得到三种不同的结果:去除扩展名、获取真实基本名称和获取自定义基本名称。我认为这是比其他方法更优雅的实现方式。

2
这是最准确和最优的答案。谢谢! - Luciano Fantuzzi
速度只在循环内部特别是最内层循环中才重要。内存只在同时分配许多对象,特别是大对象时才重要。对于一个不是关键代码的“basename”函数,这个答案是完美的。 - David Spector
如果您同时具有带扩展名和不带扩展名的情况,我修改了strip_extension代码:返回str.substr(0,(str.lastIndexOf('.')==-1) ? str.length : str.lastIndexOf('.')); - QuickPrototype
substr已被弃用,请使用slice替代。 - maxime schoeni

21

如果可以的话,尝试使用现有的包。 http://nodejs.org/api/path.html

var path = require('path');
var str = '/path/to/file/test.html'

var fileNameStringWithoutExtention = path.basename(str, '.html');
// returns 'test'

// let path determine the extension
var fileNameStringWithoutExtention = path.basename(str, path.extname(str));
// returns 'test'

其他例子:

var pathString = path.dirname(str);
var fileNameStringWithExtention = path.basename(str);
var fullPathAndFileNameString = path.join(pathString, fileNameString);

3
@Randy:正是我需要的!在nodejs下,无需重复造轮子。 (@Tazo,你在下载方面是错误的。它已经存在了。) - tivnet
@Randy 我不知道为什么人们使用自定义函数来获取文件名,而不是你所做的方式。 - Azhar Uddin Sheikh

7
 basename = function(path) {
    return path.replace(/.*\/|\.[^.]*$/g, '');
 }

将以斜杠结尾的任何内容.*\/或点 - 一些非点 - 结尾\.[^.]*$替换为空


4
与上面提供的错误信息相反,正则表达式非常高效。唯一的问题是,在程序的生命周期中尽可能将其编译一次。
以下是一个解决方案,可以同时获得dirname和basename。
const rx1 = /(.*)\/+([^/]*)$/;    // (dir/) (optional_file)
const rx2 = /()(.*)$/;            // ()     (file)

function dir_and_file(path) {
  // result is array with
  //   [0]  original string
  //   [1]  dirname
  //   [2]  filename
  return rx1.exec(path) || rx2.exec(path);
}
// Single purpose versions.
function dirname(path) {
  return (rx1.exec(path) || rx2.exec(path))[1];
}
function basename(path) {
  return (rx1.exec(path) || rx2.exec(path))[2];
}

就性能而言,我没有进行过测量,但我预计这个解决方案的范围与此页面上最快的其他解决方案相同,但是这个解决方案做得更多。实际世界性能的帮助在于rx1将匹配大多数实际路径,因此rx2很少被执行。
这里是一些测试代码。
function show_dir(parts) {
  console.log("Original str :"+parts[0]);
  console.log("Directory nm :"+parts[1]);
  console.log("File nm      :"+parts[2]);
  console.log();
}
show_dir(dir_and_file('/absolute_dir/file.txt'));
show_dir(dir_and_file('relative_dir////file.txt'));
show_dir(dir_and_file('dir_no_file/'));
show_dir(dir_and_file('just_one_word'));
show_dir(dir_and_file('')); // empty string
show_dir(dir_and_file(null)); 

以下是测试代码的输出结果:

# Original str :/absolute_dir/file.txt
# Directory nm :/absolute_dir
# File nm      :file.txt
# 
# Original str :relative_dir////file.txt
# Directory nm :relative_dir
# File nm      :file.txt
# 
# Original str :dir_no_file/
# Directory nm :dir_no_file
# File nm      :
# 
# Original str :just_one_word
# Directory nm :
# File nm      :just_one_word
# 
# Original str :
# Directory nm :
# File nm      :
# 
# Original str :null
# Directory nm :
# File nm      :null

顺便提一下,“node”有一个内置模块叫做“path”,其中包含“dirname”和“basename”。Node的“path.dirname()”函数准确地模仿了“bash” shell的“dirname”的行为,但这好吗?它的功能如下:
  • path==""(空字符串)时,生成'.'(点)。
  • path=="just_one_word"时,生成'.'(点)。
  • path=="dir_no_file/"时,生成'.'(点)。
我更喜欢上面定义的函数的结果。

4
就像 @3DFace 所评论的那样:
path.split(/[\\/]/).pop(); // works with both separators

如果你喜欢原型:

String.prototype.basename = function(sep) {
  sep = sep || '\\/';
  return this.split(new RegExp("["+sep+"]")).pop();
}

测试:

var str = "https://dev59.com/A2865IYBdhLWcg3wa-A0";
alert(str.basename());

将返回“需要在JavaScript中使用基础名称函数”。
享受!

4

使用现代(2020年)的JavaScript代码:

function basename (path) {
  return path.substring(path.lastIndexOf('/') + 1)
}
console.log(basename('/home/user/file.txt'))


2

另一个好的解决方案:

function basename (path, suffix) {
  //  discuss at: http://locutus.io/php/basename/
  // original by: Kevin van Zonneveld (http://kvz.io)
  // improved by: Ash Searle (http://hexmen.com/blog/)
  // improved by: Lincoln Ramsay
  // improved by: djmix
  // improved by: Dmitry Gorelenkov
  //   example 1: basename('/www/site/home.htm', '.htm')
  //   returns 1: 'home'
  //   example 2: basename('ecra.php?p=1')
  //   returns 2: 'ecra.php?p=1'
  //   example 3: basename('/some/path/')
  //   returns 3: 'path'
  //   example 4: basename('/some/path_ext.ext/','.ext')
  //   returns 4: 'path_ext'

  var b = path
  var lastChar = b.charAt(b.length - 1)

  if (lastChar === '/' || lastChar === '\\') {
    b = b.slice(0, -1)
  }

  b = b.replace(/^.*[\/\\]/g, '')

  if (typeof suffix === 'string' && b.substr(b.length - suffix.length) === suffix) {
    b = b.substr(0, b.length - suffix.length)
  }

  return b
}

from: http://locutus.io/php/filesystem/basename/


2

快速匹配路径,不需要使用正则表达式,适用于路径类型'/'和'\'。同时获取文件扩展名:

function baseName(str)
{
    let li = Math.max(str.lastIndexOf('/'), str.lastIndexOf('\\'));
    return new String(str).substring(li + 1);
}

为什么不使用 || 代替 Math.max 函数呢?当然,这种方法容易受到包含 / 和 \ 字符的无效字符串的影响。但是,如果完整路径有效,则不应发生这种情况。最后,之前有人问过,但我没有看到答案,为什么要使用 new String 而不是只用 String? - user15334226

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接