# Loop Read

检索文本文件中的行, 每次一行.

    Loop Read InputFile , OutputFile

## 参数

InputFile

  类型: 字符串

  需要在循环中读取其内容的文本文件的名称, 如果未指定绝对路径, 则假定在 A_WorkingDir 中. 文件的行结束符可以是回车和换行符(`r`n), 也可以只是换行符(`n), 或只是回车(`r).

OutputFile

  类型: 字符串

  在循环期间保持打开的文件的名称, 如果未指定绝对路径, 则假定在 A_WorkingDir 中.

  在循环的主体内, 使用 FileAppend 函数, 不带 Filename 参数(即省略它) 来追加到这个特殊的文件. 以这种方式追加到一个文件比使用 2 个参数模式的 FileAppend 执行地更好, 因为每次操作时不需要关闭文件和再重新打开. 如果需要的话, 记得在文本后加入换行符(`n) 或回车符和换行符(`r`n).

  如果没有向文件写入任何内容, 文件就不会被打开. 如果循环执行零迭代或从未调用 FileAppend, 就会发生这种情况.

  选项: 行末(EOL) 转换模式和输出文件编码取决于在 FileAppend 的首次调用中传递的选项(即省略 Filename 的首次调用). 随后的调用会忽略 Options 参数. 默认情况下不执行 EOL 转换; 也就是说, 除非存在 `"`n"` 选项, 否则换行符(`n) 将按原样写入.

  标准输出(stdout): 指定 OutputFile 为星号(*) 可以把 FileAppend 写入的任何文本发送到标准输出(stdout). 这些文本可以重定向到文件, 指向另一个 EXE 的管道或被高级文本编辑器捕获, 然而发送到标准输出的文本不会出现在运行它的命令提示符中. 这可以通过以下方法解决 1) 使用 Ahk2Exe ConsoleApp 指令编译脚本, 或 2) 通过管道或重定向捕获脚本的输出. 有关详情, 请参阅 FileAppend.

## 备注

当您想对包含在文本文件中的内容逐行进行操作时, 文件读取循环很有用. 在整个操作过程中, 文件是打开的, 以避免每次都要重新扫描以找到下一行.

内置变量 A_LoopReadLine 存在于任何文件读取循环中. 它包含当前行的内容, 不包括回车和标志行结束的换行符(`r`n). 如果一个内部的文件读取循环被一个外部的文件读取循环所包围, 那么最内部循环的文件行将优先.

读取长度为 65,534 个字符的行. 如果行的长度超过了这个长度, 其剩余的字符将在下一个循环迭代时被读取.

StrSplit 或解析循环通常用于文件读取循环中, 以解析从 InputFile 中获取的每一行的内容. 例如, 如果 InputFile 的每一行都是一系列以 tab 分隔的字段, 那么这些字段可以像这个例子一样单独检索:

    Loop read, "C:\Database Export.txt"
    {
        Loop parse, A_LoopReadLine, A_Tab
        {
            MsgBox "Field number " A_Index " is " A_LoopField "."
        }
    }

要加载整个文件内容到变量中, 请使用 FileRead, 因为它比循环执行的更好(尤其对于大文件).

要同时打开多个文件, 请使用 FileOpen.

可以选择使用 One True Brace(OTB) 样式, 它允许左大括号出现在同一行而不是下面. 例如: `Loop Read InputFile, OutputFile {`.

请参阅 Loop 了解关于区块, Break, Continue 和 A_Index 变量(其存在于各种类型的循环中) 的相关信息.

在不存在字节顺序标记时要控制如何解码文件, 请使用 FileEncoding.

循环的后面可以有一个可选的 Else 语句, 如果输入文件是空的或者找不到的话, 就会执行这个语句. 如果指定了 OutputFile, 上面描述的 FileAppend 的特殊模式也可以在 Else 语句的主体中使用. 如果没有 Else 语句, 如果找不到文件, 则抛出 OSError.

## 相关

FileEncoding, FileOpen/File 对象, FileRead, FileAppend, Sort, Loop, Break, Continue, 区块, FileSetAttrib, FileSetTime

## 示例

只有首个文件中包含单词 FAMILY 的那些行才会写入第二个文件中. 取消注释首行可以覆盖而不是追加到任何现有的文件中.

    ;FileDelete "C:\Docs\Family Addresses.txt"

    Loop read, "C:\Docs\Address List.txt", "C:\Docs\Family Addresses.txt"
    {
        if InStr(A_LoopReadLine, "family")
            FileAppend(A_LoopReadLine "`n")
    }
    else
        MsgBox "Address List.txt was completely empty or not found."

获取文本文件的最后一行.

    Loop read, "C:\Log File.txt"
        last_line := A_LoopReadLine  ; 当循环结束时, 这里会保持最后一行的内容.

尝试从文本或 HTML 文件中提取所有 FTP 和 HTTP URL.

    SourceFile := FileSelect(3,, "Pick a text or HTML file to analyze.")
    if SourceFile = ""
        return  ; 此时将退出.

    SplitPath SourceFile,, &SourceFilePath,, &SourceFileNoExt
    DestFile := SourceFilePath "\" SourceFileNoExt " Extracted Links.txt"

    if FileExist(DestFile)
    {
        Result := MsgBox("Overwrite the existing links file? Press No to append to it.`n`nFILE: " DestFile,, 4)
        if Result = "Yes"
            FileDelete DestFile
    }

    LinkCount := 0
    Loop read, SourceFile, DestFile
    {
        URLSearch(A_LoopReadLine)
    }
    MsgBox LinkCount ' links were found and written to "' DestFile '".'
    return

    URLSearch(URLSearchString)
    {
        ; 由于在一些 URL(网址) 中内嵌有其他的 URL, 所以用这种特殊的方法进行处理:
        ; 找到最左边的起始位置:
        URLStart := 0  ; 设置起始默认值.
        for URLPrefix in ["https://", "http://", "ftp://", "www."]
        {
            ThisPos := InStr(URLSearchString, URLPrefix)
            if !ThisPos  ; 此前缀被取消资格.
                continue
            if !URLStart
                URLStart := ThisPos
            else ; URLStart 里面有一个有效的位置, 所以要和 ThisPos 比较一下.
            {
                if ThisPos && ThisPos < URLStart
                    URLStart := ThisPos
            }
        }

        if !URLStart  ; 在 URLSearchString 中不存在 URL.
            return

        ; 否则, 提取该 URL:
        URL := SubStr(URLSearchString, URLStart)  ; 忽略开始/无效部分.
        Loop parse, URL, " `t<>"  ; 找到首个空格, tab 或尖括号(如果有).
        {
            URL := A_LoopField
            break  ; 即仅执行一次循环来获取首个 "片段".
        }
        ; 如果上面的循环因为没有找到结尾字符而导致迭代次数为零,
        ; 那么 URL 变量的内容保持不变.

        ; 如果 URL 以双引号结束, 那么移除它. 暂时使用 StringReplace, 不过注意
        ; 双引号似乎可以合法存在于 URL 中, 所以这样做
        ; 可能破坏它们:
        URLCleansed := StrReplace(URL, '"')
        FileAppend URLCleansed "`n"
        global LinkCount += 1

        ; 看看当前行中是否含有其他 URL:
        CharactersToOmit := StrLen(URL)
        CharactersToOmit += URLStart
        URLSearchString := SubStr(URLSearchString, CharactersToOmit)

        ; 到自身的递归调用:
        URLSearch(URLSearchString)
    }

dftg