无名大哥 發表於 2019-12-11 23:02:00

Go path/filepath文件路径操作

<p>本文:https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter06/06.2.html</p>
<p>path:https://www.php.cn/manual/view/35279.html</p>
<p>filepath:https://www.php.cn/manual/view/35280.html</p>
<h1 id="62-pathfilepath-—-兼容操作系统的文件路径操作">path/filepath — 兼容操作系统的文件路径操作</h1>
<p><code>path/filepath</code>&nbsp;包涉及到路径操作时,路径分隔符使用&nbsp;<code>os.PathSeparator</code>。不同系统,路径表示方式有所不同,比如 Unix 和 Windows 差别很大。本包能够处理所有的文件路径,不管是什么系统。</p>
<p>注意,路径操作函数并不会校验路径是否真实存在。</p>
<h2 id="解析路径名字符串">解析路径名字符串</h2>
<p><code>Dir()</code>&nbsp;和&nbsp;<code>Base()</code>&nbsp;函数将一个路径名字符串分解成目录和文件名两部分。(注意一般情况,这些函数与 Unix 中 dirname 和 basename 命令类似,但如果路径以&nbsp;<code>/</code>&nbsp;结尾,<code>Dir</code>&nbsp;的行为和&nbsp;<code>dirname</code>&nbsp;不太一致。)</p>
<pre><code class="lang-go"><span class="hljs-keyword">func Dir(path <span class="hljs-keyword">string) <span class="hljs-keyword">string
<span class="hljs-keyword">func Base(path <span class="hljs-keyword">string) <span class="hljs-keyword">string
</span></span></span></span></span></span></code></pre>
<p><code>Dir</code>&nbsp;返回路径中除去最后一个路径元素的部分,即该路径最后一个元素所在的目录。在使用&nbsp;<code>Split</code>&nbsp;去掉最后一个元素后,会简化路径并去掉末尾的斜杠。如果路径是空字符串,会返回 ".";如果路径由 1 到多个斜杠后跟 0 到多个非斜杠字符组成,会返回 "/";其他任何情况下都不会返回以斜杠结尾的路径。</p>
<p><code>Base</code>&nbsp;函数返回路径的最后一个元素。在提取元素前会去掉末尾的斜杠。如果路径是 "",会返回 ".";如果路径是只有一个斜杆构成的,会返回 "/"。</p>
<p>比如,给定路径名&nbsp;<code>/home/polaris/studygolang.go</code>,<code>Dir</code>&nbsp;返回&nbsp;<code>/home/polaris</code>,而&nbsp;<code>Base</code>&nbsp;返回&nbsp;<code>studygolang.go</code>。</p>
<p>如果给定路径名&nbsp;<code>/home/polaris/studygolang/</code>,<code>Dir</code>&nbsp;返回&nbsp;<code>/home/polaris/studygolang</code>(这与 Unix 中的 dirname 不一致,dirname 会返回 /home/polaris),而&nbsp;<code>Base</code>&nbsp;返回&nbsp;<code>studygolang</code>。</p>
<p>有人提出此问题,见issue13199,不过官方认为这不是问题,如果需要和&nbsp;<code>dirname</code>&nbsp;一样的功能,应该自己处理,比如在调用&nbsp;<code>Dir</code>&nbsp;之前,先将末尾的&nbsp;<code>/</code>&nbsp;去掉。</p>
<p>此外,<code>Ext</code>&nbsp;可以获得路径中文件名的扩展名。</p>
<p><code>func Ext(path string) string</code></p>
<p><code>Ext</code>&nbsp;函数返回&nbsp;<code>path</code>&nbsp;文件扩展名。扩展名是路径中最后一个从&nbsp;<code>.</code>&nbsp;开始的部分,包括&nbsp;<code>.</code>。如果该元素没有&nbsp;<code>.</code>&nbsp;会返回空字符串。</p>
<h2 id="相对路径和绝对路径">相对路径和绝对路径</h2>
<p>某个进程都会有当前工作目录(进程相关章节会详细介绍),一般的相对路径,就是针对进程当前工作目录而言的。当然,可以针对某个目录指定相对路径。</p>
<p>绝对路径,在 Unix 中,以&nbsp;<code>/</code>&nbsp;开始;在 Windows 下以某个盘符开始,比如&nbsp;<code>C:\Program Files</code>。</p>
<p><code>func IsAbs(path string) bool</code></p>
<p><code>IsAbs</code>&nbsp;返回路径是否是一个绝对路径。而</p>
<p><code>func Abs(path string) (string, error)</code></p>
<p><code>Abs</code>&nbsp;函数返回&nbsp;<code>path</code>&nbsp;代表的绝对路径,如果&nbsp;<code>path</code>&nbsp;不是绝对路径,会加入当前工作目录以使之成为绝对路径。因为硬链接的存在,不能保证返回的绝对路径是唯一指向该地址的绝对路径。在&nbsp;<code>os.Getwd</code>&nbsp;出错时,<code>Abs</code>&nbsp;会返回该错误,一般不会出错,如果路径名长度超过系统限制,则会报错。</p>
<p><code>func Rel(basepath, targpath string) (string, error)</code></p>
<p><code>Rel</code>&nbsp;函数返回一个相对路径,将&nbsp;<code>basepath</code>&nbsp;和该路径用路径分隔符连起来的新路径在词法上等价于&nbsp;<code>targpath</code>。也就是说,<code>Join(basepath, Rel(basepath, targpath))</code>&nbsp;等价于&nbsp;<code>targpath</code>。如果成功执行,返回值总是相对于&nbsp;<code>basepath</code>&nbsp;的,即使&nbsp;<code>basepath</code>&nbsp;和&nbsp;<code>targpath</code>&nbsp;没有共享的路径元素。如果两个参数一个是相对路径而另一个是绝对路径,或者&nbsp;<code>targpath</code>&nbsp;无法表示为相对于&nbsp;<code>basepath</code>&nbsp;的路径,将返回错误。</p>
<pre><code class="lang-go">fmt.Println(filepath.Rel(<span class="hljs-string">"/home/polaris/studygolang", <span class="hljs-string">"/home/polaris/studygolang/src/logic/topic.go"))
fmt.Println(filepath.Rel(<span class="hljs-string">"/home/polaris/studygolang", <span class="hljs-string">"/data/studygolang"))

