今天介绍一下nodejs Path的源代码分析,Path的API文档在https://nodejs.org/dist/latest-v5.x/docs/api/path.html,使用相对简单,在API文档中,须要特别说明的是例如以下的文字:
This module contains utilities for handling and transforming file paths. Almost all these methods perform only string transformations. The file system is not consulted to check whether paths are valid.也就是说这个类的作用是文件路径字符串的处理。而非验证文件路径的有效性,也就是说: path能够从一个文件路径中利用字符串的处理获取详细的文件夹,文件后缀。文件名称等消息,可是我们无法推断这个路径是否合法有效。
API的类型分为以下几类:
- 获取文件路径的详细信息,如文件夹。文件后缀,文件名称等
- 当前系统的一些属性,如分隔符,平台等
- 文件路径的合成,相对路径的解析和标准化等。
首先查看该模块的导出:
// 当前是否是Windows 平台var isWindows = process.platform === ‘win32‘;// 假设是windows,直接导出的win32,否则为posixif (isWindows) ?module.exports = win32;else /* posix */ ?module.exports = posix;// 同一时候导出中含有属性win32 和posixmodule.exports.posix = posix;module.exports.win32 = win32;从上面的源代码能够看出,其导出的为win32 或者posix,后面能够看到这是两个对象。 一个代表的是windows平台,一个是非windows的可移植性操作系统接口。一般就是指Linux。之全部两个不同的平台对象,是由于windows和linux上的文件路径,分隔符等不一样,所以分开处理,可是事实上现的API接口都基本都一样。也就是win32和posix对象里面的属性和方法基本一致。
为简单和避免反复。我们仅仅分析win32对象的API源代码与对象。
查看一下源代码中的帮助函数:
// resolves . and .. elements in a path array with directory names there// must be no slashes or device names (c:\) in the array// (so also no leading and trailing slashes - it does not distinguish// relative and absolute paths)// 解决文件文件夹中的相对路径// @parts 文件文件夹数组,从0- 高位分别代表一级文件夹// @allowAboveRoot 布尔值。代表能否够超过根文件夹// @returns, 解决掉相对路径后的数组,比方说数组 // [‘/test‘, ‘/re‘, ‘..‘]将会返回 [‘/test‘]function normalizeArray(parts, allowAboveRoot) { ?// 返回值 ?var res = []; ?// 遍历数组。处理数组中的相对路径字符 ‘.‘ 或者‘..‘ ?for (var i = 0; i < parts.length; i++) { ???// 取得当前的数组的字符 ???var p = parts[i]; ???// ignore empty parts ???// 对空或者‘.‘不处理 ???if (!p || p === ‘.‘) ?????continue; ???// 处理相对路径中的‘..‘ ???if (p === ‘..‘) { ?????if (res.length && res[res.length - 1] !== ‘..‘) { ?????// 直接弹出返回队列,当没有到达根文件夹时 ???????res.pop(); ?????} else if (allowAboveRoot) { ??????//allowAboveRoot 为真时。插入‘..‘ ???????res.push(‘..‘); ?????} ???} else { ????// 非 ‘.‘ 和‘..‘直接插入返回队列。 ??????res.push(p); ???} ?} ?// 返回路径数组 ?return res;}// returns an array with empty elements removed from either end of the input// array or the original array if no elements need to be removed//返回带有从头和尾空元素的队列。
假设没有空元素,直接返回原来的队列// 比方说 [‘undefined‘, 1, 2, ‘undefined‘, 3, ‘undefined‘] 会返回// [1, 2, ‘undefined‘, 3]function trimArray(arr) { ?// 确定队列的最后一个索引 ?var lastIndex = arr.length - 1; ?var start = 0; ?//确定队列中从開始位置的第一个非空元素 ?for (; start <= lastIndex; start++) { ???if (arr[start]) ?????break; ?} //确定队列中从结束位置的第一个非空元素 ?var end = lastIndex; ?for (; end >= 0; end--) { ???if (arr[end]) ?????break; ?} ?// 假设没有空元素的情况,直接返回原来的数组 ?if (start === 0 && end === lastIndex) ???return arr; ?// 处理异常情况 ???if (start > end) ???return []; ??//返回非空的数组 ?return arr.slice(start, end + 1);}// Regex to split a windows path into three parts: [*, device, slash,// tail] windows-only// 正則表達式。将windows的路径转化成三部分。文件夹。/ 或者尾部。// 分析这个正則表達式,能够看出,其有三部分结果会输出matchs数组中:// part1: ^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)? // 这部分说明的是开头部分,是以字符开头("D:")。或者以"//" "\\"开头// 加上"/abc" 通常这部分会生成两个结果,一个就是盘符,一个就是盘符后// 注意后面加上了()?,说明是尽可能少的匹配,也就是说遇到了"D://sda/" //这种,直接返回"D:"// part2: [\\\/])? 返回是否有/ 或者\// part3: ([\s\S]*?)$ 第三部分就是随意的字符结尾的匹配// 随意的字符串或者文件夹,会分成四部分,如文档所说 [*, device, slash,// tail]var splitDeviceRe = ???/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?
([\\\/])?([\s\S]*?)$/;// Regex to split the tail part of the above into [*, dir, basename, ext]// 分析上述正則表達式一样,将分成四部分为[*, dir, basename, ext]var splitTailRe = ???/^([\s\S]*?)((?
:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?
:[\\\/]*)$/;// 这个就是win32对象,导出的对象var win32 = {};// Function to split a filename into [root, dir, basename, ext]// 帮助函数,用于分离字符串中的根,文件文件夹,文件名称。和后缀名function win32SplitPath(filename) { ?// Separate device+slash from tail ?// 直接利用正則表達式。分析出device。也就是盘符 和盘符后面的尾 ?var result = splitDeviceRe.exec(filename), ?????device = (result[1] || ‘‘) + (result[2] || ‘‘), ?????tail = result[3] || ‘‘; ?// Split the tail into dir, basename and extension ?// 然后依据尾巴。来分析文件文件夹。基本名和后缀 ?var result2 = splitTailRe.exec(tail), ?????dir = result2[1], ?????basename = result2[2], ?????ext = result2[3]; ?//返回一个数组,含有盘符,文件夹名,基本名和后缀 ?//比如:‘C:\\path\\dir\\index.html‘ ?//參数。会返回 ?//{ ?// ??root : "C:\\", ?// ?dir : "C:\\path\\dir", ?// ?base : "index.html", ?// ?ext : ".html", ?// ?name : "index" ?// } ?return [device, dir, basename, ext];}// 获取文件路径详细信息function win32StatPath(path) { ?// 和上述的函数一样。解析路径中的信息。
?var result = splitDeviceRe.exec(path), ?????device = result[1] || ‘‘, ?????// 推断是否 为UNC path ?????isUnc = !!device && device[1] !== ‘:‘; ?// 返回详细的对象,盘符,是否为统一路径,绝对路径, 以及结尾 ?return { ???device: device, ???isUnc: isUnc, ???isAbsolute: isUnc || !!result[2], // UNC paths are always absolute ???tail: result[3] ?};}// 帮助函数。将路径UNC路径标准化成\\pathname\\function normalizeUNCRoot(device) { ?return ‘\\\\‘ + device.replace(/^[\\\/]+/, ‘‘).replace(/[\\\/]+/g, ‘\\‘);}
先分析API的一部分内容,也就是直接从文件路径中获取文件夹,根,后缀等信息。
// 获取文件文件夹win32.dirname = function(path) { ?// 这个直接利用的的上述的帮助函数。返回了一个数组 ?// [device, dir, basename, ext]; ??var result = win32SplitPath(path), ?????// 获取盘符 ?????root = result[0], ?????// 获取文件文件夹 ?????dir = result[1]; ?// 非法參数,直接返回当前文件夹:) ??if (!root && !dir) { ???// No dirname whatsoever ???return ‘.‘; ?} ?// 去掉 ‘/‘ ?if (dir) { ???// It has a dirname, strip trailing slash ???dir = dir.substr(0, dir.length - 1); ?} ?// 返回结果 ?return root + dir;};// 获取名字win32.basename = function(path, ext) { ?// 获取文件基本名 ?var f = win32SplitPath(path)[2]; ?// TODO: make this comparison case-insensitive on windows? ?//进一步去掉扩展。如,index.js ?去掉js ?if (ext && f.substr(-1 * ext.length) === ext) { ???f = f.substr(0, f.length - ext.length); ?} ?// 返回基本名 ?return f;};// 获取后缀win32.extname = function(path) { ??// 直接使用帮助函数的返回数组。 ?return win32SplitPath(path)[3];};// 从path对象中获取完整路径,这个函数和parse全然相反// pathObject 理论上含有例如以下属性// root, dir, base, ext, namewin32.format = function(pathObject) { ?// 第一步检查參数,确觉得Object对象 ?if (!util.isObject(pathObject)) { ???throw new TypeError( ???????"Parameter ‘pathObject‘ must be an object, not " + typeof pathObject ???); ?} ?// 确定root ?var root = pathObject.root || ‘‘; ?// 确保root属性为string类型 ?if (!util.isString(root)) { ???throw new TypeError( ???????"‘pathObject.root‘ must be a string or undefined, not " + ???????typeof pathObject.root ???); ?} ?// 确定文件文件夹 ?var dir = pathObject.dir; ?// 确定base,事实上也就是文件名称 ?var base = pathObject.base || ‘‘; ?if (!dir) { ???// 没有路径的情况下,返回文件名称 ????return base; ?} ?// 确认文件夹后缀有切割符的情况下,直接生成返回 ?if (dir[dir.length - 1] === win32.sep) { ???return dir + base; ?} ??// 确认文件夹后缀没有切割符的情况下。加上分隔符返回 ?return dir + win32.sep + base;};// 从pathString中得到详细的对象。和上面的format左右相反// win32.parse = function(pathString) { ?// 检查详细的參数,确保參数为string ?if (!util.isString(pathString)) { ???throw new TypeError( ???????"Parameter ‘pathString‘ must be a string, not " + typeof pathString ???); ?} ?// 利用帮助函数返回数组信息 [device, dir, basename, ext]; ?var allParts = win32SplitPath(pathString); ?// 确保參数为文件路径 ?if (!allParts || allParts.length !== 4) { ???throw new TypeError("Invalid path ‘" + pathString + "‘"); ?} ?// 生成pathObject对象返回 ?return { ???root: allParts[0], ???dir: allParts[0] + allParts[1].slice(0, -1), ???base: allParts[2], ???ext: allParts[3], ???name: allParts[2].slice(0, allParts[2].length - allParts[3].length) ?};};// 分隔符win32.sep = ‘\\‘;// 定界符win32.delimiter = ‘;‘;
从上面的函数能够看出。都是正則表達式字符串的处理为基础。正如API文档中所说。没有不论什么文件路径的验证。
以下来查看一下相对路径,绝对路径。路径组合相关的API的源代码:
// path.resolve([from ...], to)// 从函数參数中生成绝对路径返回// 从右往左參数依次检查是否可能组成绝对路径。假设是。返回win32.resolve = function() { ??//该參数表示找到的详细的盘符 ?var resolvedDevice = ‘‘, ????//详细找到的绝对路径的尾部 ?????resolvedTail = ‘‘, ?????//是否已经生成了绝对路径 ?????resolvedAbsolute = false; ?// 分析參数,从右往左,直到生成了一个绝对路径为止 ?for (var i = arguments.length - 1; i >= -1; i--) { ???// 候选的參数 ???var path; ???if (i >= 0) { ?????// 当前的參数路径 ?????path = arguments[i]; ???} else if (!resolvedDevice) { ?????// 运行到此,说明未找到绝对的路径,使用当前的工作文件夹 ?????path = process.cwd(); ???} else { ?????// Windows has the concept of drive-specific current working ?????// directories. If we‘ve resolved a drive letter but not yet an ?????// absolute path, get cwd for that drive. We‘re sure the device is not ?????// an unc path at this points, because unc paths are always absolute. ?????// 说明已经是參数的最后都无法找到,获取当前盘符的工作路径作为备选 ?????path = process.env[‘=‘ + resolvedDevice]; ?????// Verify that a drive-local cwd was found and that it actually points ?????// to our drive. If not, default to the drive‘s root. ?????if (!path || path.substr(0, 3).toLowerCase() !== ?????????resolvedDevice.toLowerCase() + ‘\\‘) { ???????path = resolvedDevice + ‘\\‘; ?????} ???} ???// 处理參数作为候选的,假设參数包括非字符串,直接报错。 ???// Skip empty and invalid entries ???if (!util.isString(path)) { ?????throw new TypeError(‘Arguments to path.resolve must be strings‘); ???} else if (!path) { ?????continue; ???} ???// 直接从当前的參数中获取路径的详细信息 ???var result = win32StatPath(path), ???????// 详细盘符 ???????device = result.device, ???????// 是否为UNC文件夹 ???????isUnc = result.isUnc, ???????// 是否为绝对路径 ???????isAbsolute = result.isAbsolute, ????????// 尾路径 ????????tail = result.tail; ???if (device && ???????resolvedDevice && ???????device.toLowerCase() !== resolvedDevice.toLowerCase()) { ?????// This path points to another device so it is not applicable ?????continue; ???} ???if (!resolvedDevice) { ?????// ?假设没有找到盘符。给定盘符 ?????resolvedDevice = device; ???} ???// 假设还未给定绝对路径 ???if (!resolvedAbsolute) { ??????//尝试生成一个路径,注意是 tail 和已经resolvedTail向连接 ?????resolvedTail = tail + ‘\\‘ + resolvedTail; ?????// 是否已经找到绝对的路径 ?????resolvedAbsolute = isAbsolute; ???} ???// 随意时刻假设找到绝对路径,都跳出循环。 ???// ????if (resolvedDevice && resolvedAbsolute) { ?????break; ???} ?} ?// Convert slashes to backslashes when `resolvedDevice` points to an UNC ?// root. Also squash multiple slashes into a single one where appropriate. ?if (isUnc) { ???resolvedDevice = normalizeUNCRoot(resolvedDevice); ?} ?// At this point the path should be resolved to a full absolute path, ?// but handle relative paths to be safe (might happen when process.cwd() ?// fails) ?// Normalize the tail path ?// 标准化尾部路径。 ?resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/), ???????????????????????????????!resolvedAbsolute).join(‘\\‘); //生成绝对路径,假设没找到的,直接以‘.‘返回。 ??return (resolvedDevice + (resolvedAbsolute ? ‘\\‘ : ‘‘) + resolvedTail) || ????????‘.‘;};// 标准化文件路径,主要解决 ‘.‘和‘..‘相对路径问题。
win32.normalize = function(path) { ?// 利用帮助函数获取文件路径的信息 ?var result = win32StatPath(path), ????// 盘符 ?????device = result.device, ?????// 是否为windows的UNC路径 ?????isUnc = result.isUnc, ?????// 是否为绝对路径 ?????isAbsolute = result.isAbsolute, ?????// 文件路径结尾 ?????tail = result.tail, ?????// 尾部是否为‘\‘ 或者 ‘/‘ 结尾。
?????trailingSlash = /[\\\/]$/.test(tail); ?// Normalize the tail path ?//标准化tail路径。处理掉‘.‘ ‘..‘ 以 ‘\‘ 连接 ??tail = normalizeArray(tail.split(/[\\\/]+/), !isAbsolute).join(‘\\‘); ?// 处理tail为空的情况 ?if (!tail && !isAbsolute) { ???tail = ‘.‘; ?} ?// 当原始路径中有slash时候。须要加上 ?if (tail && trailingSlash) { ???tail += ‘\\‘; ?} ?// Convert slashes to backslashes when `device` points to an UNC root. ?// Also squash multiple slashes into a single one where appropriate. ?// 处理windows UNC的情况。
?if (isUnc) { ???// 获取详细的路径,假设是UNC的情况 ???device = normalizeUNCRoot(device); ?} ?// 返回详细的路径 ?return device + (isAbsolute ?
‘\\‘ : ‘‘) + tail;};// 直接利用帮助函数来确定给定的路径是否为绝对路径。win32.isAbsolute = function(path) { ?return win32StatPath(path).isAbsolute;};// 组合出绝对路径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); ???} ?} ?// 生成以back slash 连接的字符组,这个就是文件文件夹 ?var joined = paths.join(‘\\‘); ?// Make sure that the joined path doesn‘t start with two slashes, because ?// normalize() will mistake it for an UNC path then. ?// 确保候选的合并路径不是以两个slash开头(UNC路径)。 ??// normalize() 函数对于UNC路径的处理不好 ?// This step is skipped when it is very clear that the user actually ?// intended to point at an UNC path. This is assumed when the first ?// non-empty string arguments starts with exactly two slashes followed by ?// at least one more non-slash character. ?// ?// Note that for normalize() to treat a path as an UNC path it needs to ?// have at least 2 components, so we don‘t filter for that here. ?// This means that the user can use join to construct UNC paths from ?// a server name and a share name; for example: ?// ??path.join(‘//server‘, ‘share‘) -> ‘\\\\server\\share\‘) ?if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) { ???joined = joined.replace(/^[\\\/]{2,}/, ‘\\‘); ?} ?// 利用标准化接口 获取详细的文件路径 ?return win32.normalize(joined);};// path.relative(from, to)// it will solve the relative path from ‘from‘ to ‘to‘, for instance:// from = ‘C:\\orandea\\test\\aaa‘// to = ‘C:\\orandea\\impl\\bbb‘// The output of the function should be: ‘..\\..\\impl\\bbb‘//解决相对路径问题。 假设从from和to的路径中,生成相对路径。
win32.relative = function(from, to) { ?// 生成from的绝对路径 ?from = win32.resolve(from); ?// 生成to的绝对路径 ?to = win32.resolve(to); ?// windows is not case sensitive ?// 直接不区分大写和小写 ?var lowerFrom = from.toLowerCase(); ?var lowerTo = to.toLowerCase(); ?// 生成绝对路径数组 ??var toParts = trimArray(to.split(‘\\‘)); ?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]) { ?????samePartsLength = i; ?????break; ???} ?} ?假设没有不论什么路径同样。就直接返回to ?if (samePartsLength == 0) { ???return to; ?} ?// 假设有同样的部分。就须要将剩余的部分以".."连接起来 ?var outputParts = []; ?for (var i = samePartsLength; i < lowerFromParts.length; i++) { ???outputParts.push(‘..‘); ?} ?// 连接详细的路径 ?outputParts = outputParts.concat(toParts.slice(samePartsLength)); ?// 生成Windows的路径 以 ‘\‘ 相连 ?return outputParts.join(‘\\‘);};
从上述能够看出,相对路径,合成路径等都是字符串的处理。
???????????????????????????Nodejs源代码分析之Path
原文地址:https://www.cnblogs.com/zhchoutai/p/8404210.html