nodejs中的文件操作的基础,path模块(二)

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)协议的两种路径进行一些额外的判断,并处理成相应的的格式。

传入01个参数,当传入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中,有个正则splitDeviceRepath的处理,也就变得可以理解了,所以,当你碰到不懂的问题时,不能就此停止,继续看下去,总能找到其他的相关知识点,慢慢让你理解这些。

概总

还有几个方法,因为篇幅有限,所以分到另外一篇中去了。

本篇内容主要参考自:“Nodejs”源码和“Nodejs权威指南”

本文地址:http://www.zhangyunling.com/?p=411

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

nodejs中的文件操作的基础,path模块(二)》有1个想法

  1. 写得挺好~加油~