<span class="hljs-comment">// Output:
<span class="hljs-comment">// src/logic/topic.go &lt;nil&gt;
<span class="hljs-comment">// ../../../data/studygolang &lt;nil&gt;
</span></span></span></span></span></span></span></code></pre>
<h2 id="路径的切分和拼接">路径的切分和拼接</h2>
<p>对于一个常规文件路径,我们可以通过&nbsp;<code>Split</code>&nbsp;函数得到它的目录路径和文件名:</p>
<p><code>func Split(path string) (dir, file string)</code></p>
<p><code>Split</code>&nbsp;函数根据最后一个路径分隔符将路径&nbsp;<code>path</code>&nbsp;分隔为目录和文件名两部分(<code>dir</code>&nbsp;和&nbsp;<code>file</code>)。如果路径中没有路径分隔符,函数返回值&nbsp;<code>dir</code>&nbsp;为空字符串,<code>file</code>&nbsp;等于&nbsp;<code>path</code>;反之,如果路径中最后一个字符是&nbsp;<code>/</code>,则&nbsp;<code>dir</code>&nbsp;等于&nbsp;<code>path</code>,<code>file</code>&nbsp;为空字符串。返回值满足&nbsp;<code>path == dir+file</code>。<code>dir</code>&nbsp;非空时,最后一个字符总是&nbsp;<code>/</code>。</p>
<pre><code class="lang-go"><span class="hljs-comment">// dir == /home/polaris/,file == studygolang
filepath.Split(<span class="hljs-string">"/home/polaris/studygolang")

