# Loop (读取文件内容)

逐行读取一个文本文件的内容 (比 FileReadLine 执行地更好)。

Loop, Read, InputFile [, OutputFile]

#### 参数

Read | 此参数必须是单词 READ。
InputFile | 文本文件的名称，它的内容将被 loop 读取，如果未指定绝对路径，则假设位于 %A_WorkingDir%。支持 Windows 和 Unix 格式的文件；就是说，文件行可以是回车加换行符 (`r`n) 结尾或者只用换行符 (`n) 结尾。
OutputFile | (可选) 输出文件的名称，它在循环过程中会保持打开状态，如果未指定绝对路径，则假设位于 %A_WorkingDir%。 在循环体内，请用只带一个参数(要写入的文本)的 FileAppend 命令来将文本追加到此文件。以这种方式追加到一个文件比使用 2 个参数模式的 FileAppend 执行地更好，因为每次操作不需要关闭文件再重新打开。如果需要，记得在写入内容后跟一个换行符 (`n)。 如果没有写入操作，该文件不会被打开。例如循环次数是零，或者没有使用 FileAppend 命令。 二进制模式：要以二进制模式而不是文本模式追加的话，在文件名前加一个星号。这会使每个换行符 (`n) 作为一个单独的换行 (LF) 写入，而不是 Windows 标准的回车加换行 (CR+LF)。例如：*C:\My Unix File.txt 。即使不用星号，如果循环第一次使用 FileAppend 命令的时候写入了任何成对的回车和换行符 (`r`n)，二进制模式也会自动生效。 标准输出 (stdout)：给 OutputFile 指定一个星号 (*) 可以将任何由 FileAppend 写入的文本发送到标准输出 (stdout)。这样的文本可以重定向到一个文件、传送到另一个 EXE 程序或被高级的文本编辑器获取。不过，以标准形式输出的文本并不会在执行它的命令提示符中出现。详见 FileAppend。 逗号转义：和其它大多数命令的最后一个参数不同，在 OutputFile 参数中，逗号必须进行转义 (`,)。

#### 注意

当你想对一个文本文件中的内容逐行进行操作的时候，文件读取循环十分有用。它比使用 FileReadLine 执行地更好，因为：1) 文件在整个操作过程中可以保持打开状态；2) 不必每次都重新扫描文件来查找请求的行数。

内置变量 A_LoopReadLine 存在于任何文件读取循环中。它包含了当前行的内容，行尾的回车和换行符(`r`n)标记除外。如果一个内层文件读取循环被装在一个外层文件读取循环中，则最里层循环的文件行将享有优先。

每行最多可以读取 65,534 个字符。如果某行的长度有超出，那它剩余的字符将在下次循环中被读取。

在文件读取循环中，经常使用 StringSplit 或者字符串解析循环来解析从 InputFile 中读取的每一行内容。例如，如果 InputFile 中的每一行内容都是由 tab 分隔的一系列片段，那么这些片段可以用下面这段示例逐个读取：

    Loop, read, C:\Database Export.txt
    {
        Loop, parse, A_LoopReadLine, %A_Tab%
        {
            MsgBox, 第 %A_Index% 个片段是 %A_LoopField%。
        }
    }

要将整个文件内容读取到一个变量中的话，请用 FileRead，因为它比一个循环执行地好得多 (特别是读取大文件的时候)。

要同时打开多个文件，请用 DllCall()，参考这个例子。

请看 Loop 命令获取 Blocks, Break, Continue 以及内置变量 A_Index (存在于任何类型的循环中) 的相关信息。

#### 相关命令

FileRead, FileReadLine, FileAppend, Sort, Loop, Break, Continue, Blocks, FileSetAttrib, FileSetTime

#### 示例

    ;示例 1: 第一个文件中，只有含有单词 FAMILY 的行会被写入第二个文件。
    ;取消第一行的注释的话，将会覆盖现有的文件，而不是将内容追加到现有的文件中。
    ;FileDelete, C:\Docs\Family Addresses.txt

    Loop, read, C:\Docs\Address List.txt, C:\Docs\Family Addresses.txt
    {
        IfInString, A_LoopReadLine, family, FileAppend, %A_LoopReadLine%`n
    }

    ;示例 2: 读取一个文本文件的最后一行。
    Loop, read, C:\Log File.txt
        last_line := A_LoopReadLine  ;当循环结束时，这个变量将会保存最后一行的内容。

    ;示例 3: 从一个文本文件或者 HTML 文件中提取所有的 FTP 和 HTTP URL。
    FileSelectFile, SourceFile, 3,, 选择一个文本文件或者 HTML 文件来分析。
    if SourceFile =
        return  ;未选择文件则退出。

    SplitPath, SourceFile,, SourceFilePath,, SourceFileNoExt
    DestFile = %SourceFilePath%\%SourceFileNoExt% Extracted Links.txt

    IfExist, %DestFile%
    {
        MsgBox, 4,, 覆盖已存在的链接文件？按否则是追加。`n`n文件：%DestFile%
        IfMsgBox, Yes
            FileDelete, %DestFile%
    }

    LinkCount = 0
    Loop, read, %SourceFile%, %DestFile%
    {
        URLSearchString = %A_LoopReadLine%
        Gosub, URLSearch
    }
    MsgBox 查找到 %LinkCount% 个链接，已写入 "%DestFile%"。
    return

    URLSearch:
    ;下面这么写是因为一些 URL 中嵌入了其它的 URL：
    StringGetPos, URLStart1, URLSearchString, http://
    StringGetPos, URLStart2, URLSearchString, ftp://
    StringGetPos, URLStart3, URLSearchString, www.

    ;找到最左边的开始位置：
    URLStart = %URLStart1%  ;设置默认的开始位置。
    Loop
    {
        ;通过只解析 "URLStart%A_Index%" 一次来提高效率 (至少在一个含有许多变量的脚本中能提高)：
        ArrayElement := URLStart%A_Index%
        if ArrayElement =  ;抵达数组的末尾。
            break
        if ArrayElement = -1  ;取消这个元素。
            continue
        if URLStart = -1
            URLStart = %ArrayElement%
        else ; URLStart 中包含有效的起始位置，所以和 ArrayElement 比较。
        {
            if ArrayElement <> -1
                if ArrayElement < %URLStart%
                    URLStart = %ArrayElement%
        }
    }

    if URLStart = -1  ; URLSearchString 中没有 URL。
        return

    ;否则，提取这个 URL：
    StringTrimLeft, URL, URLSearchString, %URLStart%  ;省略开始部分/不相关的内容。
    Loop, parse, URL, %A_Tab%%A_Space%<>  ;查找第一个空格，Tab，或者尖括号 (如果有的话)。
    {
        URL = %A_LoopField%
        break  ;即只循环一次来获取首个“片段”。
    }
    ;如果没有进入上面的循环，表示没找到结束符，将不处理 URL 变量的内容。

    ;如果 URL 以双引号结尾，移除它。目前使用了 StringReplace，
    ;但是要注意，由于双引号也可以出现在 URL 中，所以这个操作可能会破坏它们：
    StringReplace, URLCleansed, URL, ",, All
    FileAppend, %URLCleansed%`n
    LinkCount += 1

    ;检查这一行中是否还有其它 URL：
    StringLen, CharactersToOmit, URL
    CharactersToOmit += %URLStart%
    StringTrimLeft, URLSearchString, URLSearchString, %CharactersToOmit%
    Gosub, URLSearch  ;循环调用自身。
    return

翻译：okey3m 修正：天堂之门 menk33@163.com 2009年1月7日