[
  {
    "path": "license.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it. By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users. This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it. (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.) You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice. Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have. You must make sure that they, too, receive or can get the\nsource code. And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware. If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents. We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary. To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License. The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage. (Hereinafter, translation is included without limitation in\nthe term \"modification\".) Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope. The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License. (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole. If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works. But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code. (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it. For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable. However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License. Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it. However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works. These actions are\nprohibited by law if you do not accept this License. Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions. You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License. If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all. For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices. Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded. In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time. Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number. If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation. If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission. For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this. Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS"
  },
  {
    "path": "readme.md",
    "content": "# TF: Textfile & String Library for AutoHotkey [lib] - v3.8\n\n__A \"Swiss Army Knife\" library for Text (files)__\n\n## Introduction\n\nAs the name suggest this is an AutoHotkey (AHK) Library with a number of functions to \"manipulate\" **text**,\nboth **files** such as \\*.txt, \\*.ahk, \\*.html, \\*.css etc AND **Strings** (or variables). For example\nyou can delete specific lines, replace words or specific lines, number lines, remove or insert columns\nof text, etc. See the list of functions below for a complete overview.\n\nIt is **not** useful for binary files or data such as MS Office files, PDFs, EXEcutables, images etc.\n(Tip: search the AHK forum for \"binread\" to find some pointers on how to read, write and \"edit\" binary files.)\n\n## Functions\n\n1.  [TF](#TF)\n2.  [TF_Save](#TF_Save)\n3.  [TF_CountLines](#TF_CountLines)\n4.  [TF_Count](#TF_Count) *1\n5.  [TF_ReadLines](#TF_ReadLines)\n6.  [TF_Tail](#TF_Tail)\n7.  [TF_Replace](#TF_Replace)\n8.  [TF_ReplaceInLines](#TF_ReplaceInLines)\n9.  [TF_RegExReplace](#TF_RegExReplace)\n10. [TF_RegExReplaceInLines](#TF_RegExReplaceInLines)\n11. [TF_RemoveLines](#TF_RemoveLines)\n12. [TF_RemoveBlankLines](#TF_RemoveBlankLines)\n13. [TF_RemoveDuplicateLines](#TF_RemoveDuplicateLines)\n14. [TF_InsertLine](#TF_InsertLine)\n15. [TF_ReplaceLine](#TF_ReplaceLine)\n16. [TF_InsertPrefix](#TF_InsertPrefix)\n17. [TF_InsertSuffix](#TF_InsertSuffix)\n18. [TF_TrimLeft](#TF_TrimLeft)\n19. [TF_TrimRight](#TF_TrimRight)\n20. [TF_AlignLeft](#TF_AlignLeft)\n21. [TF_AlignCenter](#TF_AlignCenter)\n22. [TF_AlignRight](#TF_AlignRight)\n23. [TF_LineNumber](#TF_LineNumber)\n24. [TF_ConCat](#TF_ConCat)\n25. [TF_ColGet](#TF_ColGet)\n26. [TF_ColPut](#TF_ColPut)\n27. [TF_ColCut](#TF_ColCut)\n28. [TF_ReverseLines](#TF_ReverseLines)\n29. [TF_Find](#TF_Find) (can be used as basic grep)\n30. [TF_SplitFileByLines](#TF_SplitFileByLines)\n31. [TF_SplitFileByText](#TF_SplitFileByText)\n32. [TF_Merge](#TF_Merge) *2\n33. [TF_Prepend](#TF_Prepend) *2\n34. [TF_Append](#TF_Append) *2\n35. [TF_Wrap](#TF_Wrap)\n36. [TF_WhiteSpace](#TF_WhiteSpace)\n37. [TF_Substract](#TF_Substract)\n38. [TF_RangeReplace](#TF_RangeReplace)\n39. [TF_MakeFile](#TF_MakeFile)\n40. [TF_Tab2Spaces](#TF_Tab2Spaces)\n41. [TF_Spaces2Tab](#TF_Spaces2Tab)\n42. [TF_Sort](#TF_Sort)\n43. [TF_Join](#TF_Join)\n\n**Notes**\n\n1. TF_Count does not support files and only works with variables.\n2. TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.\n \n**Concept**\n\nIt is important to understand a few basic concepts before you start working with the TF library:\n\n- If you pass on a file to a TF function the output will generally be a file (there are a few exceptions). By default it writes the output to a COPY of the input file, leaving the original input file intact.\n- You **can** overwrite the input file by using a ! as prefix. See \"Quick intro to Parameters\" and \"Textfile and the ! Prefix\" below for more details.\n- If you pass on a variable to a TF function the returned output will also be a variable.\n- Most TF Functions allow you to work on specific lines or sections of lines.\n- TF \"knows\" if something is a file or a variable, even if the variable passed on to TF is meant to represent a file. (This is done in the helper function TF_GetData for those interested).\n\nTo understand how to work with **files and variables** please read the \"Textfile and the ! Prefix\"\nand \"Files & Variables\" sections below.\n\n**File encoding, codepage**\n\nIf you experience TF is changing the file encoding (codepage) try to set the correct file encoding\nat the top of your script (or at least before you call a TF function) using the following AutoHotkey command:\nhttp://ahkscript.org/docs/commands/FileEncoding.htm\n\nPlease note, there is no way to determine the file encoding 100% accurately, even if a file\ncontains BOM. See the discussion about this topic and possibly useful additional functions\nat the AutoHotkey forum http://www.autohotkey.com/board/topic/95986-filegetencoding-filegetformat/\n(The result of these functions are simply a best guess assuming UTF-8 more common when BOM - https://en.wikipedia.org/wiki/Byte_Order_Mark - \nis missing).\n\n## How to \"install\" / use\n\nYou can either place TF.ahk in your LIB directory (see [http://ahkscript.org/docs/Functions.htm#lib](http://ahkscript.org/docs/Functions.htm#lib))\nor use #include (see [http://ahkscript.org/docs/commands/_Include.htm](http://ahkscript.org/docs/commands/_Include.htm))\n\nYou can find examples of most functions in the \"example script\" here\n[http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363](http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363)\n\n**Note:** Because most of these functions operate on a line by line basis they WILL be\nslower compared to a function or script which could operate on an entire file or\nvariable at once. Keep this in mind if you need to process many files/variables or very\nlarge files/variables in case speed is an issue.\n\n## Quick intro to Parameters\n\nAlmost all functions accept the following basic parameters:\n\n<table border=\"1\" width=\"100%\" cellspacing=\"0\" cellpadding=\"3\" bordercolor=\"#C0C0C0\">\n      <tr valign=\"top\">\n\t\t<td><b>Parameter</b></td>\n\t\t<td><b>Meaning</b></td>\n\t  </tr>\t\n\t  <tr valign=\"top\">\n\t    <td>Text</td>\n\t    <td>The Filename (may include (absolute) path) to read from and save to (for all functions that write an output file).<br />\n         <b>Note:</b> by default a filename_copy will be created, use the ! prefix if you want to OverWrite the TextFile (e.g. the source file)<br />\n\t     As of v3 \"Text\" can also be a variable or indeed text directly passed on to the function.<br />\n         See <a href='#textfile-and-the--prefix'>Textfile and the ! Prefix</a>.\n\t    </td>\n\t  </tr>\n      <tr valign=\"top\">\n        <td>! Prefix</td>\n        <td>If Text starts with ! (eg: \"!c:\\sample.txt\") it will overwrite the text file, otherwise it will save the new file to a copy of the text file eg: Filename_copy.txt (All functions, apart from reading functions because there is no output file) <br />\n        <b>Tip:</b> you can use concatenation to add the !, e.g. \"!\" . \"Filename.txt\", see the examples in the AHK thread.<br />\n        Note: If Text is a variable, it can start with a ! as TF will detect automatically that it is not a file and will therefore not create a file but return the altered variable instead.<br />\n        See <a href='#textfile-and-the--prefix'>Textfile and the ! Prefix</a>.\n\t    </td>\n\t  </tr>\n      <tr valign=\"top\">\n        <td>Lines</td>\n        <td>Number of lines to read</td>\n      </tr>\n      <tr valign=\"top\">\n        <td>StartLine</td>\n        <td>Start of Range (Almost all).<br />\n\t    See <a href='#startline-endline-syntax'>(StartLine, Endline) Syntax</a>.\n\t    </td>\n\t  </tr>\n      <tr valign=\"top\">\n        <td>EndLine</td>\n        <td>End of Range (Almost all).<br />\n  \t    See <a href='#startline-endline-syntax'>(StartLine, Endline) Syntax</a>.\n\t    </td>\n\t  </tr>\n      <tr valign=\"top\">\n        <td>SearchText</td>\n        <td>Text to Find (Find / Replace functions)</td>\n      </tr>\n      <tr valign=\"top\">\n        <td>ReplaceText</td>\n        <td>Text to Replace (Find / Replace functions)</td>\n      </tr>\n</table>\n\n**Notes**\n\n1. Many functions also have specific parameters, see the description of each function below.\n2. **Backup files:** If a subdirectory \"backup\" is present in the directory of TextFile \na backup is made before overwriting the original file (both for file.txt and file_copy.txt)\nwith the BAK extension\n3. You can find examples of most functions in the \"example script\" here [http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363](http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363)\n\n### (StartLine, Endline) Syntax\n\nYou can pass on multiple lines (sections) by using the StartLine parameter.\n\nExamples:\n\n- (5) ; start from line 5 to the end (StartLine)\n- (5, 15) ; Section: lines 5 to 15 (StartLine, EndLine)\n- (\"5,13,45,67\", 135) ; Specific lines: 5,13,45,67, ignore 135 (\"StartLine\", Endline - **EndLine value is ignored**)\n- (\"5-13,45-51\", 135) ; Multiple sections of lines: 5 to 13, 45 to 51 ignore 135 (\"StartLine\", Endline - **EndLine value is ignored**)\n\nIncremental (EndLine will **not** be ignored) in the following cases:\n\n- (\"2+3\", 150) ; start with line 2, increment 3 up-to line 150 so 2,5,8,11,14 etc\n- (\"5+15\") ; start with line 5, increment 15 until end of file so 5,20,35 etc\n\nNegative Startline\n\n- A Negative startline value makes a TF function operate on the last X lines.\n- TF_ColGet and TF_ColPut accept negative StartColumn parameter. This can be used to \"Get\" or \"Put\" text in the x-th \"column\" from the right\n\n### Textfile and the ! Prefix\n\nValid examples for using a Textfile:\n\n```autohotkey\nTF_(\"file.txt\", .... ; Process file.txt and write output to file_copy.txt\n\nF=file.txt ; ; Note how F is a variable, but AHK/TF will see it is meant to represent file.txt and write output to file_copy.txt\nTF_(F, .... ; Process file.txt and write output to file_copy.txt\nTF_(\"!file.txt\", .... ; Process file.txt and overwrite file.txt\n\nF=file.txt\nTF_(\"!\" . F, .... ; Process file.txt and overwrite file.txt\n\nF=!file.txt\nTF_(F, .... ; Process file.txt and overwrite file.txt\n\nIn a Loop, FilePattern:\nTF_(A_LoopFileName, .... ; Process file and write output to file_copy\nTF_(\"!\" . A_LoopFileName, .... ; Process file and overwrite file\n\nLoop, *.txt\n    {\n     TF_(A_LoopFileName, .... ; Process file and write output to file_copy\n    }\n```\n\nIf you want to use multiple TF functions on a single file it is advised to use the ! Prefix\n\n```autohotkey\nF=!file.txt \nTF_RemoveBlankLines(F)        ; remove all empty lines from file.txt and overwrite original file.txt\nTF_LineNumber(F)              ; add linenumbers to all lines and overwrite original file.txt\nTF_Replace(F, \"this\", \"that\") ; Replace the word \"this\" with \"that\" and overwrite original file.txt\n; So the original file.txt has undergone three changes\n```\n\n**Files & Variables (v3+)**\n\nYou can use TF functions directly on files as well as variables. \nBelow you will find some easy examples.\n\n**Introduced in v3: TF()**\n\nIn order to save you the trouble of using a fileread to read a file into\na variable and proceed to use various TF functions there is the TF()\nFunction.\n\n\n```autohotkey\nTF(\"pathtofile\", CreateGlobalVar=\"T\")\n```\n\nBy default it reads the contents of the file in a global variable named **t** and \nreturns it for further processing. By default it creates variable **t** if a variable\nname is **omitted** so if you do not like to use **t** you can use anything you like.\nIt is NOT mandatory to use TF(), you are welcome to use a fileread as well, TF() is \nmerely added for convenience as we will see in the examples below. TF() doesn't suddenly\nmake the TF library \"not standard compliant\" as some seem to think as it only creates a \nglobal variable if you use the TF() function.\n\n**Repeat:** You only have to use TF() if you want to read a file into a variable \nand use it on multiple TF_\\* Functions but it is NOT required.\n\nSee also [TF Lib Errors](#tf-lib-errors) below.\n\n**TF() Examples:**\n\n\n```autohotkey\nTF(\"file.txt\") ; will create global var t\nTF(\"file.txt\", \"MyVar\") ; will create global var MyVar\n```\n   \n**Examples on how to use files & variables with TF:**\n\n\n```autohotkey\n#Include tf.ahk\nTestFile= ; create variable\n(join`r`n\n1 Hi this\n2 a test variable\n3 to demonstrate\n4 how to \n5 use this\n6 new version\n7 of TF.AHK\n)\nFileDelete, TestFile.txt\nFileAppend, %TestFile%, TestFile.txt ; create file\nF=TestFile.txt ; just a shorthand code for TextFile.txt, so when\n; we are using 'F' below we are still passing on a TextFile, not a variable!\n\n; pass on file, read lines 5 to end of file:\nMsgBox % \"From File 1:`n\" TF_ReadLines(\"TestFile.txt\",5)\nMsgBox % \"From File 2:`n\" TF_ReadLines(F,5)              ; same\n\n; pass on variable, read lines 1 to 5\nMsgBox % \"From Variable 1:`n\" TF_ReadLines(TestFile,\"1-5\")  \nMsgBox % \"From Variable 2:`n\" TF_ReadLines(\"Hi`nthis`nis`na`ntest`nvariable`nfor`ntesting\",1,3) ; pass on string\n\n; Examples using TF(): (it will save you a FileRead if you want to work with Variables)\n\nTF(\"TestFile.txt\") ; read file, create global var t\nt:=TF_ReadLines(t,5) ; pass on global var t created by TF(), read lines 5 to end of file, assign result to t \nMsgBox % \"TF(), example 1:`n\" t\n\nTF(\"TestFile.txt\", \"MyVar\") ; read file, create global var MyVar\nMyVar:=TF_ReadLines(MyVar,5) ; pass on global var MyVar created by TF(), read lines 5 to end of file, assign result to MyVar\nMsgBox % \"TF(), example 2:`n\" MyVar\n\n; Note how we can use TF() here\nt:=TF_ReadLines(TF(\"TestFile.txt\"),5) ; pass on global var t created by TF(), read lines 5 to end of file, assign result to t \nMsgBox % \"TF(), example 3:`n\" t\n\nMyVar:=TF_ReadLines(TF(\"TestFile.txt\",\"MyVar\"),5) ; pass on global var t created by TF(), read lines 5 to end of file, assign result to t \nMsgBox % \"TF(), example 4:`n\" MyVar\n\nt:=TF_ReadLines(TF(F),5) ; pass on global var t created by TF(), read lines 5 to end of file, assign result to t\nt:=TF_ReverseLines(t,5) ; pass on global var t created by TF(), reverse lines, assign result to t\nMsgBox % \"TF(), example 5:`n\" t\n\n; Work directly with the clipboard or another other variable\nClipboard=Line 1`nLine 2`nLine 3`nLine 4`nLine 5`nLine 6`nLine 7`nLine 8\nClipboard:=TF_RemoveLines(Clipboard, 3, 6) ; remove lines 3 to 6\nMsgBox % \"Clipboard, example 6:`n\" Clipboard\n```\n    \n**Note** TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.\n\n**Common mistake(s):**\n\n\n```autohotkey\nMyVar:=TF_ReadLines(\"TestFile.txt\",5) ; this is wrong. It is AN INCORRECT EXAMPLE!\n```\n\nThe example above is incorrect: You pass on a file so the output is\ntestfile_copy.txt. In this case nothing meaningful is assigned to the\nvariable MyVar. Correct would be:\n\n\n```autohotkey\nMyVar:=TF_ReadLines(MyVar,5) ; this is OK\nMyVar:=TF_ReadLines(TF(\"TestFile.txt\",\"MyVar\"),5) ; this is OK\n```\n\n--------------------------------------------------------------------------\n\n# Functions and Examples\n\n**Note:** Some of these functions are very similar, but they are included in the library for ease of use ...\n\nYou can find examples of most functions in the \"example script\" here [http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363](http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363)\n\n<a name=\"TF\"></a>\n\n**TF(TextFile, CreateGlobalVar=\"T\")**\n\n- Purpose: Read contents of file to create global variable, **T** by default.\n- Parameters: TextFile, CreateGlobalVar (=preferred name of global variable)\n- Credits: See TF thread & discussion http://www.autohotkey.com/forum/viewtopic.php?p=313120#313120\n\nNote: see background info and examples above at [Files and Variables](#FilesAndVariables).\n\n<a name=\"TF_CountLines\"></a>\n\n**TF_CountLines(Text)**\n\n- Purpose: Returns the number of lines in a file or variable\n- Parameters: Text\n- Credits: SKAN\n\n\n```autohotkey\nMsgBox % TF_CountLines(\"File.txt\") ; show the number of lines of file in a MsgBox\nLines:=TF_CountLines(\"File.txt\") ; store the number of lines of file in a variable\n```\n\n<a name=\"TF_Count\"></a>    \n\n**TF_Count(String, Char)**\n\n- Purpose: Count the number of occurrence of Char using StringReplace\n- Parameters: String, Char\n\nNotes: do **not** use it to count lines (using `n) because it will return an incorrect value,\nuse TF_CountLines for counting lines. TF_Count does not support files and only works with variables.\n\n\n```autohotkey\nMsgBox % TF_Count(\"Hello this is an example\", \"i\") ; count how many i's there are in the string\n```\n\n<a name=\"TF_ReadLines\"></a>\n\n**TF_ReadLines(Text, StartLine = 1, EndLine = 0, RemoveTrailing = 0)**\n\n- Purpose: Return the specified lines, can be used to read sections of a file or variable.\n- Parameters: Text, StartLine, EndLine\n\nNote: by default it will add a newline character to the last line, so if you want to \nappend new data you don't have to start it with a newline char. If you don't want\nthat use RemoveTrailing = 1.\n\n\n```autohotkey\nMsgBox % TF_ReadLines(\"File.txt\",5) ; Read lines 5 to end of file, show result in a MsgBox\nLines:=TF_ReadLines(\"File.txt\",5) ; Read lines 5 to end of file, store result in variable\nMsgBox % TF_ReadLines(\"File.txt\",5,0,1) ; 0 for end line indicates until end of file, remove trailing empty line.\n```\n\n<a name=\"TF_Tail\"></a>\n\n**TF_Tail(Text, Lines = 1, RemoveTrailing = 0, ReturnEmpty = 1)**\n\n- Purpose: Read last X lines from file or variable\n- Parameters: Text, Lines, RemoveTrailing (0 or 1), ReturnEmpty (0 or 1)\n- Credits: incl. ideas from Tuncay\n\nNotes:\n\n1.  Lines can be a negative number: -1 will get the second to last line.\n    If you use a negative number it will ALWAYS retrieve a SINGLE line\n2.  By default TF_Tail will return empty lines, if you don't want that\n    use ReturnEmpty = 0\n3.  By default TF_Tail will add a newline character to the last line, so\n    if you want to append new data to the returned data you don't have\n    to start it with a newline char. If you don't want that use\n    RemoveTrailing = 1\n\n\n```autohotkey\nMsgBox % TF_Tail(\"File.txt\", 3) ; get the last three lines\nMsgBox % TF_Tail(\"File.txt\", -2) ; get second to last line, negative values only return one line\nMsgBox % TF_Tail(\"File.txt\", 5, 0, 0) ; return the last five lines, with trailing new line and excluding empty lines\n```\n\n<a name=\"TF_Replace\"></a>\n\n**TF_Replace(Text, SearchText, Replacement=\"\")**\n\n- Purpose: Find and Replace text in entire file (using StringReplace)\n- Parameters: Text, SearchText, Replacement\n\n\n```autohotkey\nTF_Replace(\"File.txt\",\"key\",\"lock\")            ; pass on a file, replace the word \"key\" with \"lock\" in file_copy.txt\n```\n\n<a name=\"TF_ReplaceInLines\"></a>\n\n**TF_ReplaceInLines(Text, StartLine = 1, EndLine = 0, SearchText = \"\", ReplaceText = \"\")**\n\n- Purpose: Find and Replace text on specific lines (using StringReplace)\n- Parameters: Text, StartLine, EndLine, SearchText, ReplaceText\n\n\n```autohotkey\nTF_ReplaceInLines(\"!File.txt\",\"1,3,9\",\"\",\"key\",\"lock\") ;  update source file, replace \"key\" with \"lock\" in lines 1, 3 and 9\n```\n\n<a name=\"TF_RegExReplace\"></a>\n\n**TF_RegExReplace(Text, NeedleRegEx = \"\", Replacement = \"\")**\n\n- Purpose: Find and Replace text in entire file (using RegExReplace)\n- Parameters: Text, NeedleRegEx, Replacement\n\n\n```autohotkey\nTF_RegExReplace(\"File.txt\",\"im)^(.*)$\",\"[$1]\") ; pass on a file, wrap all lines in []\n```\n\n<a name=\"TF_RegExReplaceInLines\"></a>\n\n**TF_RegExReplaceInLines(Text, StartLine = 1, EndLine = 0, NeedleRegEx= \"\", Replacement = \"\")**\n\n- Purpose: Find and Replace text in specific lines (using RegExReplace)\n- Parameters: Text, StartLine, EndLine, NeedleRegEx, Replacement\n\n\n```autohotkey\nTF_RegExReplaceInLines(\"File.txt\",3,8,\" [a-z]{3} \",\" lock \")  ; replace any three letter word with \"lock\" in lines 3 to 8\n```\n\n<a name=\"TF_RemoveLines\"></a>\n\n**TF_RemoveLines(Text, StartLine = 1, EndLine = 0)**\n\n- Purpose: Remove specified lines from file\n- Parameters: Text, StartLine, EndLine\n\n\n```autohotkey\nTF_RemoveLines(\"File.txt\", 5, 10) ; remove lines 5 to 10 from file\n```\n\nNote: If you pass on a negative value for StartLine, example\nTF_RemoveLines(Text, -5, .... it will remove these lines from the end\nof Text, so -5 will remove the last five lines. The EndLine parameter is\nignored.\n\n\n<a name=\"TF_RemoveBlankLines\"></a>\n\n**TF_RemoveBlankLines(Text, StartLine = 1, EndLine = 0)**\n\n- Purpose: Remove blank lines from file (in specified section)\n- Parameters: Text, StartLine, EndLine\n\n\nNote: also removes lines with only tabs & spaces e.g. \"white space\"\n\n\n```autohotkey\nTF_RemoveBlankLines(\"File.txt\") ; remove blanklines in entire file\n```\n\n\n<a name=\"TF_RemoveDuplicateLines\"></a>\n\n**TF_RemoveDuplicateLines(Text, StartLine = 1, Endline = 0, Consecutive= 0, CaseSensitive = false)**\n\n- Purpose: Remove duplicate lines, optionally you can specify if they need to be Consecutive and / or Case sensitive\n- Parameters: Text, StartLine, EndLine, Consecutive (0 or 1), CaseSensitive (true or false)\n\n\n```autohotkey\nTF_RemoveDuplicateLines(\"File.txt\",\"\",\"\",1,false) ; remove only Consecutive duplicate lines\n```\n\n<a name=\"TF_InsertLine\"></a>\n\n**TF_InsertLine(Text, StartLine = 1, Endline = 0, InsertText = \"\")**\n\n- Purpose: Insert text in specified lines\n- Parameters: Text, StartLine, Endline, InsertText\n\n\n```autohotkey\nTF_InsertLine(\"File.txt\",\"2,4,9\",5,\"---\")     ; insert --- in lines 2 4 and 9. 5 is endline will be ignored\n```\n\n<a name=\"TF_ReplaceLine\"></a>\n\n**TF_ReplaceLine(Text, StartLine = 1, Endline = 0, ReplaceText = \"\")**\n\n- Purpose: Replace specified lines with new text\n- Parameters: Text, StartLine, Endline, ReplaceText\n\n\n```autohotkey\nTF_ReplaceLine(\"File.txt\",\"1+3\",8,\"---\")      ; replace lines 1 4 and 7. 8 is end line so no more lines are processed after \n```\n\n<a name=\"TF_InsertPrefix\"></a>\n\n**TF_InsertPrefix(Text, StartLine = 1, EndLine = 0, Text = \"\")**\n\n- Purpose: Insert a text at the BEGINNING of each of the specified lines\n- Parameters: Text, StartLine, Endline, Text\n\n\n```autohotkey\nTF_InsertPrefix(\"File.txt\",5,8, \"Hello \")          ; Prefix Hello in lines 5 to 8\n```\n\n<a name=\"TF_InsertSuffix\"></a>\n\n**TF_InsertSuffix(Text, StartLine = 1, EndLine = 0 , Text = \"\")**\n\n- Purpose: Append a text at the END of each of the specified lines\n- Parameters: Text, StartLine, Endline, Text\n\n\n```autohotkey\nTF_InsertSuffix(\"File.txt\",\"2-4,6-9\",\"\", \" Hello\")  ; Suffix Hello in lines 2 to 4 and 6 to 9\n```\n\n<a name=\"TF_TrimLeft\"></a>\n\n**TF_TrimLeft(Text, StartLine = 1, EndLine = 0, Count = 1)**\n\n- Purpose: Trim number of specified columns from specified lines\n- Parameters: Text, StartLine, Endline, Text, Count\n\n\n```autohotkey\nTF_TrimLeft(\"File.txt\",\"\",\"\",25)                    ; Trim Left 25 Characters of all lines\n```\n\n<a name=\"TF_TrimRight\"></a>\n\n**TF_TrimRight(Text, StartLine = 1, EndLine = 0, Count = 1)**\n\n- Purpose: Trim number of specified columns from specified lines\n- Parameters: Text, StartLine, Endline, Text, Count\n\n\n```autohotkey\nTF_TrimRight(\"File.txt\",\"4,6,8\",\"\",45)              ; Trim Right 45 Characters in lines 4, 6 and 8\n```\n\n<a name=\"TF_AlignLeft\"></a>\n\n**TF_AlignLeft(Text, StartLine = 1, EndLine = 0, Columns = 80, Padding = 0)**\n\n- Purpose: Align lines according to number of specified columns\n- Parameters: Text, StartLine, Endline, Columns, Padding\n - incl. ideas from SKAN\n\nNote: Using the Align functions will not take into account any leading or trailing\nspaces a line had BEFORE processing. So all white space before and after the text\nis removed before the text is aligned.\n\n~~~~\nPadding = 0 Remove trailing white space\nPadding = 1 Keep trailing white space\n~~~~\n\n\n```autohotkey\nTF_AlignLeft(\"File.txt\",\"\",\"\",90, 1)    ; AlignLeft all lines, keep trailing white space\n```\n\n<a name=\"TF_AlignCenter\"></a>\n\n**TF_AlignCenter(Text, StartLine = 1, EndLine = 0, Columns = 80, Padding = 0)**\n\n- Purpose: Align lines according to number of specified columns\n- Parameters: Text, Columns, StartLine, Endline, Text\n- uses some of SKAN functions\n\nNote: Using the Align functions will not take into account any leading or trailing\nspaces a line had BEFORE processing. So all white space before and after the text\nis removed before the text is aligned.\n\n~~~~\nSkip = 0 process empty lines, fill with spaces\nSkip = 1 skip empty lines, do not fill with spaces\n~~~~\n\n\n```autohotkey\nTF_AlignCenter(\"File.txt\",\"\",\"\",150, 1) ; AlignCenter, all lines skip empty lines, do not fill with spaces\n```\n\n<a name=\"TF_AlignRight\"></a>\n\n**TF_AlignRight(Text, StartLine = 1, EndLine = 0, Columns = 80, Skip = 0)**\n\n- Purpose: Align lines according to number of specified columns\n- Parameters: Text, Columns, StartLine, Endline, Skip (0 or 1)\n - uses some of SKAN functions\n\nNote: Using the Align functions will not take into account any leading\nor trailing spaces a line had BEFORE processing. So all white space\nbefore and after the text is removed before the text is aligned.\n\n~~~~\nSkip = 0 process empty lines, fill with spaces\nSkip = 1 skip empty lines, do not fill with spaces\n~~~~\n\n\n```autohotkey\nTF_AlignRight(\"File.txt\",\"\",\"\", 190, 0) ; AlignRight, all lines, do not skip empty lines fill them with spaces\n```\n\n<a name=\"TF_LineNumber\"></a>\n\n**TF_LineNumber(Text, Leading = 0, Restart = 0, Char = 0)**\n\n- Purpose: Insert line numbers before each line\n- Parameters: Text, Leading, Restart, Char\n- Credits: incl. ideas from ribbet.1\n\n~~~~\nLeading = 0 No padding with leading zeros\nLeading >= 1 Include padding with leading zeros (001 v 1)\nRestart = restart counter after X lines (starting again at 1)\nChar = character to use for leading/padding. Default 0, but can be any character. Tip: use a space.\n~~~~\n\n\n```autohotkey\nTF_LineNumber(\"File.txt\",1,15,A_Space)  ; Add linenumbers, padding with spaces, linenumbers are aligned right because of space\n```\n\n<a name=\"TF_ConCat\"></a>\n\n**TF_ConCat(FirstTextFile, SecondTextFile, OutputFile, Blanks = 0, FirstPadMargin = 0, SecondPadMargin = 0)**\n\n- Purpose: Join Text files side by side (line1-from-file1 line1-from-file2 newline line2-from-file1 line2-from-file2 newline etc)\n- Parameters: FirstTextFile, SecondTextFile, OutputFile, Blanks, FirstPadMargin, SecondPadMargin\n\nBased on: CONCATenate text files, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip - Backup: http://www.retroarchive.org/garbo/pc/ts/\n\nWith TF_ConCat you can join Text files side by side. Blanks is number of blanks between lines.\nYou can pad blanks the right margin of either of the text files, for this use FirstPadMargin and SecondPadMargin.\n\n<a name=\"TF_ColGet\"></a>\n\n**TF_ColGet(Text, StartLine = 1, EndLine = 0, StartColumn = 1, EndColumn = 1, Skip = 0)**\n\n- Purpose: Get specified columns from text file\n- Parameters: Text, StartLine, EndLine, StartColumn, EndColumn, Skip (0 or 1)\n\n~~~~\nNote: A TAB character is 1 character so for files with TABS the column might not be where you expect it to be\nskip = 0, DO NOT skip lines shorter then startcolumn position\nskip = 1, skip lines shorter then startcolumn position\nTF_ColGet and TF_ColPut accept negative StartColumn parameter. This can be used to \"Get\" or \"Put\" text in the x-th \"column\" from the right\n~~~~\n\n\n```autohotkey\nTF_ColGet(\"File.txt\",\"\",\"\",2,13)    ; get columns 2 to 13 from all lines, so it removes all other columns from the file or variable\n```\n\n<a name=\"TF_ColPut\"></a>\n\n**TF_ColPut(Text, Startline = 1, EndLine = 0, StartColumn = 1, Text = \"\", Skip = 0)**\n\n- Purpose: Insert text at specified columns in text file\n- Parameters: Text, StartLine, EndLine, StartColumn, EndColumn, Skip (0 or 1)\n\nBased on: COLPUT.EXE & CUT.EXE, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip - Backup: http://www.retroarchive.org/garbo/pc/ts/\n\n~~~~\nNote: A TAB character is 1 character so for files with TABS the column might not be where you expect it to be\nskip = 0, DO NOT skip lines shorter then startcolumn position\nskip = 1, skip lines shorter then startcolumn position\nTF_ColGet and TF_ColPut accept negative StartColumn parameter. This can be used to \"Get\" or \"Put\" text in the x-th \"column\" from the right\n~~~~\n\n\n```autohotkey\nTF_ColPut(\"File.txt\",\"\",\"\",5, \"|\", 0) ; insert | in column 5 of all lines including empty lines (= | will be at column 1)\n```\n\n<a name=\"TF_ColCut\"></a>\n\n**TF_ColCut(Text, StartLine = 1, EndLine = 0, StartColumn = 1, EndColumn = 1)**\n\n- Purpose: Remove specified columns from text file\n- Parameters: Text, StartLine, EndLine, StartColumn, EndColumn\n\nBased on: COLPUT.EXE & CUT.EXE, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip\n\n\n```autohotkey\nTF_ColCut(\"File.txt\", \"2+2\", \"\", 4, 38)  ; remove columns 4 to 38 in lines 2 4 6 8 etc\n```\n\n<a name=\"TF_ReverseLines\"></a>\n\n**TF_ReverseLines(Text, StartLine = 1, EndLine = 0)**\n\n- Purpose: Reverse the order of specified lines\n- Parameters: Text, StartLine, EndLine\n\nNote: Startline parameter can not use specific lines, sections or incremental values here\n\n\n```autohotkey\nTF_ReverseLines(\"File.txt\",2,9) ; reverse lines 2 to 9\n```\n\n<a name=\"TF_Find\"></a>\n\n**TF_Find(Text, StartLine = 1, EndLine = 0, SearchText = \"\", ReturnFirst = 1, ReturnText = 0)**\n\n- Purpose: Find text using RegExMatch, return line(s), text or lines and text\n- Parameters: Text, StartLine, EndLine, SearchText, ReturnFirst, ReturnText\n\nTF_Find(Lines) uses Regular Expressions - This means that if you use certain characters which\nhave a special meaning in a RegEx: **\\\\.\\*?+[{|()\\^\\$** they must be preceded by a backslash\nto be seen as literal. For example, **\\\\.** is a literal period and **\\\\\\\\** is a literal backslash.\nEscaping can be avoided by using **\\\\Q**...**\\\\E**. For example: **\\\\QLiteral Text\\\\E**.\nSee [http://ahkscript.org/docs/commands/RegExMatch.htm](http://ahkscript.org/docs/commands/RegExMatch.htm)\nfor further information.\n\n~~~~\nReturnFirst = 0 return multiple lines \nReturnFirst = 1 return first line only\n\nReturnText = 0 return line numbers only\nReturnText = 1 return entire line (text). This simulates a basic grep feature\nReturnText = 2 return line numbers + entire line (text). This simulates a basic grep feature \n~~~~\n\n\n```autohotkey\nMsgBox % TF_Find(\"File.txt\", \"\", \"\", \"keys\") ; return first line number with keys in it\nMsgBox % TF_Find(\"File.txt\", \"\", \"\", \" c[a-z]+s \", 0, 1) ; find all lines with words that start with a c an end with an s\n```\n\n<a name=\"TF_SplitFileByLines\"></a>\n\n**TF_SplitFileByLines(Text, SplitAt, Prefix = \"file\", Extension = \"txt\", InFile = 1)**\n\n- Purpose: Split a text file in to several others based on number of lines\n- Parameters: Text, SplitAt (number), Prefix, Extension, InFile (0, 1 or 2)\n\n~~~~\nSplitAt = Number of lines (three methods, see below)\nPrefix . Extension = filenameAUTOINCREMENT.Extension (Example: part_ . txt)\nInFile = 0 skip line e.g. do not include the actual line in any of the output files\nInFile = 1 include line IN current file\nInFile = 2 include line IN next file\n~~~~\n\nNote: If you pass on a variable to TF_SplitFileByLines, the array size will be returned\nas Prefix0 and the array elements as Prefix1, Prefix2 etc similar to the AHK StringSplit\ncommand. The Extension parameter is ignored when using variables. Some characters are\nillegal such as - + @ % & \\* _ \\\\ / [ ] etc to use in Prefix, stick to a-z A-Z.\n\nOptions for \"SplitAt\" parameter:\n\n~~~~\na) One numerical value, example TF_SplitFileByLines(Text, \"25\", .... \n   will split text every 25 lines until the end \nb) Split at rotating line lengths using a dash \"-\" as separator, example TF_SplitFileByLines(Text, \"5-10-15\", .... \n   will split text at 5 10 15 lines, until the end so 5 lines, 10 lines, 15 lines, 5 lines, 10 lines etc\nc) Split at specific lines using a comma \",\" as separator, example TF_SplitFileByLines(Text, \"5,81,135\", .... \n   will split text at lines 5,81,135 until end of file (e.g. last file will be from line 135 until the end)\n~~~~\n\n\n```autohotkey\nTF_SplitFileByLines(\"File.txt\", 2, \"part\", \"zec\", 1) ; split source file every 2 lines, include 2nd line INFILE\n; illustrate use of variables and returned arrays:\nTF_SplitFileByLines(Variable, 2, \"part\", \"zec\", 1)\nMsgBox % \"Array size: \" . Part0 . \"`n1st array element: \"  Part1\n```\n\n<a name=\"TF_SplitFileByText\"></a>\n\n**TF_SplitFileByText(Text, SplitAt, Prefix = \"file\", Extension = \"txt\", InFile = 1)**\n\n- Purpose: Split a text file in to several others based on text\n- Parameters: Text, SplitAt (text, can be RegEx), Prefix, Extension, InFile (0, 1 or 2)\n\n~~~~\nSplitAt = Text, can be RegEx\nPrefix . Extension = filenameAUTOINCREMENT.Extension (Example: part_ . txt)\nInFile = 0 skip line e.g. do not include the actual line in any of the output files\nInFile = 1 include line IN current file\nInFile = 2 include line IN next file\n~~~~\n\nNote: If you pass on a variable to TF_SplitFileByLines, the array size will be returned\nas Prefix0 and the array elements as Prefix1, Prefix2 etc similar to the AHK StringSplit\ncommand. The Extension parameter is ignored when using variables. Some characters are \nillegal such as - + @ % & \\* _ \\\\ / [ ] etc to use in Prefix, stick to a-z A-Z.\n\n\n```autohotkey\nTF_SplitFileByText(\"File.txt\", \"button\", \"part\", \"zec\", 1)   ; split source file on every line with the word button, include that line INFILE\n; illustrate use of variables and returned arrays:\nTF_SplitFileByText(Variable, \"keyboard\", \"part\", \"zec\", 1)\nMsgBox % \"Array size: \" . Part0 . \"`n1st array element: \"  Part1\n```\n\n**TF_Merge(FileList, Separator = \"\\`n\", FileName = \"merged.txt\")**\n\n- Purpose: Merge several files into one\n- Parameters: FileList, Separator (what to put between two files, newline by default), FileName (name of output file, Prefix with a ! to overwrite target file)\n\nExample FileList:\n\n~~~~\nFileList=\n(\nfile1.txt\nfile2.txt\nfile3.txt\n)\n~~~~\n\nNote: TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.\n\n\n```autohotkey\n; using Loop (files & folders) to create one quickly if you want to merge all TXT files for example:\nLoop, c:\\*.txt\n  FileList .= A_LoopFileFullPath \"`n\"\nTF_Merge(FileList) ; will create merged.txt, you can use ! to overwrite an existing file if you want\n\n; using FileSelectFile to select files to merged: (Thanks for asking Vitor, http://www.autohotkey.com/forum/viewtopic.php?p=335329#335329)\nFileDelete merged.txt ; not required\nFileList=\nFileSelectFile, FileList, M 1,,, *.txt ; M allows you to select multiple files while holding down the left ctrl button\nIf (ErrorLevel = 1) or (FileList = \"\")\n   ExitApp ; no files selected\nPath:=TF_ReadLines(FileList,1,1,1) ; the first line holds the directory of the selected files, so read path\nFileList:=TF_RemoveLines(FileList,1,1) ; remove path from filelist\nFileList:=TF_InsertPrefix(FileList, \"\", \"\", Path . \"\\\") ; make sure all files have full paths to file so the are read correctly\nTF_Merge(FileList) ; will create a file in the current script dir called merged.txt you can also specify another filename, take into account the filedelete merged.txt above\n\n; You could skip the Path:= step above by calling TF_ReadLines directly in TF_InsertPrefix, but you would have to delete the first line AFTER it like so:\n;FileList:=TF_InsertPrefix(FileList, \"\", \"\", TF_ReadLines(FileList,1,1,1) . \"\\\")\n;FileList:=TF_RemoveLines(FileList,1,1)\n```\n\n<a name=\"TF_Prepend\"></a>\n\n**TF_Prepend(File1, File2)**\n\n- Purpose: Prepend file1 to file2 (file2 is changed, uses TF_Merge)\n- Parameters: File1, File2\n\nNote: TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.\n\n<a name=\"TF_Append\"></a>\n\n**TF_Append(File1, File2)**\n\n- Purpose: Append file1 to file2 (file2 is changed, uses TF_Merge)\n- Parameters: File1, File2\n\nNote: TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.\n\n<a name=\"TF_Wrap\"></a>\n\n**TF_Wrap(Text, Columns = 80, AllowBreak = 0, StartLine = 1, EndLine = 0)**\n\n- Purpose: Wrap (specified) lines\n- Parameters: Text, Columns, AllowBreak (0 or 1), StartLine, EndLine\n\n~~~~\nAllowBreak = 0 will not \"break\" words, so it will take into account whole words and not chop them off. \nAllowBreak = 1 will break words\n~~~~\n\n\n```autohotkey\nTF_Wrap(\"File.txt\",60)            ; wrap at col 60\n```    \n\n<a name=\"TF_WhiteSpace\"></a>\n\n**TF_WhiteSpace(Text, RemoveLeading = 1, RemoveTrailing = 1, StartLine = 1, EndLine = 0)**\n\n- Purpose: Remove leading and/or trailing whitespace\n- Parameters: Text, RemoveLeading (0 or 1), RemoveTrailing (0 or 1), StartLine, EndLine\n\n~~~~\nRemoveLeading = 0  Do not remove leading white space of lines\nRemoveLeading = 1  Remove leading white space of lines\nRemoveTrailing = 0 Do not remove trailing white space of lines\nRemoveTrailing = 1 Remove trailing white space of lines       \n~~~~\n\n\n```autohotkey\nTF_WhiteSpace(\"File.txt\", 1, 0, \"5-10\") ; remove leading and keep trailing whitespace in lines 5 to 10\n```    \n\n<a name=\"TF_Substract\"></a>\n\n**TF_Substract(File1, File2, PartialMatch = 0)**\n\n- Purpose: Delete lines from file1 in file2 (using StringReplace)\n- Parameters: File1, File2, PartialMatch (0 or 1)\n\n~~~~\nFile2: you can use !file2 to overwrite file2 otherwise output is file2_copy.txt\nPartialMatch = 0 lines from file1 must appear as is, case insensitive \nPartialMatch = 1 allow for paRTIal match of line\nPartialMatch = 2 allow for paRTIal match of line, but remove entire line (uses RegEx, new as of v3.4)\n~~~~\n\n<a name=\"TF_RangeReplace\"></a>\n\n**TF_RangeReplace(Text, SearchTextBegin, SearchTextEnd, ReplaceText = \"\", CaseSensitive = \"False\", KeepBegin = 0, KeepEnd = 0)**\n\n- Purpose: A Range Replacement allows you to perform a replacement on text whose beginning and ending remains the same, but whose middle contents might change.\n- Parameters: SearchTextBegin, SearchTextEnd, ReplaceText, CaseSensitive (True or False), KeepBegin (0 or 1), KeepEnd (0 or 1)\n\nNote: Similar to \"BK Replace EM\" Range Replacement. It is basically an easier shortcut for a more complex RegExp.\n\n~~~~\nKeepBegin = 0 Remove SearchTextBegin from file\nKeepBegin = 1 Do not remove SearchTextBegin from file (saves you the trouble of putting it back in via ReplaceText)\nKeepEnd = 0 Remove SearchTextEnd from file\nKeepEnd = 1 Do not remove SearchTextEnd from file (saves you the trouble of putting it back in via ReplaceText)\n\nSearchTextBegin = \"\" e.g. empty means from START of File *1\nSearchTextEnd = \"\" e.g. empty means until END of File *1\n~~~~\n\n\\*1 if you pass on empty strings to SearchTextBegin and SearchTextEnd the entire file will \nreplaced with the replacement text.\nSearchTextBegin/End **can** be on the same line. Remember this LIB mainly operates on\na line by line basis. This Function only operates on the FIRST SearchTextBegin to \nthe FIRST SearchTextEnd it finds.\n\n\n```autohotkey\nRange=[insert this`ntext for the`nrange replace Text`ntest function]\nTF_RangeReplace(\"File.txt\", \"Create hotkeys for keyboard\", \"into an EXE file\", Range)\n```\n\n<a name=\"TF_MakeFile\"></a>\n\n**TF_MakeFile(Text, Lines = 1, Columns = 1, Fill = \" \")**\n\n- Purpose: Create file of X lines and Y columns, fill with space or other character(s)\n- Parameters: TextFile (new file to be created), Lines, Columns, Fill\n\nSometimes you need an \"empty\" file before you can start adding line numbers or putting data into a file.\n\n<a name=\"TF_Tab2Spaces\"></a>\n\n**TF_Tab2Spaces(Text, TabStop = 4, StartLine = 1, EndLine = 0)**\n\n- Purpose: Convert tabs to spaces, shorthand for TF_ReplaceInLines\n- Parameters: Text, TabStop, StartLine, EndLine\n- Credits: incl. ideas from infogulch\n\n~~~~\nTabStop = number of spaces to replace a TAB with, so 4 means a TAB will be replaced by 4 spaces\n~~~~\n\n<a name=\"TF_Spaces2Tab\"></a>\n\n**TF_Spaces2Tab(Text, TabStop = 4, StartLine = 1, EndLine = 0)**\n\n- Purpose: Convert tabs to spaces, shorthand for TF_ReplaceInLines\n- Parameters: Text, TabStop, StartLine, EndLine\n- Credits: incl. ideas from infogulch\n\n~~~~\nTabStop = number of spaces to replace with a TAB, so 4 means 4 spaces will be replaced by a TAB\n~~~~\n\n<a name=\"TF_Sort\"></a>\n\n**TF_Sort(Text, SortOptions = \"\", StartLine = 1, EndLine = 0)**\n\n- Purpose: Sort (section of) TextFile\n- Parameters: Text, SortOptions, StartLine, EndLine\n\n~~~~\nSortOptions: use the SORT options http://ahkscript.org/docs/commands/Sort.htm\n~~~~\n\nNote: StartLine can not have multiple sections, increments or multiple lines in this case.\nWhen dealing with variables/strings instead of text files the native AHK Sort command is\nof course more useful.\n\n<a name=\"TF_Join\"></a>\n\n**TF_Join(Text, StartLine = 1, EndLine = 0, SmartJoin = 0, Char = 0)**\n\n- Purpose: Join lines (section of)\n- Parameters: Text, StartLine, EndLine, SmartJoin, Char\n\n~~~~\nSmartJoin: Detect if CHAR(s) is/are already present at the end of the line before joining the next, this to prevent unnecessary double spaces for example.\nChar: character(s) to use between new lines, defaults to a space. To use nothing use \"\"\n~~~~\n\nNote: StartLine can not use increments. It can have multiple sections.\n\n<a name=\"TF_Save\"></a>\n\n**TF_Save(Text, FileName, OverWrite = 1)**\n\n- Purpose: To save a variable to a file\n- Parameters: Text, FileName, OverWrite (O or 1)\n\n~~~~\nOverWrite = 0 create filename_copy.ext if filename.ext exists\nOverWrite = 1 will overwrite filename.ext if filename.ext exists (default)\n~~~~\n\n# TF Lib Errors\n\n1. Error when using the ! prefix.  \n   The correct syntax is \"!file.txt\". If you do this !\"file.txt\" (the ! is not within the quotes it will not be able to read a file)\n2. If you pass on a single zero as text ```TF_TrimLeft(\"0\",1,1,3)``` and there is no file with that name ```\"0\"``` it will shown an error. More as one zero as \"text\" is OK.\n\n# History\n\n**History v3.8, 11 December 2020**\n\n- Prevent TF_Sort from removing last character from the last line https://github.com/hi5/TF/issues/11\n\n**History v3.7, 16 April 2017**\n\n- Added: additional check for passing on just zero/zeros as text to TF functions in TF_GetData() - see v3.6 below\n- Fix: changed readme.md to fix rendering issue of MD files on GH - https://github.com/hi5/TF/issues/5\n\n**History v3.6, 25 December 2014**\n\n- Added: Added additional minor error check in TF_GetData - if \"Text\" is \"false\", there is nothing to process so Exit\n\n**History v3.5, 2 August 2014**\n\n- Changed A_ScriptDir to A_WorkingDir in TF_ReturnOutPut - https://github.com/hi5/TF/issues/1\n- Fixed: TF_ColGet negative startcolumn and sections now work correctly\n\n**History v3.4, 30 October 2010 - UNRELEASED at the time**\n\n- New function: TF_Join: joining lines.\n- Added: A_ThisFunc to helper function TF_MatchList, this may help with error messages such as: Invalid StartLine parameter (non numerical character) - Function used: A_ThisFunc\n- Added/Change: New option for StartLine, Negative startline operate on last X lines was already the case for TF_removelines, applies to all TF functions\n- Added: TF_Substract: Partialmatch = 2 now deletes entire line on partial match, see TF DOC for more info.\n- Added: TF_ColGet and TF_ColPut now accept negative StartColumn parameter. Can be used to Get or Put text in the xth \"column\" from the right, see description above - TF_ColCut does **not** support this as you can use TF_TrimRight.\n- Added: TF_ColGet, TF_ColPut and TF_ColCut can now process multiple columns in one go. Format: CSV or incremental (DO **not** use sections for TF_ColPut due to unexpected end results)\n- Minor improvements to the documentation\n- removed TF_FindLines from lib (was already deprecated)\n\n**History v3.3, 16 April 2010**\n\n- Fixed: If you used variables with any of the replacement functions or tried to remove empty lines from a VARIABLE in the following format: variable:=TF_..(variable,\"search\",\"replace\") and the searchtext was NOT present in the variable it returned an empty variable (e.g. deleted the contents of \"variable\"). It affected TF_ReplaceInLines, TF_Replace, TF_RegExReplaceInLines, TF_RegExReplace, TF_RemoveBlankLines, TF_RangeReplace\\. The built-in check only worked correctly for FILES and with the introduction of variables in TF 3 this didn't surface during testing.\n- Fixed: documentation error for TF_Merge (separator and filename where swapped) and added examples on how to use TF_Merge in a Loop and with FileSelectFile\n\n**History v3.2, 20 February 2010**\n\n- Fixed: TF_AlignRight: due to a bug it didn't work as it should have, it prepended the number of spaces rather than aligning the text at the specified width.\n- Changed: TF_GetData (helper function) should now avoid unnecessary IfNotExist for files, for scripts with many loops in combination with variables it should improve the speed slightly\n- Changed: TF_SplitFileByLines: New options for SplitAt, now three methods available, see notes at function description\n- Changed: TF_RemoveLines: New option for StartLine, if negative value is used it will remove the last X lines from file, see notes at function description\n\n**History v3.1, 09 December 2009**\n\n- Changed: Rewrite of TF_Find. Can now return multiple lines (like TF_Findlines used to), not only linenumbers but the entire line (text) of found lines so it can be used as a basic grep. Now uses RegExp, see compatibility notes.\n- Deprecated: TF_FindLines (see change TF_Find). Kept in for backwards compatibility\n- Updated and expanded documentation with examples and a hopefully \"better\" introduction highlighting some basic concepts\n\n**History v3.0, 27 November 2009**\n\nComplete overhaul of library, now accepts files & variables for input and output:\n\n- Changed: New parameter for TF_Readlines & TF_Tail: trailing new line now optional\n- Added: TF_Save, shorthand for filedelete+fileappend\n- Added: TF_GetData, helper function to determine if a file or a variable is passed on to function\n- Added: TF() To read a text file in global var, t by default - Credits various ...\n- Added: TF_ReturnOutput has replaced: Overwrite, MakeCopy and the newly developed ReturnStr\n- Changed: Complete rewrite of TF_Tail, new options - borrowed bits from Tuncay (Thanks!)\n- Changed: MakeMatchList: Removed TF_Countlines (one less fileread), Pass on \"String\" and not a TextFile\n- Fix: TF_ConCat & TF_MakeFile didn't write output file - same bug as splitby* earlier.\n\n**History v2.5 fix, 01 November 2009**\n\n- Unreleased, but available on request :-) Note that in 2.4a TF_MakeFile and TF_ConCat do not produce output files, easy to fix if you are determined to use an older version of TF.\n\n**History v2.4a fix, 10 August 2009:**\n\n- The fix for TF_SplitFileBy* functions of 2.3b wasn't complete, now  it should work correctly (it didn't write the last file)\n\n**History v2.4 update, 06 August 2009:**\n\n- Added: TF_Sort\n\n**History v2.3b update, 03 August 2009:**\n\n- Fixed: No output problem for TF_SplitFileBy* functions, \"bug\" introduced by 2.3a \"Built in Check if TextFile actually exists\"\n- TF_RemoveBlankLines check if file has empty empty lines to start with, if not return and do nothing (does not create file_copy identical to file)\n- TF_RangeReplace same fix as 2.3a\n\n**History v2.3a update/bugfix (29 July 2009, HugoV, ribbet.1, Murp|e)**\n\nFixed/Changed:\n\n- New features in TF_LineNumber: Restart + Choice of leading/padding character. Thanks for the idea ribbet.1, [http://www.autohotkey.com/forum/viewtopic.php?p=284687#284687](http://www.autohotkey.com/forum/viewtopic.php?p=284687#284687)\n- Built in Check if TextFile actually exists, to prevent creation of empty file(s). Thanks for the idea Murp|e, [http://www.autohotkey.com/forum/viewtopic.php?p=284649#284649](http://www.autohotkey.com/forum/viewtopic.php?p=284687#284687)\n- TF_Replace: if SearchText wasn't present in TextFile the function never returned (stuck in endless loop)\n- TF_ReplaceInLines: if SearchText wasn't present in TextFile simple return an do nothing (faster, does not create file_copy)\n- TF_RegExReplaceInLines: if NeedleRegEx wasn't present in TextFile simple return an do nothing (faster, does not create file_copy)\n\n**History v2.3 (28 July 2009)**\n\n- Added: TF_Tab2Spaces - Note: Thanks to infogulch\n- Added: TF_Spaces2Tab - ditto\n- Added: TF_RangeReplace\n- Added: TF_MakeFile\n\nAlso thanks to Murp|e for pointing out some errors in the documentation (TF_COL\\* functions).\n\n**History v2.2 (10 July 2009)**\n\n- Added: TF_Substract\n- Added: TF_WhiteSpace\n- Added: TF_Wrap\n\n**History v2.1**\n\n- Added: TF_Prepend\n- Added: TF_Append\n\n**History v2.0 (Overhaul by HugoV using TXT lib as starting point)**\n\n- Renamed: TF_TotalLines to TF_CountLines (seemed more logical)\n- Removed: TF_GetCSV, TF_SetCSV to keep the focus on TXT files, CSV requires a different library IMHO (HugoV)\n- Introduced: _MakeMatchList_ and adjusted TF_ReadLines, TF_RemoveLines, TF_InsertPrefix, TF_InsertSuffix, TF_TrimLeft, TF_TrimRight, TF_ColGet, TF_ColPut, TF_ColCut accordingly (HugoV)\n     - Make a MatchList which is used in various functions (listed above)\n     - Using a MatchList gives greater flexibility so you can process multiple sections of lines in one go avoiding repetitive fileread/append actions\n     - You can pass on multiple lines (sections) If you quote the StartLine parameter. Examples of StartLine, Endline:\n     5 ; start from line 5 to the end (StartLine)\n     5, 15 ; lines 5 to 15 (StartLine, EndLine)\n     \"5,13,45,67\", 135) ; lines 5,13,45,67, ignore 135 (\"StartLine\", Endline - EndLine value is ignored)\n     \"5-13,45-51\", 135) ; lines 5 to 13, 45 to 51 ignore 135 (\"StartLine\", Endline - EndLine value is ignored)\n     - Incremental processing of lines, example startline, endline - endline will not be ignored\n     \"2+3\", 150 ; start with line 2, increment 3 up-to line 150 so 2,5,8,11,14 etc\n     \"5+15\" ; start with line 5, increment 15 until end of file so  5,20,35 etc\n- Introduced: _OverWrite_ & _MakeCopy_ (Heresy / HugoV)\n     - No longer necessary to pass on full path, e.g. \"file.txt\" works OK now (HugoV)\n     - Reduces size of library by removing repeating code sections for saving Output (Heresy)\n     - Backup files: If a subdirectory \"backup\" is present in the directory of TextFile a backup is made before overwriting the original file (both for file.txt and file_copy.txt) with the BAK extension (HugoV)\n     - Includes fix to remove trailing \\`n added by most functions (HugoV)\n- Introduced: SetWidth/space by SKAN for TF_Align* Functions http://www.autohotkey.com/forum/viewtopic.php?p=45880#45880\n- Added: TF_ReplaceInLines. Similar to TF_Replace\n- Added: TF_RegExReplaceInLines. Similar to TF_RegExReplace\n- Added: TF_SplitFileByLines (HugoV)\n- Added: TF_SplitFileByText (HugoV)\n- Added: TF_Merge (HugoV)\n- Added: TF_Find (HugoV, based on example by olegbl)\n- Added: TF_FindLines (HugoV, based on example by olegbl)\n- Change: TF_TrimLeft. 1 - it now processes all lines of the TextFile. 2 - you can now omit EndLine e.g. will process until end of file (HugoV)\n- Change: TF_TrimRight. 1 - it now processes all lines of the TextFile. 2 - you can now omit EndLine e.g. will process until end of file (HugoV)\n- Change: TF_AlignCenter, TF_AlignLeft, TF_AlignRight. Now use SetWidth/space by SKAN, more reliable also adds spaces to RIGHT side of line\n- Change: TF_AlignLeft. 1 - you can now use StartLine, EndLine parameters. 2 - StartLine and/or EndLine can be omitted (HugoV)\n- Change: TF_AlignRight. 1 - you can now use StartLine, EndLine parameters. 2 - StartLine and/or EndLine can be omitted (HugoV)\n- Change: TF_ReadLines you can now omit EndLine e.g. will read until end of file (HugoV)\n- Change: TF_RemoveBlankLines you can now use StartLine, EndLine parameters. EndLine can be omitted (HugoV)\n- Change: TF_InsertLine modified to accommodate line sections and incremental lines.\n- Change: TF_ReplaceLine modified to accommodate line sections and incremental lines.\n- Change: TF_RegExReplace no longer returns \"Counts\" (HugoV)\n- Change: TF_RemoveDuplicateLines 1 - no longer returns \"Removed\". 2 - can use start/end line, 3 - no longer uses sort but keeps original order intact, 4 - to methods to check consecutive lines or unique files in file (HugoV)\n- Change: TF_ColGet, TF_ColPut, TF_ColCut. 1 - order of parameters changed, 2 - now accept Startline, EndLine parameters. (HugoV)\n- Fixed: TF_RemoveDuplicateLines always produced empty Output file (HugoV)\n\n**History prior 2.0 - TXT library (Heresy, HugoV)**\n\n- 2008-06-18 : added TF_AlignLeft(), TF_AlignCenter(),  TF_AlignRight() (Heresy)\n- 2008-06-18 : added TF_Tail(), TF_ReverseLines() (HugoV)\n- 2008-06-17 : added TF_GetCSV(), TF_SetCSV() (Heresy)\n- 2008-06-17 : added TF_LineNumber(), TF_Concat(), TF_ColGet(), TF_ColPut(), TF_ColCut() (HugoV)\n- 2008-06-16 : added TF_RegExReplace(), TF_RemoveLines(), TF_TrimLeft(), TF_TrimRight() (Heresy)\n- 2008-06-15 : added TF_TotalLines(), TF_ReadLines(), TF_RemoveDuplicateLines() (Heresy)\n\n## Credits\n\nThis library is based on the *Library for Text file manipulation* started by [Heresy](http://www.autohotkey.com/forum/profile.php?mode=viewprofile&u=8193). I have contributed a number of functions to that library, but that version also has some \"bugs\" which are hopefully resolved in this TF library.\n\nThanks to:\nHeresy, SKAN! (countlines, setwidth, spaces), Olegbl, (suggestion for find*), infogulch (suggestion for tab \\<-\\> spaces),\nMurp|e (suggestions for documentation, check if file exists), ribbet.1 (New features in TF_LineNumber), Tuncay (help with\nTF() and borrowed ideas for TAIL).\n"
  },
  {
    "path": "tf.ahk",
    "content": "/*\nName          : TF: Textfile & String Library for AutoHotkey\nVersion       : 3.8\nDocumentation : https://github.com/hi5/TF\nAutoHotkey.com: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=576\nAutoHotkey.com: http://www.autohotkey.com/forum/topic46195.html (Also for examples)\nLicense       : see license.txt (GPL 2.0)\n\nCredits & History: See documentation at GH above.\n\nStructure of most functions:\n\nTF_...(Text, other parameters)\n\t{\n\t ; get the basic data we need for further processing and returning the output:\n\t TF_GetData(OW, Text, FileName)\n\t ; OW = 0 Copy inputfile\n\t ; OW = 1 Overwrite inputfile\n\t ; OW = 2 Return variable\n\t ; Text : either contents of file or the var that was passed on\n\t ; FileName : Used in case OW is 0 or 1 (=file), not used for OW=2 (variable)\n\n\t ; Creates a matchlist for use in Loop below\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; A_ThisFunc useful for debugging your scripts\n\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t...\n\t\t\t}\n\t\t Else\n\t\t\t{\n\t\t\t...\n\t\t\t}\n\t\t}\n\t ; either copy or overwrite file or return variable\n\t Return TF_ReturnOutPut(OW, OutPut, FileName, TrimTrailing, CreateNewFile)\n\t ; OW 0 or 1 = file\n\t ; Output = new content of file to save or variable to return\n\t ; FileName\n\t ; TrimTrailing: because of the loops used most functions will add trailing newline, this will remove it by default\n\t ; CreateNewFile: To create a file that doesn't exist this parameter is needed, only used in few functions\n\t}\n\n*/\n\nTF_CountLines(Text)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t StringReplace, Text, Text, `n, `n, UseErrorLevel\n\t Return ErrorLevel + 1\n\t}\n\nTF_ReadLines(Text, StartLine = 1, EndLine = 0, Trailing = 0)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t Else if (A_Index => EndLine)\n\t\t\tBreak\n\t\t}\n\t OW = 2 ; make sure we return variable not process file\n\t Return TF_ReturnOutPut(OW, OutPut, FileName, Trailing)\n\t}\n\nTF_ReplaceInLines(Text, StartLine = 1, EndLine = 0, SearchText = \"\", ReplaceText = \"\")\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t IfNotInString, Text, %SearchText%\n\t\tReturn Text ; SearchText not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t StringReplace, LoopField, A_LoopField, %SearchText%, %ReplaceText%, All\n\t\t\t OutPut .= LoopField \"`n\"\n\t\t\t}\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_Replace(Text, SearchText, ReplaceText=\"\")\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t IfNotInString, Text, %SearchText%\n\t\tReturn Text ; SearchText not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3\n\t Loop\n\t\t{\n\t\t StringReplace, Text, Text, %SearchText%, %ReplaceText%, All\n\t\t if (ErrorLevel = 0) ; No more replacements needed.\n\t\t\tbreak\n\t\t}\n\t Return TF_ReturnOutPut(OW, Text, FileName, 0)\n\t}\n\nTF_RegExReplaceInLines(Text, StartLine = 1, EndLine = 0, NeedleRegEx = \"\", Replacement = \"\")\n\t{\n\t options:=\"^[imsxADJUXPS]+\\)\" ; Hat tip to sinkfaze http://www.autohotkey.com/forum/viewtopic.php?t=60062\n\t If RegExMatch(searchText,options,o)\n\t\tsearchText := RegExReplace(searchText,options,(!InStr(o,\"m\") ? \"m$0\" : \"$0\"))\n\t Else searchText := \"m)\" . searchText\n\t TF_GetData(OW, Text, FileName)\n\t\tIf (RegExMatch(Text, SearchText) < 1)\n\t\t\tReturn Text ; SearchText not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3\n\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t LoopField := RegExReplace(A_LoopField, NeedleRegEx, Replacement)\n\t\t\t OutPut .= LoopField \"`n\"\n\t\t\t}\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_RegExReplace(Text, NeedleRegEx = \"\", Replacement = \"\")\n\t{\n\t options:=\"^[imsxADJUXPS]+\\)\" ; Hat tip to sinkfaze http://www.autohotkey.com/forum/viewtopic.php?t=60062\n\t if RegExMatch(searchText,options,o)\n\t\tsearchText := RegExReplace(searchText,options,(!InStr(o,\"m\") ? \"m$0\" : \"$0\"))\n\t else searchText := \"m)\" . searchText\n\t TF_GetData(OW, Text, FileName)\n\t\tIf (RegExMatch(Text, SearchText) < 1)\n\t\t\tReturn Text ; SearchText not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3\n\t Text := RegExReplace(Text, NeedleRegEx, Replacement)\n\t Return TF_ReturnOutPut(OW, Text, FileName, 0)\n\t}\n\nTF_RemoveLines(Text, StartLine = 1, EndLine = 0)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\tContinue\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_RemoveBlankLines(Text, StartLine = 1, EndLine = 0)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t If (RegExMatch(Text, \"[\\S]+?\\r?\\n?\") < 1)\n\t \tReturn Text ; No empty lines so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\tOutPut .= (RegExMatch(A_LoopField,\"[\\S]+?\\r?\\n?\")) ? A_LoopField \"`n\" :\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_RemoveDuplicateLines(Text, StartLine = 1, Endline = 0, Consecutive = 0, CaseSensitive = false)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t If (StartLine = \"\")\n\t \tStartLine = 1\n\t If (Endline = 0 OR Endline = \"\")\n\t\tEndLine := TF_Count(Text, \"`n\") + 1\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If (A_Index < StartLine)\n\t\t\tSection1 .= A_LoopField \"`n\"\n\t\t If A_Index between %StartLine% and %Endline%\n\t\t\t{\n\t\t\t If (Consecutive = 1)\n\t\t\t\t{\n\t\t\t\t If (A_LoopField <> PreviousLine) ; method one for consecutive duplicate lines\n\t\t\t\t\t Section2 .= A_LoopField \"`n\"\n\t\t\t\t PreviousLine:=A_LoopField\n\t\t\t\t}\n\t\t\t Else\n\t\t\t\t{\n\t\t\t\t If !(InStr(SearchForSection2,\"__bol__\" . A_LoopField . \"__eol__\",CaseSensitive)) ; not found\n\t\t\t\t \t{\n\t\t\t\t \t SearchForSection2 .= \"__bol__\" A_LoopField \"__eol__\" ; this makes it unique otherwise it could be a partial match\n\t\t\t\t\t Section2 .= A_LoopField \"`n\"\n\t\t\t\t \t}\n\t\t\t\t}\n\t\t\t}\n\t\t If (A_Index > EndLine)\n\t\t\tSection3 .= A_LoopField \"`n\"\n\t\t}\n\t Output .= Section1 Section2 Section3\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_InsertLine(Text, StartLine = 1, Endline = 0, InsertText = \"\")\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\tOutput .= InsertText \"`n\" A_LoopField \"`n\"\n\t\t Else\n\t\t\tOutput .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_ReplaceLine(Text, StartLine = 1, Endline = 0, ReplaceText = \"\")\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\tOutput .= ReplaceText \"`n\"\n\t\t Else\n\t\t\tOutput .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_InsertPrefix(Text, StartLine = 1, EndLine = 0, InsertText = \"\")\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\tOutPut .= InsertText A_LoopField \"`n\"\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_InsertSuffix(Text, StartLine = 1, EndLine = 0 , InsertText = \"\")\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\tOutPut .= A_LoopField InsertText \"`n\"\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_TrimLeft(Text, StartLine = 1, EndLine = 0, Count = 1)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t StringTrimLeft, StrOutPut, A_LoopField, %Count%\n\t\t\t OutPut .= StrOutPut \"`n\"\n\t\t\t}\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_TrimRight(Text, StartLine = 1, EndLine = 0, Count = 1)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t StringTrimRight, StrOutPut, A_LoopField, %Count%\n\t\t\t OutPut .= StrOutPut \"`n\"\n\t\t\t}\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_AlignLeft(Text, StartLine = 1, EndLine = 0, Columns = 80, Padding = 0)\n\t{\n\t Trim:=A_AutoTrim ; store trim settings\n\t AutoTrim, On ; make sure AutoTrim is on\n\t TF_GetData(OW, Text, FileName)\n\t If (Endline = 0 OR Endline = \"\")\n\t\tEndLine := TF_Count(Text, \"`n\") + 1\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t LoopField = %A_LoopField% ; Make use of AutoTrim, should be faster then a RegExReplace. Trims leading and trailing spaces!\n\t\t\t SpaceNum := Columns-StrLen(LoopField)-1\n\t\t\t If (SpaceNum > 0) and (Padding = 1) ; requires padding + keep padding\n\t\t\t\t{\n\t\t\t\t Left:=TF_SetWidth(LoopField,Columns, 0) ; align left\n\t\t\t\t OutPut .= Left \"`n\"\n\t\t\t\t}\n\t\t\t Else\n\t\t\t\tOutPut .= LoopField \"`n\"\n\t\t\t}\n\t\t Else\n\t\t \tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t AutoTrim, %Trim%\t; restore original Trim\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_AlignCenter(Text, StartLine = 1, EndLine = 0, Columns = 80, Padding = 0)\n\t{\n\t Trim:=A_AutoTrim ; store trim settings\n\t AutoTrim, On ; make sure AutoTrim is on\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t LoopField = %A_LoopField% ; Make use of AutoTrim, should be faster then a RegExReplace\n\t\t\t SpaceNum := (Columns-StrLen(LoopField)-1)/2\n\t\t\t If (Padding = 1) and (LoopField = \"\") ; skip empty lines, do not fill with spaces\n\t\t\t\t{\n\t\t\t\t OutPut .= \"`n\"\n\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t If (StrLen(LoopField) >= Columns)\n\t\t\t\t{\n\t\t\t\t OutPut .= LoopField \"`n\" ; add as is\n\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t Centered:=TF_SetWidth(LoopField,Columns, 1) ; align center using set width\n\t\t\t OutPut .= Centered \"`n\"\n\t\t\t}\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t AutoTrim, %Trim%\t; restore original Trim\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_AlignRight(Text, StartLine = 1, EndLine = 0, Columns = 80, Skip = 0)\n\t{\n\t Trim:=A_AutoTrim ; store trim settings\n\t AutoTrim, On ; make sure AutoTrim is on\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t LoopField = %A_LoopField% ; Make use of AutoTrim, should be faster then a RegExReplace\n\t\t\t If (Skip = 1) and (LoopField = \"\") ; skip empty lines, do not fill with spaces\n\t\t\t\t{\n\t\t\t\t OutPut .= \"`n\"\n\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t If (StrLen(LoopField) >= Columns)\n\t\t\t\t{\n\t\t\t\t OutPut .= LoopField \"`n\" ; add as is\n\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t Right:=TF_SetWidth(LoopField,Columns, 2) ; align right using set width\n\t\t\t OutPut .= Right \"`n\"\n\t\t\t}\n\t\t Else\n\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t}\n\t AutoTrim, %Trim%\t; restore original Trim\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\n; Based on: CONCATenate text files, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip\nTF_ConCat(FirstTextFile, SecondTextFile, OutputFile = \"\", Blanks = 0, FirstPadMargin = 0, SecondPadMargin = 0)\n\t{\n\t If (Blanks > 0)\n\t\tLoop, %Blanks%\n\t\t\tInsertBlanks .= A_Space\n\t If (FirstPadMargin > 0)\n\t\tLoop, %FirstPadMargin%\n\t\t\tPaddingFile1 .= A_Space\n\t If (SecondPadMargin > 0)\n\t\tLoop, %SecondPadMargin%\n\t\t\tPaddingFile2 .= A_Space\n\t Text:=FirstTextFile\n\t TF_GetData(OW, Text, FileName)\n\t StringSplit, Str1Lines, Text, `n, `r\n\t Text:=SecondTextFile\n\t TF_GetData(OW, Text, FileName)\n\t StringSplit, Str2Lines, Text, `n, `r\n\t Text= ; clear mem\n\n\t ; first we need to determine the file with the most lines for our loop\n\t If (Str1Lines0 > Str2Lines0)\n\t\tMaxLoop:=Str1Lines0\n\t Else\n\t\tMaxLoop:=Str2Lines0\n\t Loop, %MaxLoop%\n\t\t{\n\t\t Section1:=Str1Lines%A_Index%\n\t\t Section2:=Str2Lines%A_Index%\n\t\t OutPut .= Section1 PaddingFile1 InsertBlanks Section2 PaddingFile2 \"`n\"\n\t\t Section1= ; otherwise it will remember the last line from the shortest file or var\n\t\t Section2=\n\t\t}\n\t OW=1 ; it is probably 0 so in that case it would create _copy, so set it to 1\n\t If (OutPutFile = \"\") ; if OutPutFile is empty return as variable\n\t\tOW=2\n\t Return TF_ReturnOutPut(OW, OutPut, OutputFile, 1, 1)\n\t}\n\nTF_LineNumber(Text, Leading = 0, Restart = 0, Char = 0) ; HT ribbet.1\n\t{\n\t global t\n\t TF_GetData(OW, Text, FileName)\n\t Lines:=TF_Count(Text, \"`n\") + 1\n\t Padding:=StrLen(Lines)\n\t If (Leading = 0) and (Char = 0)\n\t\tChar := A_Space\n\t Loop, %Padding%\n\t\tPadLines .= Char\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If Restart = 0\n\t\t\tMaxNo = %A_Index%\n\t\t Else\n\t\t\t{\n\t\t\t MaxNo++\n\t\t\t If MaxNo > %Restart%\n\t\t\t\tMaxNo = 1\n\t\t\t}\n\t\t LineNumber:= MaxNo\n\t\t If (Leading = 1)\n\t\t\t{\n\t\t\t LineNumber := Padlines LineNumber ; add padding\n\t\t\t StringRight, LineNumber, LineNumber, StrLen(Lines) ; remove excess padding\n\t\t\t}\n\t\t If (Leading = 0)\n\t\t\t{\n\t\t\t LineNumber := LineNumber Padlines ; add padding\n\t\t\t StringLeft, LineNumber, LineNumber, StrLen(Lines) ; remove excess padding\n\t\t\t}\n\t\t OutPut .= LineNumber A_Space A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\n; skip = 1, skip shorter lines (e.g. lines shorter startcolumn position)\n; modified in TF 3.4, fixed in 3.5\nTF_ColGet(Text, StartLine = 1, EndLine = 0, StartColumn = 1, EndColumn = 1, Skip = 0)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t If (StartColumn < 0)\n\t\t{\n\t\t StartColumn++\n\t\t Loop, Parse, Text, `n, `r ; parsing file/var\n\t\t\t{\n\t\t\t If A_Index in %TF_MatchList%\n\t\t\t\t{\n\t\t\t\t output .= SubStr(A_LoopField,StartColumn) \"`n\"\n\t\t\t\t}\n\t\t\t else\n\t\t\t\t output .= A_LoopField \"`n\"\n\t\t\t}\n\t\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t\t}\n\t if RegExMatch(StartColumn, \",|\\+|-\")\n\t\t{\n\t\t StartColumn:=_MakeMatchList(Text, StartColumn, 1, 1)\n\t\t Loop, Parse, Text, `n, `r ; parsing file/var\n\t\t\t{\n\t\t\t If A_Index in %TF_MatchList%\n\t\t\t\t{\n\t\t\t\t loop, parse, A_LoopField ; parsing LINE char by char\n\t\t\t\t\t{\n\t\t\t\t\t If A_Index in %StartColumn% ; if col in index get char\n\t\t\t\t\t\toutput .= A_LoopField\n\t\t\t\t\t}\n\t\t\t\t output .= \"`n\"\n\t\t\t\t}\n\t\t\t else\n\t\t\t\t output .= A_LoopField \"`n\"\n\t\t\t}\n\t\t output .= A_LoopField \"`n\"\n\t\t}\n\t else\n\t\t{\n\t\t EndColumn:=(EndColumn+1)-StartColumn\n\t\t Loop, Parse, Text, `n, `r\n\t\t\t{\n\t\t\t If A_Index in %TF_MatchList%\n\t\t\t\t{\n\t\t\t\t StringMid, Section, A_LoopField, StartColumn, EndColumn\n\t\t\t\t If (Skip = 1) and (StrLen(A_LoopField) < StartColumn)\n\t\t\t\t\tContinue\n\t\t\t\t OutPut .= Section \"`n\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\n; Based on: COLPUT.EXE & CUT.EXE, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip\n; modified in TF 3.4\nTF_ColPut(Text, Startline = 1, EndLine = 0, StartColumn = 1, InsertText = \"\", Skip = 0)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t If RegExMatch(StartColumn, \",|\\+\")\n\t\t{\n\t\t StartColumn:=_MakeMatchList(Text, StartColumn, 0, 1)\n\t\t Loop, Parse, Text, `n, `r ; parsing file/var\n\t\t\t{\n\t\t\t If A_Index in %TF_MatchList%\n\t\t\t\t{\n\t\t\t\t loop, parse, A_LoopField ; parsing LINE char by char\n\t\t\t\t\t{\n\t\t\t\t\t If A_Index in %StartColumn% ; if col in index insert text\n\t\t\t\t\t\toutput .= InsertText A_LoopField\n\t\t\t\t\t Else\n\t\t\t\t\t\toutput .= A_LoopField\n\t\t\t\t\t}\n\t\t\t\t output .= \"`n\"\n\t\t\t\t}\n\t\t\t else\n\t\t\t\t output .= A_LoopField \"`n\"\n\t\t\t}\n\t\t output .= A_LoopField \"`n\"\n\t\t}\n\t else\n\t\t{\n\t\t StartColumn--\n\t\t Loop, Parse, Text, `n, `r\n\t\t\t{\n\t\t\t If A_Index in %TF_MatchList%\n\t\t\t\t{\n\t\t\t\t If (StartColumn > 0)\n\t\t\t\t\t{\n\t\t\t\t\t StringLeft, Section1, A_LoopField, StartColumn\n\t\t\t\t\t StringMid, Section2, A_LoopField, StartColumn+1\n\t\t\t\t\t If (Skip = 1) and (StrLen(A_LoopField) < StartColumn)\n\t\t\t\t\t\tOutPut .= Section1 Section2 \"`n\"\n\t\t\t\t\t}\n\t\t\t\t Else\n\t\t\t\t\t{\n\t\t\t\t\t Section1:=SubStr(A_LoopField, 1, StrLen(A_LoopField) + StartColumn + 1)\n\t\t\t\t\t Section2:=SubStr(A_LoopField, StrLen(A_LoopField) + StartColumn + 2)\n\t\t\t\t\t If (Skip = 1) and (A_LoopField = \"\")\n\t\t\t\t\t\tOutPut .= Section1 Section2 \"`n\"\n\t\t\t\t\t}\n\t\t\t\t OutPut .= Section1 InsertText Section2 \"`n\"\n\t\t\t\t}\n\t\t\t Else\n\t\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t\t}\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\n; modified TF 3.4\nTF_ColCut(Text, StartLine = 1, EndLine = 0, StartColumn = 1, EndColumn = 1)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t If RegExMatch(StartColumn, \",|\\+|-\")\n\t\t{\n\t\t StartColumn:=_MakeMatchList(Text, StartColumn, EndColumn, 1)\n\t\t Loop, Parse, Text, `n, `r ; parsing file/var\n\t\t\t{\n\t\t\t If A_Index in %TF_MatchList%\n\t\t\t\t{\n\t\t\t\t loop, parse, A_LoopField ; parsing LINE char by char\n\t\t\t\t\t{\n\t\t\t\t\t If A_Index not in %StartColumn% ; if col not in index get char\n\t\t\t\t\t\toutput .= A_LoopField\n\t\t\t\t\t}\n\t\t\t\t output .= \"`n\"\n\t\t\t\t}\n\t\t\t else\n\t\t\t\t output .= A_LoopField \"`n\"\n\t\t\t}\n\t\t output .= A_LoopField \"`n\"\n\t\t}\n\t else\n\t\t{\n\t\t StartColumn--\n\t\t EndColumn++\n\t\t Loop, Parse, Text, `n, `r\n\t\t\t{\n\t\t\t If A_Index in %TF_MatchList%\n\t\t\t\t{\n\t\t\t\t StringLeft, Section1, A_LoopField, StartColumn\n\t\t\t\t StringMid, Section2, A_LoopField, EndColumn\n\t\t\t\t OutPut .= Section1 Section2 \"`n\"\n\t\t\t\t}\n\t\t\t Else\n\t\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t\t}\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_ReverseLines(Text, StartLine = 1, EndLine = 0)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t StringSplit, Line, Text, `n, `r ; line0 is number of lines\n\t If (EndLine = 0 OR EndLine = \"\")\n\t\tEndLine:=Line0\n\t If (EndLine > Line0)\n\t\tEndLine:=Line0\n\t CountDown:=EndLine+1\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If (A_Index < StartLine)\n\t\t\tOutput1 .= A_LoopField \"`n\" ; section1\n\t\t If A_Index between %StartLine% and %Endline%\n\t\t\t{\n\t\t\t CountDown--\n\t\t\t Output2 .= Line%CountDown% \"`n\" section2\n\t\t\t}\n\t\t If (A_Index > EndLine)\n\t\t\t Output3 .= A_LoopField \"`n\"\n\t\t}\n\t OutPut.= Output1 Output2 Output3\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\n;TF_SplitFileByLines\n;example:\n;TF_SplitFileByLines(\"TestFile.txt\", \"4\", \"sfile_\", \"txt\", \"1\") ; split file every 3 lines\n; InFile = 0 skip line e.g. do not include the actual line in any of the output files\n; InFile = 1 include line IN current file\n; InFile = 2 include line IN next file\nTF_SplitFileByLines(Text, SplitAt, Prefix = \"file\", Extension = \"txt\", InFile = 1)\n\t{\n\t LineCounter=1\n\t FileCounter=1\n\t Where:=SplitAt\n\t Method=1\n\t ; 1 = default, splitat every X lines,\n\t ; 2 = splitat: - rotating if applicable\n\t ; 3 = splitat: specific lines comma separated\n\t TF_GetData(OW, Text, FileName)\n\n\t IfInString, SplitAt, `- ; method 2\n\t\t{\n\t\t StringSplit, Split, SplitAt, `-\n\t\t Part=1\n\t\t Where:=Split%Part%\n\t\t Method=2\n\t\t}\n\t IfInString, SplitAt, `, ; method 3\n\t\t{\n\t\t StringSplit, Split, SplitAt, `,\n\t\t Part=1\n\t\t Where:=Split%Part%\n\t\t Method=3\n\t\t}\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t OutPut .= A_LoopField \"`n\"\n\t\t If (LineCounter = Where)\n\t\t\t{\n\t\t\t If (InFile = 0)\n\t\t\t\t{\n\t\t\t\t StringReplace, CheckOutput, PreviousOutput, `n, , All\n\t\t\t\t StringReplace, CheckOutput, CheckOutput, `r, , All\n\t\t\t\t If (CheckOutput <> \"\") and (OW <> 2) ; skip empty files\n\t\t\t\t\tTF_ReturnOutPut(1, PreviousOutput, Prefix FileCounter \".\" Extension, 0, 1)\n\t\t\t\t If (CheckOutput <> \"\") and (OW = 2) ; skip empty files\n\t\t\t\t\t TF_SetGlobal(Prefix FileCounter,PreviousOutput)\n\t\t\t\t Output:=\n\t\t\t\t}\n\t\t\t If (InFile = 1)\n\t\t\t\t{\n\t\t\t\t StringReplace, CheckOutput, Output, `n, , All\n\t\t\t\t StringReplace, CheckOutput, CheckOutput, `r, , All\n\t\t\t\t If (CheckOutput <> \"\") and (OW <> 2) ; skip empty files\n\t\t\t\t \t TF_ReturnOutPut(1, Output, Prefix FileCounter \".\" Extension, 0, 1)\n\t\t\t\t If (CheckOutput <> \"\") and (OW = 2) ; skip empty files\n\t\t\t\t \t TF_SetGlobal(Prefix FileCounter,Output)\n\t\t\t\t Output:=\n\t\t\t\t}\n\t\t\t If (InFile = 2)\n\t\t\t\t{\n\t\t\t\t OutPut := PreviousOutput\n\t\t\t\t StringReplace, CheckOutput, Output, `n, , All\n\t\t\t\t StringReplace, CheckOutput, CheckOutput, `r, , All\n\t\t\t\t If (CheckOutput <> \"\") and (OW <> 2) ; skip empty files\n\t\t\t\t\t TF_ReturnOutPut(1, Output, Prefix FileCounter \".\" Extension, 0, 1)\n\t\t\t\t If (CheckOutput <> \"\") and (OW = 2) ; output to array\n\t\t\t\t \t TF_SetGlobal(Prefix FileCounter,Output)\n\t\t\t\t OutPut := A_LoopField \"`n\"\n\t\t\t\t}\n\t\t\t If (Method <> 3)\n\t\t\t\t LineCounter=0 ; reset\n\t\t\t FileCounter++ ; next file\n\t\t\t Part++\n\t\t\t If (Method = 2) ; 2 = splitat: - rotating if applicable\n\t\t\t \t{\n\t\t\t If (Part > Split0)\n\t\t\t\t\t{\n\t\t\t\t\t Part=1\n\t\t\t\t\t}\n\t\t\t\t Where:=Split%Part%\n\t\t\t\t}\n\t\t\t If (Method = 3) ; 3 = splitat: specific lines comma separated\n\t\t\t\t{\n\t\t\t\t If (Part > Split0)\n\t\t\t\t\tWhere:=Split%Split0%\n\t\t\t\t Else\n\t\t\t\t\tWhere:=Split%Part%\n\t\t\t\t}\n\t\t\t}\n\t\t LineCounter++\n\t\t PreviousOutput:=Output\n\t\t PreviousLine:=A_LoopField\n\t\t}\n\t StringReplace, CheckOutput, Output, `n, , All\n\t StringReplace, CheckOutput, CheckOutput, `r, , All\n\t If (CheckOutPut <> \"\") and (OW <> 2) ; skip empty files\n\t\tTF_ReturnOutPut(1, Output, Prefix FileCounter \".\" Extension, 0, 1)\n\t If (CheckOutput <> \"\") and (OW = 2) ; output to array\n\t\t{\n\t\t TF_SetGlobal(Prefix FileCounter,Output)\n\t\t TF_SetGlobal(Prefix . \"0\" , FileCounter)\n\t\t}\n\t}\n\n; TF_SplitFileByText(\"TestFile.txt\", \"button\", \"sfile_\", \"txt\") ; split file at every line with button in it, can be regexp\n; InFile = 0 skip line e.g. do not include the actual line in any of the output files\n; InFile = 1 include line IN current file\n; InFile = 2 include line IN next file\nTF_SplitFileByText(Text, SplitAt, Prefix = \"file\", Extension = \"txt\", InFile = 1)\n\t{\n\t LineCounter=1\n\t FileCounter=1\n\t TF_GetData(OW, Text, FileName)\n\t SplitPath, TextFile,, Dir\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t OutPut .= A_LoopField \"`n\"\n\t\t FoundPos:=RegExMatch(A_LoopField, SplitAt)\n\t\t If (FoundPos > 0)\n\t\t\t{\n\t\t\t If (InFile = 0)\n\t\t\t\t{\n\t\t\t\t StringReplace, CheckOutput, PreviousOutput, `n, , All\n\t\t\t\t StringReplace, CheckOutput, CheckOutput, `r, , All\n\t\t\t\t If (CheckOutput <> \"\") and (OW <> 2) ; skip empty files\n\t\t\t\t\tTF_ReturnOutPut(1, PreviousOutput, Prefix FileCounter \".\" Extension, 0, 1)\n\t\t\t\t If (CheckOutput <> \"\") and (OW = 2) ; output to array\n\t\t\t\t\tTF_SetGlobal(Prefix FileCounter,PreviousOutput)\n\t\t\t\t Output:=\n\t\t\t\t}\n\t\t\t If (InFile = 1)\n\t\t\t\t{\n\t\t\t\t StringReplace, CheckOutput, Output, `n, , All\n\t\t\t\t StringReplace, CheckOutput, CheckOutput, `r, , All\n\t\t\t\t If (CheckOutput <> \"\") and (OW <> 2) ; skip empty files\n\t\t\t\t\tTF_ReturnOutPut(1, Output, Prefix FileCounter \".\" Extension, 0, 1)\n\t\t\t\t If (CheckOutput <> \"\") and (OW = 2) ; output to array\n\t\t\t\t\tTF_SetGlobal(Prefix FileCounter,Output)\n\t\t\t\t Output:=\n\t\t\t\t}\n\t\t\t If (InFile = 2)\n\t\t\t\t{\n\t\t\t\t OutPut := PreviousOutput\n\t\t\t\t StringReplace, CheckOutput, Output, `n, , All\n\t\t\t\t StringReplace, CheckOutput, CheckOutput, `r, , All\n\t\t\t\t If (CheckOutput <> \"\") and (OW <> 2) ; skip empty files\n\t\t\t\t\tTF_ReturnOutPut(1, Output, Prefix FileCounter \".\" Extension, 0, 1)\n\t\t\t\t If (CheckOutput <> \"\") and (OW = 2) ; output to array\n\t\t\t\t\tTF_SetGlobal(Prefix FileCounter,Output)\n\t\t\t\t OutPut := A_LoopField \"`n\"\n\t\t\t\t}\n\t\t\t LineCounter=0 ; reset\n\t\t\t FileCounter++ ; next file\n\t\t\t}\n\t\t LineCounter++\n\t\t PreviousOutput:=Output\n\t\t PreviousLine:=A_LoopField\n\t\t}\n\t StringReplace, CheckOutput, Output, `n, , All\n\t StringReplace, CheckOutput, CheckOutput, `r, , All\n\t If (CheckOutPut <> \"\") and (OW <> 2) ; skip empty files\n\t\tTF_ReturnOutPut(1, Output, Prefix FileCounter \".\" Extension, 0, 1)\n\t If (CheckOutput <> \"\") and (OW = 2) ; output to array\n\t\t{\n\t\t TF_SetGlobal(Prefix FileCounter,Output)\n\t\t TF_SetGlobal(Prefix . \"0\" , FileCounter)\n\t\t}\n\t}\n\nTF_Find(Text, StartLine = 1, EndLine = 0, SearchText = \"\", ReturnFirst = 1, ReturnText = 0)\n\t{\n\t options:=\"^[imsxADJUXPS]+\\)\"\n\t if RegExMatch(searchText,options,o)\n\t\tsearchText:=RegExReplace(searchText,options,(!InStr(o,\"m\") ? \"m$0(*ANYCRLF)\" : \"$0\"))\n\t else searchText:=\"m)(*ANYCRLF)\" searchText\n\t options:=\"^[imsxADJUXPS]+\\)\" ; Hat tip to sinkfaze, see http://www.autohotkey.com/forum/viewtopic.php?t=60062\n\t if RegExMatch(searchText,options,o)\n\t\tsearchText := RegExReplace(searchText,options,(!InStr(o,\"m\") ? \"m$0\" : \"$0\"))\n\t else searchText := \"m)\" . searchText\n\n\t TF_GetData(OW, Text, FileName)\n\t If (RegExMatch(Text, SearchText) < 1)\n\t\tReturn \"0\" ; SearchText not in file or error, so do nothing\n\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t If (RegExMatch(A_LoopField, SearchText) > 0)\n\t\t\t\t{\n\t\t\t\t If (ReturnText = 0)\n\t\t\t\t\tLines .= A_Index \",\" ; line number\n\t\t\t\t Else If (ReturnText = 1)\n\t\t\t\t\tLines .= A_LoopField \"`n\" ; text of line\n\t\t\t\t Else If (ReturnText = 2)\n\t\t\t\t\tLines .= A_Index \": \" A_LoopField \"`n\" ; add line number\n\t\t\t\t If (ReturnFirst = 1) ; only return first occurrence\n\t\t\t\t\tBreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t If (Lines <> \"\")\n\t\tStringTrimRight, Lines, Lines, 1 ; trim trailing , or `n\n\t Else\n\t\tLines = 0 ; make sure we return 0\n\t Return Lines\n\t}\n\nTF_Prepend(File1, File2)\n\t{\nFileList=\n(\n%File1%\n%File2%\n)\nTF_Merge(FileList,\"`n\", \"!\" . File2)\nReturn\n\t}\n\nTF_Append(File1, File2)\n\t{\nFileList=\n(\n%File2%\n%File1%\n)\nTF_Merge(FileList,\"`n\", \"!\" . File2)\nReturn\n\t}\n\n; For TF_Merge You will need to create a Filelist variable, one file per line,\n; to pass on to the function:\n; FileList=\n; (\n; c:\\file1.txt\n; c:\\file2.txt\n; )\n; use Loop (files & folders) to create one quickly if you want to merge all TXT files for example\n;\n; Loop, c:\\*.txt\n;   FileList .= A_LoopFileFullPath \"`n\"\n;\n; By default, a new line is used as a separator between two text files\n; !merged.txt deletes target file before starting to merge files\nTF_Merge(FileList, Separator = \"`n\", FileName = \"merged.txt\")\n\t{\n\t OW=0\n\t Loop, Parse, FileList, `n, `r\n\t\t{\n\t\t Append2File= ; Just make sure it is empty\n\t\t IfExist, %A_LoopField%\n\t\t\t{\n\t\t\t FileRead, Append2File, %A_LoopField%\n\t\t\t If not ErrorLevel ; Successfully loaded\n\t\t\t\tOutput .= Append2File Separator\n\t\t\t}\n\t\t}\n\n\t If (SubStr(FileName,1,1)=\"!\") ; check if we want to delete the target file before we start\n\t\t{\n\t\t FileName:=SubStr(FileName,2)\n\t\t OW=1\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName, 0, 1)\n\t}\n\nTF_Wrap(Text, Columns = 80, AllowBreak = 0, StartLine = 1, EndLine = 0)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t If (AllowBreak = 1)\n\t\tBreak=\n\t Else\n\t\tBreak=[ \\r?\\n]\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t If (StrLen(A_LoopField) > Columns)\n\t\t\t\t{\n\t\t\t\t LoopField := A_LoopField \" \" ; just seems to work better by adding a space\n\t\t\t\t OutPut .= RegExReplace(LoopField, \"(.{1,\" . Columns . \"})\" . Break , \"$1`n\")\n\t\t\t\t}\n\t\t\t Else\n\t\t\t\tOutPut .= A_LoopField \"`n\"\n\t\t\t}\n\t\t Else\n\t\t\t OutPut .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\nTF_WhiteSpace(Text, RemoveLeading = 1, RemoveTrailing = 1, StartLine = 1, EndLine = 0) {\n\t TF_GetData(OW, Text, FileName)\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList\n\t Trim:=A_AutoTrim ; store trim settings\n\t AutoTrim, On ; make sure AutoTrim is on\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t If (RemoveLeading = 1) AND (RemoveTrailing = 1)\n\t\t\t\t{\n\t\t\t\t LoopField = %A_LoopField%\n\t\t\t\t Output .= LoopField \"`n\"\n\t\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t If (RemoveLeading = 1) AND (RemoveTrailing = 0)\n\t\t\t\t{\n\t\t\t\t LoopField := A_LoopField . \".\"\n\t\t\t\t LoopField = %LoopField%\n\t\t\t\t StringTrimRight, LoopField, LoopField, 1\n\t\t\t\t Output .= LoopField \"`n\"\n\t\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t If (RemoveLeading = 0) AND (RemoveTrailing = 1)\n\t\t\t\t{\n\t\t\t\t LoopField := \".\" A_LoopField\n\t\t\t\t LoopField = %LoopField%\n\t\t\t\t StringTrimLeft, LoopField, LoopField, 1\n\t\t\t\t Output .= LoopField \"`n\"\n\t\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t If (RemoveLeading = 0) AND (RemoveTrailing = 0)\n\t\t\t\t{\n\t\t\t\t Output .= A_LoopField \"`n\"\n\t\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t}\n\t\t Else\n\t\t\tOutput .= A_LoopField \"`n\"\n\t\t}\n\tAutoTrim, %Trim%\t; restore original Trim\n\tReturn TF_ReturnOutPut(OW, OutPut, FileName)\n}\n\n; Delete lines from file1 in file2 (using StringReplace)\n; Partialmatch = 2 added in 3.4\nTF_Substract(File1, File2, PartialMatch = 0) {\n\tText:=File1\n\tTF_GetData(OW, Text, FileName)\n\tStr1:=Text\n\tText:=File2\n\tTF_GetData(OW, Text, FileName)\n\t\tOutPut:=Text\n\tIf (OW = 2)\n\t\tFile1= ; free mem in case of var/text\n\tOutPut .= \"`n\" ; just to make sure the StringReplace will work\n\n\tIf (PartialMatch = 2)\n\t\t{\n\t\t Loop, Parse, Str1, `n, `r\n\t\t\t{\n\t\t\t IfInString, Output, %A_LoopField%\n\t\t\t\t{\n\t\t\t\t Output:= RegExReplace(Output, \"im)^.*\" . A_LoopField . \".*\\r?\\n?\", replace)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tElse If (PartialMatch = 1) ; allow paRTIal match\n\t\t{\n\t\t Loop, Parse, Str1, `n, `r\n\t\t\tStringReplace, Output, Output, %A_LoopField%, , All ; remove lines from file1 in file2\n\t\t}\n\tElse If (PartialMatch = 0)\n\t\t{\n\t\t search:=\"m)^(.*)$\"\n\t\t replace=__bol__$1__eol__\n\t\t Output:=RegExReplace(Output, search, replace)\n\t\t StringReplace, Output, Output, `n__eol__,__eol__ , All ; strange fix but seems to be needed.\n\t\t Loop, Parse, Str1, `n, `r\n\t\t\tStringReplace, Output, Output, __bol__%A_LoopField%__eol__, , All ; remove lines from file1 in file2\n\t\t}\n\tIf (PartialMatch = 0)\n\t\t{\n\t\t StringReplace, Output, Output, __bol__, , All\n\t\t StringReplace, Output, Output, __eol__, , All\n\t\t}\n\n\t; Remove all blank lines from the text in a variable:\n\tLoop\n\t\t{\n\t\t StringReplace, Output, Output, `r`n`r`n, `r`n, UseErrorLevel\n\t\t if (ErrorLevel = 0) or (ErrorLevel = 1) ; No more replacements needed.\n\t\t\tbreak\n\t\t}\n\tReturn TF_ReturnOutPut(OW, OutPut, FileName, 0)\n}\n\n; Similar to \"BK Replace EM\" RangeReplace\nTF_RangeReplace(Text, SearchTextBegin, SearchTextEnd, ReplaceText = \"\", CaseSensitive = \"False\", KeepBegin = 0, KeepEnd = 0)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t IfNotInString, Text, %SearchText%\n\t\tReturn Text ; SearchTextBegin not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3\n\t Start = 0\n\t End = 0\n\t If (KeepBegin = 1)\n\t\t KeepBegin:=SearchTextBegin\n\t Else\n\t\t KeepBegin=\n\t If (KeepEnd = 1)\n\t\t KeepEnd:= SearchTextEnd\n\t Else\n\t\t KeepEnd=\n\t If (SearchTextBegin = \"\")\n\t\t Start=1\n\t If (SearchTextEnd = \"\")\n\t\t End=2\n\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If (End = 1) ; end has been found already, replacement made simply continue to add all lines\n\t\t\t{\n\t\t\t Output .= A_LoopField \"`n\"\n\t\t\t\t Continue\n\t\t\t}\n\t\t If (Start = 0) ; start hasn't been found\n\t\t\t{\n\t\t\t If (InStr(A_LoopField,SearchTextBegin,CaseSensitive)) ; start has been found\n\t\t\t\t{\n\t\t\t\t Start = 1\n\t\t\t\t KeepSection := SubStr(A_LoopField, 1, InStr(A_LoopField, SearchTextBegin)-1)\n\t\t\t\t EndSection := SubStr(A_LoopField, InStr(A_LoopField, SearchTextBegin)-1)\n\t\t\t\t ; check if SearchEndText is in second part of line\n\t\t\t\t If (InStr(EndSection,SearchTextEnd,CaseSensitive)) ; end found\n\t\t\t\t\t{\n\t\t\t\t\t EndSection := ReplaceText KeepEnd SubStr(EndSection, InStr(EndSection, SearchTextEnd) + StrLen(SearchTextEnd) ) \"`n\"\n\t\t\t\t\t If (End <> 2)\n\t\t\t\t\t\tEnd=1\n\t\t\t\t\t If (End = 2)\n\t\t\t\t\t \tEndSection=\n\t\t\t\t\t}\n\t\t\t\t Else\n\t\t\t\t\tEndSection=\n\t\t\t\t Output .= KeepSection KeepBegin EndSection\n\t\t\t\t Continue\n\t\t\t\t}\n\t\t\t Else\n\t\t\t\tOutput .= A_LoopField \"`n\" ; if not found yet simply add\n\t\t\t\t}\n\t\t If (Start = 1) and (End <> 2) ; start has been found, now look for end if end isn't an empty string\n\t\t\t{\n\t\t\t If (InStr(A_LoopField,SearchTextEnd,CaseSensitive)) ; end found\n\t\t\t\t{\n\t\t\t\t End = 1\n\t\t\t\t Output .= ReplaceText KeepEnd SubStr(A_LoopField, InStr(A_LoopField, SearchTextEnd) + StrLen(SearchTextEnd) ) \"`n\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t If (End = 2)\n\t\tOutput .= ReplaceText\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\n; Create file of X lines and Y columns, fill with space or other character(s)\nTF_MakeFile(Text, Lines = 1, Columns = 1, Fill = \" \")\n\t{\n\t OW=1\n\t If (Text = \"\") ; if OutPutFile is empty return as variable\n\t\tOW=2\n\t Loop, % Columns\n\t\tCols .= Fill\n\t Loop, % Lines\n\t\tOutput .= Cols \"`n\"\n\t Return TF_ReturnOutPut(OW, OutPut, Text, 1, 1)\n\t}\n\n; Convert tabs to spaces, shorthand for TF_ReplaceInLines\nTF_Tab2Spaces(Text, TabStop = 4, StartLine = 1, EndLine =0)\n\t{\n\t Loop, % TabStop\n\t\tReplace .= A_Space\n\t Return TF_ReplaceInLines(Text, StartLine, EndLine, A_Tab, Replace)\n\t}\n\n; Convert spaces to tabs, shorthand for TF_ReplaceInLines\nTF_Spaces2Tab(Text, TabStop = 4, StartLine = 1, EndLine =0)\n\t{\n\t Loop, % TabStop\n\t\tReplace .= A_Space\n\t Return TF_ReplaceInLines(Text, StartLine, EndLine, Replace, A_Tab)\n\t}\n\n; Sort (section of) a text file\nTF_Sort(Text, SortOptions = \"\", StartLine = 1, EndLine = 0) ; use the SORT options http://www.autohotkey.com/docs/commands/Sort.htm\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t If StartLine contains -,+,`, ; no sections, incremental or multiple line input\n\t\tReturn\n\t If (StartLine = 1) and (Endline = 0) ; process entire file\n\t\t{\n\t\t Output:=Text\n\t\t Sort, Output, %SortOptions%\n\t\t}\n\t Else\n\t\t{\n\t\t Output := TF_ReadLines(Text, 1, StartLine-1) ; get first section\n\t\t ToSort := TF_ReadLines(Text, StartLine, EndLine) ; get section to sort\n\t\t Sort, ToSort, %SortOptions%\n\t\t OutPut .= ToSort\n\t\t OutPut .= TF_ReadLines(Text, EndLine+1) ; append last section\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName, 0) ; https://github.com/hi5/TF/issues/11\n\t}\n\nTF_Tail(Text, Lines = 1, RemoveTrailing = 0, ReturnEmpty = 1)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t Neg = 0\n\t If (Lines < 0)\n\t\t{\n\t\t Neg=1\n\t\t Lines:= Lines * -1\n\t\t}\n\t If (ReturnEmpty = 0) ; remove blank lines first so we can't return any blank lines anyway\n\t\t{\n\t\t Loop, Parse, Text, `n, `r\n\t\t\tOutPut .= (RegExMatch(A_LoopField,\"[\\S]+?\\r?\\n?\")) ? A_LoopField \"`n\" :\n\t\t StringTrimRight, OutPut, OutPut, 1 ; remove trailing `n added by loop above\n\t\t Text:=OutPut\n\t\t OutPut=\n\t}\n\t If (Neg = 1) ; get only one line!\n\t\t{\n\t\t Lines++\n\t\t Output:=Text\n\t\t StringGetPos, Pos, Output, `n, R%Lines% ; These next two Lines by Tuncay see\n\t\t StringTrimLeft, Output, Output, % ++Pos ; http://www.autoHotkey.com/forum/viewtopic.php?p=262375#262375\n\t\t StringGetPos, Pos, Output, `n\n\t\t StringLeft, Output, Output, % Pos\n\t\t Output .= \"`n\"\n\t\t}\n\t Else\n\t\t{\n\t\t Output:=Text\n\t\t StringGetPos, Pos, Output, `n, R%Lines% ; These next two Lines by Tuncay see\n\t\t StringTrimLeft, Output, Output, % ++Pos ; http://www.autoHotkey.com/forum/viewtopic.php?p=262375#262375\n\t\t Output .= \"`n\"\n\t\t}\n\t OW = 2 ; make sure we return variable not process file\n\t Return TF_ReturnOutPut(OW, OutPut, FileName, RemoveTrailing)\n\t}\n\nTF_Count(String, Char)\n\t{\n\tStringReplace, String, String, %Char%,, UseErrorLevel\n\tReturn ErrorLevel\n\t}\n\nTF_Save(Text, FileName, OverWrite = 1) { ; HugoV write file\n\tReturn TF_ReturnOutPut(OverWrite, Text, FileName, 0, 1)\n\t}\n\nTF(TextFile, CreateGlobalVar = \"T\") { ; read contents of file in output and %output% as global var ...  http://www.autohotkey.com/forum/viewtopic.php?p=313120#313120\n\t global\n\t FileRead, %CreateGlobalVar%, %TextFile%\n\t Return, (%CreateGlobalVar%)\n\t}\n\n; TF_Join\n; SmartJoin: Detect if CHAR(s) is/are already present at the end of the line before joining the next, this to prevent unnecessary double spaces for example.\n; Char: character(s) to use between new lines, defaults to a space. To use nothing use \"\"\nTF_Join(Text, StartLine = 1, EndLine = 0, SmartJoin = 0, Char = 0)\n\t{\n\t If ( (InStr(StartLine,\",\") > 0) AND (InStr(StartLine,\"-\") = 0) ) OR (InStr(StartLine,\"+\") > 0)\n\t\tReturn Text ; can't do multiplelines, only multiple sections of lines e.g. \"1,5\" bad \"1-5,15-10\" good, \"2+2\" also bad\n\t TF_GetData(OW, Text, FileName)\n\t If (InStr(Text,\"`n\") = 0)\n\t\tReturn Text ; there are no lines to join so just return Text\n\t If (InStr(StartLine,\"-\") > 0)\t; OK, we need some counter-intuitive string mashing to substract ONE from the \"endline\" parameter\n\t\t{\n\t\t Loop, Parse, StartLine, CSV\n\t\t\t{\n\t\t\t StringSplit, part, A_LoopField, -\n\t\t\t NewStartLine .= part1 \"-\" (part2-1) \",\"\n\t\t\t}\n\t\t StringTrimRight, StartLine, NewStartLine, 1\n\t\t}\n\t If (Endline > 0)\n\t\tEndline--\n\t TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc)\n\t If (Char = 0)\n\t\tChar:=A_Space\n\t Char_Org:=Char\n\t GetRightLen:=StrLen(Char)-1\n\t Loop, Parse, Text, `n, `r\n\t\t{\n\t\t If A_Index in %TF_MatchList%\n\t\t\t{\n\t\t\t If (SmartJoin = 1)\n\t\t\t\t{\n\t\t\t\t GetRightText:=SubStr(A_LoopField,0)\n\t\t\t\t If (GetRightText = Char)\n\t\t\t\t\tChar=\n\t\t\t\t}\n\t\t\t Output .= A_LoopField Char\n\t\t\t Char:=Char_Org\n\t\t\t}\n\t\t Else\n\t\t\tOutput .= A_LoopField \"`n\"\n\t\t}\n\t Return TF_ReturnOutPut(OW, OutPut, FileName)\n\t}\n\n;----- Helper functions ----------------\n\nTF_SetGlobal(var, content = \"\") ; helper function for TF_Split* to return array and not files, credits Tuncay :-)\n\t{\n\t global\n\t %var% := content\n\t}\n\n; Helper function to determine if VAR/TEXT or FILE is passed to TF\n; Update 11 January 2010 (skip filecheck if `n in Text -> can't be file)\nTF_GetData(byref OW, byref Text, byref FileName)\n\t{\n\t If (text = 0 \"\") ; v3.6 -> v3.7 https://github.com/hi5/TF/issues/4 and https://autohotkey.com/boards/viewtopic.php?p=142166#p142166 in case user passes on zero/zeros (\"0000\") as text - will error out when passing on one 0 and there is no file with that name\n\t\t{\n\t\t IfNotExist, %Text% ; additional check to see if a file 0 exists\n\t\t\t{\n\t\t\t MsgBox, 48, TF Lib Error, % \"Read Error - possible reasons (see documentation):`n- Perhaps you used !\"\"file.txt\"\" vs \"\"!file.txt\"\"`n- A single zero (0) was passed on to a TF function as text\"\n\t\t\t ExitApp\n\t\t\t}\n\t\t}\n\t OW=0 ; default setting: asume it is a file and create file_copy\n\t IfNotInString, Text, `n ; it can be a file as the Text doesn't contact a newline character\n\t\t{\n\t\t If (SubStr(Text,1,1)=\"!\") ; first we check for \"overwrite\"\n\t\t\t{\n\t\t\t Text:=SubStr(Text,2)\n\t\t\t OW=1 ; overwrite file (if it is a file)\n\t\t\t}\n\t\t IfNotExist, %Text% ; now we can check if the file exists, it doesn't so it is a var\n\t\t\t{\n\t\t\t If (OW=1) ; the variable started with a ! so we need to put it back because it is variable/text not a file\n\t\t\t\tText:= \"!\" . Text\n\t\t\t OW=2 ; no file, so it is a var or Text passed on directly to TF\n\t\t\t}\n\t\t}\n\t Else ; there is a newline character in Text so it has to be a variable\n\t\t{\n\t\t OW=2\n\t\t}\n\t If (OW = 0) or (OW = 1) ; it is a file, so we have to read into var Text\n\t\t{\n\t\t Text := (SubStr(Text,1,1)=\"!\") ? (SubStr(Text,2)) : Text\n\t\t FileName=%Text% ; Store FileName\n\t\t FileRead, Text, %Text% ; Read file and return as var Text\n\t\t If (ErrorLevel > 0)\n\t\t\t{\n\t\t\t MsgBox, 48, TF Lib Error, % \"Can not read \" FileName\n\t\t\t ExitApp\n\t\t\t}\n\t\t}\n\t Return\n\t}\n\n; Skan - http://www.autohotkey.com/forum/viewtopic.php?p=45880#45880\n; SetWidth() : SetWidth increases a String's length by adding spaces to it and aligns it Left/Center/Right. ( Requires Space() )\nTF_SetWidth(Text,Width,AlignText)\n\t{\n\t If (AlignText!=0 and AlignText!=1 and AlignText!=2)\n\t\tAlignText=0\n\t If AlignText=0\n\t\t{\n\t\t RetStr= % (Text)TF_Space(Width)\n\t\t StringLeft, RetText, RetText, %Width%\n\t\t}\n\t If AlignText=1\n\t\t{\n\t\t Spaces:=(Width-(StrLen(Text)))\n\t\t RetStr= % TF_Space(Round(Spaces/2))(Text)TF_Space(Spaces-(Round(Spaces/2)))\n\t\t}\n\t If AlignText=2\n\t\t{\n\t\t RetStr= % TF_Space(Width)(Text)\n\t\t StringRight, RetStr, RetStr, %Width%\n\t\t}\n\t Return RetStr\n\t}\n\n; Skan - http://www.autohotkey.com/forum/viewtopic.php?p=45880#45880\nTF_Space(Width)\n\t{\n\t Loop,%Width%\n\t\tSpace=% Space Chr(32)\n\t Return Space\n\t}\n\n; Write to file or return variable depending on input\nTF_ReturnOutPut(OW, Text, FileName, TrimTrailing = 1, CreateNewFile = 0) {\n\tIf (OW = 0) ; input was file, file_copy will be created, if it already exist file_copy will be overwritten\n\t\t{\n\t\t IfNotExist, % FileName ; check if file Exist, if not return otherwise it would create an empty file. Thanks for the idea Murp|e\n\t\t\t{\n\t\t\t If (CreateNewFile = 1) ; CreateNewFile used for TF_SplitFileBy* and others\n\t\t\t\t{\n\t\t\t\t OW = 1\n\t\t\t\t Goto CreateNewFile\n\t\t\t\t}\n\t\t\t Else\n\t\t\t\tReturn\n\t\t\t}\n\t\t If (TrimTrailing = 1)\n\t\t\t StringTrimRight, Text, Text, 1 ; remove trailing `n\n\t\t SplitPath, FileName,, Dir, Ext, Name\n\t\t If (Dir = \"\") ; if Dir is empty Text & script are in same directory\n\t\t\tDir := A_WorkingDir\n\t\t IfExist, % Dir \"\\backup\" ; if there is a backup dir, copy original file there\n\t\t\tFileCopy, % Dir \"\\\" Name \"_copy.\" Ext, % Dir \"\\backup\\\" Name \"_copy.bak\", 1\n\t\t FileDelete, % Dir \"\\\" Name \"_copy.\" Ext\n\t\t FileAppend, %Text%, % Dir \"\\\" Name \"_copy.\" Ext\n\t\t Return Errorlevel ? False : True\n\t\t}\n\t CreateNewFile:\n\t If (OW = 1) ; input was file, will be overwritten by output\n\t\t{\n\t\t IfNotExist, % FileName ; check if file Exist, if not return otherwise it would create an empty file. Thanks for the idea Murp|e\n\t\t\t{\n\t\t\tIf (CreateNewFile = 0) ; CreateNewFile used for TF_SplitFileBy* and others\n\t\t\t\tReturn\n\t\t\t}\n\t\t If (TrimTrailing = 1)\n\t\t\t StringTrimRight, Text, Text, 1 ; remove trailing `n\n\t\t SplitPath, FileName,, Dir, Ext, Name\n\t\t If (Dir = \"\") ; if Dir is empty Text & script are in same directory\n\t\t\tDir := A_WorkingDir\n\t\t IfExist, % Dir \"\\backup\" ; if there is a backup dir, copy original file there\n\t\t\tFileCopy, % Dir \"\\\" Name \".\" Ext, % Dir \"\\backup\\\" Name \".bak\", 1\n\t\t FileDelete, % Dir \"\\\" Name \".\" Ext\n\t\t FileAppend, %Text%, % Dir \"\\\" Name \".\" Ext\n\t\t Return Errorlevel ? False : True\n\t\t}\n\tIf (OW = 2) ; input was var, return variable\n\t\t{\n\t\t If (TrimTrailing = 1)\n\t\t\tStringTrimRight, Text, Text, 1 ; remove trailing `n\n\t\t Return Text\n\t\t}\n\t}\n\n; _MakeMatchList()\n; Purpose:\n; Make a MatchList which is used in various functions\n; Using a MatchList gives greater flexibility so you can process multiple\n; sections of lines in one go avoiding repetitive fileread/append actions\n; For TF 3.4 added COL = 0/1 option (for TF_Col* functions) and CallFunc for\n; all TF_* functions to facilitate bug tracking\n_MakeMatchList(Text, Start = 1, End = 0, Col = 0, CallFunc = \"Not available\")\n\t{\n\t ErrorList=\n\t (join|\nError 01: Invalid StartLine parameter (non numerical character)`nFunction used: %CallFunc%\nError 02: Invalid EndLine parameter (non numerical character)`nFunction used: %CallFunc%\nError 03: Invalid StartLine parameter (only one + allowed)`nFunction used: %CallFunc%\n\t )\n\t StringSplit, ErrorMessage, ErrorList, |\n\t Error = 0\n\n\t If (Col = 1)\n\t\t{\n\t\t LongestLine:=TF_Stat(Text)\n\t\t If (End > LongestLine) or (End = 1) ; FIXITHERE BUG\n\t\t\tEnd:=LongestLine\n\t\t}\n\n\t TF_MatchList= ; just to be sure\n\t If (Start = 0 or Start = \"\")\n\t\tStart = 1\n\n\t ; some basic error checking\n\n\t ; error: only digits - and + allowed\n\t If (RegExReplace(Start, \"[ 0-9+\\-\\,]\", \"\") <> \"\")\n\t\t Error = 1\n\n\t If (RegExReplace(End, \"[0-9 ]\", \"\") <> \"\")\n\t\t Error = 2\n\n\t ; error: only one + allowed\n\t If (TF_Count(Start,\"+\") > 1)\n\t\t Error = 3\n\n\t If (Error > 0 )\n\t\t{\n\t\t MsgBox, 48, TF Lib Error, % ErrorMessage%Error%\n\t\t ExitApp\n\t\t}\n\n\t ; Option #0 [ added 30-Oct-2010 ]\n\t ; Startline has negative value so process X last lines of file\n\t ; endline parameter ignored\n\n\t If (Start < 0) ; remove last X lines from file, endline parameter ignored\n\t\t{\n\t\t Start:=TF_CountLines(Text) + Start + 1\n\t\t End=0 ; now continue\n\t\t}\n\n\t ; Option #1\n\t ; StartLine has + character indicating startline + incremental processing.\n\t ; EndLine will be used\n\t ; Make TF_MatchList\n\n\t IfInString, Start, `+\n\t\t{\n\t\t If (End = 0 or End = \"\") ; determine number of lines\n\t\t\tEnd:= TF_Count(Text, \"`n\") + 1\n\t\t StringSplit, Section, Start, `, ; we need to create a new \"TF_MatchList\" so we split by ,\n\t\t Loop, %Section0%\n\t\t\t{\n\t\t\t StringSplit, SectionLines, Section%A_Index%, `+\n\t\t\t LoopSection:=End + 1 - SectionLines1\n\t\t\t Counter=0\n\t\t\t \t TF_MatchList .= SectionLines1 \",\"\n\t\t\t Loop, %LoopSection%\n\t\t\t\t{\n\t\t\t\t If (A_Index >= End) ;\n\t\t\t\t\tBreak\n\t\t\t\t If (Counter = (SectionLines2-1)) ; counter is smaller than the incremental value so skip\n\t\t\t\t\t{\n\t\t\t\t\t TF_MatchList .= (SectionLines1 + A_Index) \",\"\n\t\t\t\t\t Counter=0\n\t\t\t\t\t}\n\t\t\t\t Else\n\t\t\t\t\tCounter++\n\t\t\t\t}\n\t\t\t}\n\t\t StringTrimRight, TF_MatchList, TF_MatchList, 1 ; remove trailing ,\n\t\t Return TF_MatchList\n\t\t}\n\n\t ; Option #2\n\t ; StartLine has - character indicating from-to, COULD be multiple sections.\n\t ; EndLine will be ignored\n\t ; Make TF_MatchList\n\n\t IfInString, Start, `-\n\t\t{\n\t\t StringSplit, Section, Start, `, ; we need to create a new \"TF_MatchList\" so we split by ,\n\t\t Loop, %Section0%\n\t\t\t{\n\t\t\t StringSplit, SectionLines, Section%A_Index%, `-\n\t\t\t LoopSection:=SectionLines2 + 1 - SectionLines1\n\t\t\t Loop, %LoopSection%\n\t\t\t\t{\n\t\t\t\t TF_MatchList .= (SectionLines1 - 1 + A_Index) \",\"\n\t\t\t\t}\n\t\t\t}\n\t\t StringTrimRight, TF_MatchList, TF_MatchList, 1 ; remove trailing ,\n\t\t Return TF_MatchList\n\t\t}\n\n\t ; Option #3\n\t ; StartLine has comma indicating multiple lines.\n\t ; EndLine will be ignored\n\n\t IfInString, Start, `,\n\t\t{\n\t\t TF_MatchList:=Start\n\t\t Return TF_MatchList\n\t\t}\n\n\t ; Option #4\n\t ; parameters passed on as StartLine, EndLine.\n\t ; Make TF_MatchList from StartLine to EndLine\n\n\t If (End = 0 or End = \"\") ; determine number of lines\n\t\t\tEnd:= TF_Count(Text, \"`n\") + 1\n\t LoopTimes:=End-Start\n\t Loop, %LoopTimes%\n\t\t{\n\t\t TF_MatchList .= (Start - 1 + A_Index) \",\"\n\t\t}\n\t TF_MatchList .= End \",\"\n\t StringTrimRight, TF_MatchList, TF_MatchList, 1 ; remove trailing ,\n\t Return TF_MatchList\n\t}\n\n; added for TF 3.4 col functions - currently only gets longest line may change in future\nTF_Stat(Text)\n\t{\n\t TF_GetData(OW, Text, FileName)\n\t Sort, Text, f _AscendingLinesL\n\t Pos:=InStr(Text,\"`n\")-1\n\t Return pos\n\t}\n\n_AscendingLinesL(a1, a2) ; used by TF_Stat\n\t{\n\t Return StrLen(a2) - StrLen(a1)\n\t}\n\n/* -------------- */\n"
  }
]