<span class="hljs-comment">// dir == /home/polaris/studygolang/,file == ""
filepath.Split(<span class="hljs-string">"/home/polaris/studygolang/")

<span class="hljs-comment">// dir == "",file == studygolang
filepath.Split(<span class="hljs-string">"studygolang")
</span></span></span></span></span></span></code></pre>
<p>相对路径到绝对路径的转变,需要经过路径的拼接。<code>Join</code>&nbsp;用于将多个路径拼接起来,会根据情况添加路径分隔符。</p>
<p><code>func Join(elem ...string) string</code></p>
<p><code>Join</code>&nbsp;函数可以将任意数量的路径元素放入一个单一路径里,会根据需要添加路径分隔符。结果是经过&nbsp;<code>Clean</code>&nbsp;的,所有的空字符串元素会被忽略。对于拼接路径的需求,我们应该总是使用&nbsp;<code>Join</code>&nbsp;函数来处理。</p>
<p>有时,我们需要分割&nbsp;<code>PATH</code>&nbsp;或&nbsp;<code>GOPATH</code>&nbsp;之类的环境变量(这些路径被特定于&nbsp;<code>OS</code>&nbsp;的列表分隔符连接起来),<code>filepath.SplitList</code>&nbsp;就是这个用途:</p>
<p><code>func SplitList(path string) []string</code></p>
<p>注意,与&nbsp;<code>strings.Split</code>&nbsp;函数的不同之处是:对 "",SplitList 返回[]string{},而&nbsp;<code>strings.Split</code>&nbsp;返回 []string{""}。<code>SplitList</code>&nbsp;内部调用的是&nbsp;<code>strings.Split</code>。</p>
<h2 id="规整化路径">规整化路径</h2>
<p><code>func Clean(path string) string</code></p>
<p><code>Clean</code>&nbsp;函数通过单纯的词法操作返回和&nbsp;<code>path</code>&nbsp;代表同一地址的最短路径。</p>
<p>它会不断的依次应用如下的规则,直到不能再进行任何处理:</p>
<ol>
<li>将连续的多个路径分隔符替换为单个路径分隔符</li>
<li>剔除每一个&nbsp;<code>.</code>&nbsp;路径名元素(代表当前目录)</li>
<li>剔除每一个路径内的&nbsp;<code>..</code>&nbsp;路径名元素(代表父目录)和它前面的非&nbsp;<code>..</code>&nbsp;路径名元素</li>
<li>剔除开始于根路径的&nbsp;<code>..</code>&nbsp;路径名元素,即将路径开始处的&nbsp;<code>/..</code>&nbsp;替换为&nbsp;<code>/</code>(假设路径分隔符是&nbsp;<code>/</code>)</li>
</ol>
<p>返回的路径只有其代表一个根地址时才以路径分隔符结尾,如 Unix 的&nbsp;<code>/</code>&nbsp;或 Windows 的&nbsp;<code>C:\</code>。</p>
<p>如果处理的结果是空字符串,Clean 会返回&nbsp;<code>.</code>,代表当前路径。</p>
<h2 id="符号链接指向的路径名">符号链接指向的路径名</h2>
<p>在上一节&nbsp;<code>os</code>&nbsp;包中介绍了&nbsp;<code>Readlink</code>,可以读取符号链接指向的路径名。不过,如果原路径中又包含符号链接,<code>Readlink</code>&nbsp;却不会解析出来。<code>filepath.EvalSymlinks</code>&nbsp;会将所有路径的符号链接都解析出来。除此之外,它返回的路径,是直接可访问的。</p>
<p><code>func EvalSymlinks(path string) (string, error)</code></p>
<p>如果&nbsp;<code>path</code>&nbsp;或返回值是相对路径,则是相对于进程当前工作目录。</p>
<p><code>os.Readlink</code>&nbsp;和&nbsp;<code>filepath.EvalSymlinks</code>&nbsp;区别示例程序:</p>
<pre><code class="lang-go"><span class="hljs-comment">// 在当前目录下创建一个 studygolang.txt 文件和一个 symlink 目录,在 symlink 目录下对 studygolang.txt 建一个符号链接 studygolang.txt.2
fmt.Println(filepath.EvalSymlinks(<span class="hljs-string">"symlink/studygolang.txt.2"))
fmt.Println(os.Readlink(<span class="hljs-string">"symlink/studygolang.txt.2"))

