path
模块是一个很基本的模块,其中最主要的方法也就是resolve
方法(前篇),也因为比较复杂,所以,使用以及源码的解析,占用了整个篇幅,本篇就对path
模块的其他属性和方法。
path
模块的方法和属性
话不多说,直接开始。
1:normalize方法
话不多说,还是直接看这个方法吧,使用方法:
path.normalize(p);
用途:该方法将非标准路径字符串转换为标准路径字符串,在转换的过程中执行以下处理:
1:解析路径中字符串中的“..”字符串和“.”字符串,返回解析后的标准路径:
2:将多个斜杠字符串转换为一个斜杠字符串,这个见上面的示例。
3:将斜杠转换为反斜杠,也可以见上面的示例。
4:如果路径字符串以斜杠字符串串结尾,则在转换完之后的字符串结尾保留该斜杠。
5:如果输入的字符串中,没有获取都对应的子目录,则在末尾加“.”。
下面,就对上述的功能进行验证,在REPL
模式下,输入以下代码:
> var path = require("path");
undefined
>console.log(path.normalize("..//a/b"));
..\a\b
undefined
>console.log(path.normalize("./a/b/"));
a\b\
undefined
>path.normalize("..//a/b")
..\\a\\b
//这里加console和不加的情况下,为什么会显示不同的结果呢?
//因为不加的时候,第一个反斜杠是用于转义的,当使用的时候,会被转义
//而console中的结果,是转义之后的,这就是差距
>console.log(path.normalize("C:"));
C:.
undefined
>console.log(path.normalize(".\b"));
.
undefined
//这里为什么只反会了一个“.”,b去哪里了?不是去哪里了,
//而是“\b”中的斜杠,被认为是转义字符了,所以可以如下写:
>console.log(path.normalize(".\b"));
.\b
undefined
看完了示例,继续看下,下面来看下,normalize
的源码时如何实现的:
//win32StatPath,normalizeArray,normalizeUNCRoot
//这三个函数,请参考上一篇中的源码分析部分,这里不再占地
win32 = {};
win32.normalize = function(path) {
var result = win32StatPath(path),
device = result.device,
isUnc = result.isUnc,
isAbsolute = result.isAbsolute,
tail = result.tail,
trailingSlash = /[\\\/]$/.test(tail);
// Normalize the tail path
//对tail进行格式化,并且把"..//a/b"中的"."去掉,把开头的"//"去掉
tail = normalizeArray(tail.split(/[\\\/]+/g), !isAbsolute).join('\\');
if (!tail && !isAbsolute) {
//在tail为空,即类似“C:”这个时候,给结尾添加"."符合
tail = '.';
}
if (tail && trailingSlash) {
//如果tail存在,并且tail最初的时候,结尾有斜杠,则在这里给它在添加上
tail += '\\';
}
// Convert slashes to backslashes when `device` points to an UNC root.
// Also squash multiple slashes into a single one where appropriate.
if (isUnc) {
//格式化一下:
device = normalizeUNCRoot(device);
}
//返回值~~
return device + (isAbsolute ? '\\' : '') + tail;
};
2:isAbsolute方法
判断一个路径是否为一个绝对的路径,使用方法为:
path.isAbsolute(p);
返回值为true
或者false
,基本上,只有两种情况下,该属性才会返回true
,即输入值为“C:\d”
和“//a/b”
开头的字符串中。这个是我在源码中,按照源码的逻辑,总结出来的,所以,这里就不再举例了,直接看下源码。
win32.isAbsolute = function(path) {
//按照源码分析,基本上path的开头字母只有等于“C:/a”和“//a/b”的情况下,才会返回true
return win32StatPath(path).isAbsolute;
只要win32StatPath内部源码的分析,请参考前一篇中的源码。
};
3:join方法
看到这个方法,立马就想到了数组的join
方法,就是用于将多个参数值字符串结合为一个路径字符串,但是,还是有一些区别的。后面来看区别,先看用法:
path.join([path1],[path2],[...]);
该方法返回值将是输入参数值结合而成的路径字符串。这属于比较简单的方法,看下面在REPL模式下的示例:
>var path = require("path");
undefined
>path.join("a","b");
'a\\b'
>console.log(path.join("a","b"));
a\b
undefined
>path.join();
.
说这个方法使用简单,因为基本上,也就这两种写法了,
1:当无参数传入的时候,返回”.”。
2:当有参数传入的时候,返回以”\”连接在一起的字符串。
下面看下源码中的实现逻辑:
win32.join = function() {
var paths = [];
for (var i = 0; i < arguments.length; i++) {
//判断每一个输入元素是否为字符串
//如果不是,则抛出一个类型错误
var arg = arguments[i];
if (!util.isString(arg)) {
throw new TypeError('Arguments to path.join must be strings');
}
if (arg) {
//如果是字符串,则添加到一个数组中
paths.push(arg);
}
}
var joined = paths.join('\\');
//使用数组的join方法,把上面生成的数组,链接成字符串,以特定的分隔符
//这里,也就是说,数组的join和path的join的区别。
if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) {
//如果path数组的第一个元素支付成,不是以"\\a"(双斜杠加一个非斜杠)开头,
//则要把最初的多个斜杠,替换换为"\\"(转义符和反斜杠)
//输出的值,为一个反斜杠
joined = joined.replace(/^[\\\/]{2,}/, '\\');
}
//把生成的字符串,通过normalize方法,进行标准化
//在内部,path.join()返回"."也是normalize方法处理的结果。
return win32.normalize(joined);
};
4:relative方法
该方法传入两个参数,用于获取两个路径之间的相对关系,使用方法如下:
path.relative(from , to);
在该方法中,传入的路径,可以是绝对路径,也可以是相对路径,可以是目录的路径,也可以是文件的路径。
返回值是一个路径,该路径的计算方法是:把第一个参数作为当前路径,使用相对路径来指定第二个路径时,应该使用的相对路径表达式,如果两个路径不位于同一个硬盘分区,则返回第二个参数的值路径的绝对路径。
REPL模式下示例:
>var path = require("path");
undefined
>path.relative("a/b","a/c");
"..\\c"
>path.relative("a/b","D:/a/c");
"D:\\a\\c"
基本上,relative
的使用,就只有上面的两种情况,相同的盘符,和不同的盘符。
那接下来,就可以看下源码中的逻辑了:
win32.relative = function(from, to) {
from = win32.resolve(from);
to = win32.resolve(to);
//使用resolve获取到对应的绝对路径,关于resolve的详情,请参考前一篇文章
// windows is not case sensitive
//把字符串都转换为小写字母,window下,大小写字符的文件夹,会被认为同一个文件夹
//文件名也是,如果完全相同的英文组成的一个文件名,大小写不同,
//也会被认为同一个文件
var lowerFrom = from.toLowerCase();
var lowerTo = to.toLowerCase();
var toParts = trimArray(to.split('\\'));
//trimArray方法,是规范数组,把数组开头和结尾的空元素去除,
//只保留有效的元素
var lowerFromParts = trimArray(lowerFrom.split('\\'));
var lowerToParts = trimArray(lowerTo.split('\\'));
var length = Math.min(lowerFromParts.length, lowerToParts.length);
//查找相同的目录,只要按照短数组对比即可
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (lowerFromParts[i] !== lowerToParts[i]) {
//查找它们的相同的目录,直到查找到不同的位置截止
//这里,表示从第i个元素开始,它们的路径就开始有所差别
samePartsLength = i;
break;
}
}
if (samePartsLength == 0) {
//如果第一个元素就不同,则表示在不同的盘符,所以返回to的绝对路径
return to;
}
//否则为相同盘符下。
var outputParts = [];
for (var i = samePartsLength; i < lowerFromParts.length; i++) {
//从form的路径到两个共同的路径下,需要的返回几级父目录
//返回一次父目录,则添加一个"..",所以这里在计算,from返回到相同父目录的表达式
outputParts.push('..');
}
//把to绝对路径的与from绝对路径不同的部分,于前面的返回的父目录进行连接
//就变成了from路径到to路径的相对路径了。
outputParts = outputParts.concat(toParts.slice(samePartsLength));
//输出
return outputParts.join('\\');
};
5:_makeLong方法
说这个之前,闲来看下UNC
的概念,下面的定义摘自百度百科:
对于网络服务器上的目标文件,可使用“通用命名约定 (UNC)” (UNC:“统一命名约定”地址,用于确定保存在网络服务器上的文件位置。这些地址以两个反斜线 (\\) 开头,并提供服务器名、共享名和完整的文件路径。)地址。这些地址以“file:\\”开始并提供服务器名、共享名和文件的完整路径。例如,“file:\\server\share\path\project file.mpp”是绝对 UNC 地址。
_makeLong
方法,就是为了对路径中属于“C:\a”
和“//a/b”(UNC)
协议的两种路径进行一些额外的判断,并处理成相应的的格式。
传入0
或1
个参数,当传入0
个参数时,返回为undefined
当传入一个空字符串时,则返回一个””
当传入非空字符串时,根据生成绝对路径的格式,返回不同的路径字符串
使用方法:
path._makeLong(path1);
那么就看下,传入的不同值时,返回值是哪些吧,依然在REPL模式下进行处理:
>var path = require("path");
undefined
>path._makeLong();
undefined
>path._makeLong("");
""
>path._makeLong("a/b");
"\\\\?\\C:\\nodejs\\fs\\a\\b"
//在绝对路径前,添加了“\\\\?\\”字符串
>path._makeLong("//a/b");
"\\\\?\\UNC\\a\\b\\"
//在前面添加了“\\\\?\\UNC\\”字符串
其实,在我当前,值关心用法以及源码,而不知道这些功能,是在何时使用的情况,还真是有些蛋疼,不过不管如何,学习总归是没错的,所以,就先坚持下去吧。
继续看下源码中的处理:
win32._makeLong = function(path) {
// Note: this will *probably* throw somewhere.
if (!util.isString(path))
return path;
//如果传入的值,不是一个字符串,则直接返回该值。
//如果传入值字符串为一个空字符串,则直接返回一个空字符串
if (!path) {
return '';
}
var resolvedPath = win32.resolve(path);
//如果字符串不为空,则根据该字符串,返回该字符串的绝对路径
if (/^[a-zA-Z]\:\\/.test(resolvedPath)) {
//如果,该字符串是一个盘符的绝对路径,则处理成该种类型的字符串
// path is local filesystem path, which needs to be converted
// to long UNC path.
return '\\\\?\\' + resolvedPath;
} else if (/^\\\\[^?.]/.test(resolvedPath)) {
//如果字符串,是以双斜杠和非“?”和“.”开头的,则处理为该种类型的字符串
// path is network UNC path, which needs to be converted
// to long UNC path.
return '\\\\?\\UNC\\' + resolvedPath.substring(2);
}
//返回代表路径的字符串
return path;
};
看到这里的UNC
协议,对于之前为什么在resolve
中,有个正则splitDeviceRe
对path
的处理,也就变得可以理解了,所以,当你碰到不懂的问题时,不能就此停止,继续看下去,总能找到其他的相关知识点,慢慢让你理解这些。
概总
还有几个方法,因为篇幅有限,所以分到另外一篇中去了。
本篇内容主要参考自:“Nodejs”源码和“Nodejs权威指南”
写得挺好~加油~