<span class="hljs-comment">// Ouput:
<span class="hljs-comment">// studygolang.txt &lt;nil&gt;
<span class="hljs-comment">// ../studygolang.txt &lt;nil&gt;
</span></span></span></span></span></span></code></pre>
<h2 id="文件路径匹配">文件路径匹配</h2>
<p><code>func Match(pattern, name string) (matched bool, err error)</code></p>
<p><code>Match</code>&nbsp;指示&nbsp;<code>name</code>&nbsp;是否和 shell 的文件模式匹配。模式语法如下:</p>
<pre><code class="lang-go">pattern:
    { term }
term:
    <span class="hljs-string">'*'         匹配 <span class="hljs-number">0 或多个非路径分隔符的字符
    <span class="hljs-string">'?'         匹配 <span class="hljs-number">1 个非路径分隔符的字符
    <span class="hljs-string">'[' [ <span class="hljs-string">'^' ] { character-<span class="hljs-keyword">range } <span class="hljs-string">']'
                  字符组(必须非空)
    c         匹配字符 c(c != <span class="hljs-string">'*', <span class="hljs-string">'?', <span class="hljs-string">'\\', '[<span class="hljs-string">')
    '\\<span class="hljs-string">' c      匹配字符 c
character-range:
    c         匹配字符 c(c != '\\<span class="hljs-string">', '-<span class="hljs-string">', ']<span class="hljs-string">')
    '\\<span class="hljs-string">' c      匹配字符 c
    lo '-<span class="hljs-string">' hi   匹配区间内的字符
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>匹配要求&nbsp;<code>pattern</code>&nbsp;必须和&nbsp;<code>name</code>&nbsp;全匹配上,不只是子串。在 Windows 下转义字符被禁用。</p>
<p><code>Match</code>&nbsp;函数很少使用,搜索了一遍,标准库没有用到这个函数。而&nbsp;<code>Glob</code>&nbsp;函数在模板标准库中被用到了。</p>
<p><code>func Glob(pattern string) (matches []string, err error)</code></p>
<p><code>Glob</code>&nbsp;函数返回所有匹配了 模式字符串&nbsp;<code>pattern</code>&nbsp;的文件列表或者 nil(如果没有匹配的文件)。<code>pattern</code>的语法和&nbsp;<code>Match</code>&nbsp;函数相同。<code>pattern</code>&nbsp;可以描述多层的名字,如&nbsp;<code>/usr/*/bin/ed</code>(假设路径分隔符是&nbsp;<code>/</code>)。</p>
<p>注意,<code>Glob</code>&nbsp;会忽略任何文件系统相关的错误,如读目录引发的 I/O 错误。唯一的错误和&nbsp;<code>Match</code>&nbsp;一样,在&nbsp;<code>pattern</code>&nbsp;不合法时,返回&nbsp;<code>filepath.ErrBadPattern</code>。返回的结果是根据文件名字典顺序进行了排序的。</p>
<p><code>Glob</code>&nbsp;的常见用法,是读取某个目录下所有的文件,比如写单元测试时,读取&nbsp;<code>testdata</code>&nbsp;目录下所有测试数据:</p>
<p><code>filepath.Glob("testdata/*.input")</code></p>
<h2 id="遍历目录">遍历目录</h2>
<p>在介绍&nbsp;<code>os</code>&nbsp;时,讲解了读取目录的方法,并给出了一个遍历目录的示例。在&nbsp;<code>filepath</code>&nbsp;中,提供了&nbsp;<code>Walk</code>函数,用于遍历目录树。</p>
<p><code>func Walk(root string, walkFn WalkFunc) error</code></p>
<p><code>Walk</code>&nbsp;函数会遍历&nbsp;<code>root</code>&nbsp;指定的目录下的文件树,对每一个该文件树中的目录和文件都会调用&nbsp;<code>walkFn</code>,包括&nbsp;<code>root</code>&nbsp;自身。所有访问文件 / 目录时遇到的错误都会传递给&nbsp;<code>walkFn</code>&nbsp;过滤。文件是按字典顺序遍历的,这让输出更漂亮,但也导致处理非常大的目录时效率会降低。<code>Walk</code>&nbsp;函数不会遍历文件树中的符号链接(快捷方式)文件包含的路径。</p>
<p><code>walkFn</code>&nbsp;的类型&nbsp;<code>WalkFunc</code>&nbsp;的定义如下:</p>
<p><code>type WalkFunc func(path string, info os.FileInfo, err error) error</code></p>
<p><code>Walk</code>&nbsp;函数对每一个文件 / 目录都会调用&nbsp;<code>WalkFunc</code>&nbsp;函数类型值。调用时&nbsp;<code>path</code>&nbsp;参数会包含&nbsp;<code>Walk</code>&nbsp;的&nbsp;<code>root</code>&nbsp;参数作为前缀;就是说,如果&nbsp;<code>Walk</code>&nbsp;函数的&nbsp;<code>root</code>&nbsp;为 "dir",该目录下有文件 "a",将会使用 "dir/a" 作为调用&nbsp;<code>walkFn</code>&nbsp;的参数。<code>walkFn</code>&nbsp;参数被调用时的&nbsp;<code>info</code>&nbsp;参数是&nbsp;<code>path</code>&nbsp;指定的地址(文件 / 目录)的文件信息,类型为&nbsp;<code>os.FileInfo</code>。</p>
<p>如果遍历&nbsp;<code>path</code>&nbsp;指定的文件或目录时出现了问题,传入的参数&nbsp;<code>err</code>&nbsp;会描述该问题,<code>WalkFunc</code>&nbsp;类型函数可以决定如何去处理该错误(<code>Walk</code>&nbsp;函数将不会深入该目录);如果该函数返回一个错误,<code>Walk</code>&nbsp;函数的执行会中止;只有一个例外,如果&nbsp;<code>Walk</code>&nbsp;的&nbsp;<code>walkFn</code>&nbsp;返回值是&nbsp;<code>SkipDir</code>,将会跳过该目录的内容而&nbsp;<code>Walk</code>&nbsp;函数照常执行处理下一个文件。</p>
<p>和&nbsp;<code>os</code>&nbsp;遍历目录树的示例对应,使用&nbsp;<code>Walk</code>&nbsp;遍历目录树的示例程序在&nbsp;walk,程序简单很多。</p>
<h2 id="windows-起作用的函数">Windows 起作用的函数</h2>
<p><code>filepath</code>&nbsp;中有三个函数:<code>VolumeName</code>、<code>FromSlash</code>&nbsp;和&nbsp;<code>ToSlash</code>,针对非 Unix 平台的。</p>
<h2 id="关于-path-包">关于 path 包</h2>
<p><code>path</code>&nbsp;包提供了对&nbsp;<code>/</code>&nbsp;分隔的路径的实用操作函数。</p>
<p>在 Unix 中,路径的分隔符是&nbsp;<code>/</code>,但 Windows 是&nbsp;<code>\</code>。在使用&nbsp;<code>path</code>&nbsp;包时,应该总是使用&nbsp;<code>/</code>,不论什么系统。</p>
<p><code>path</code>&nbsp;包中提供的函数,<code>filepath</code>&nbsp;都有提供,功能类似,但实现不同。</p>
<p>一般应该总是使用&nbsp;<code>filepath</code>&nbsp;包,而不是&nbsp;<code>path</code>&nbsp;包。</p><br><br>
来源:https://www.cnblogs.com/-wenli/p/12026413.html
頁: [1]
查看完整版本: Go path/filepath文件路径操作