{"id":15029526,"url":"https://github.com/pathikrit/better-files","last_synced_at":"2025-05-14T15:06:55.301Z","repository":{"id":1359264,"uuid":"42343629","full_name":"pathikrit/better-files","owner":"pathikrit","description":"Simple, safe and intuitive Scala I/O","archived":false,"fork":false,"pushed_at":"2024-08-13T00:56:52.000Z","size":6604,"stargazers_count":1476,"open_issues_count":50,"forks_count":144,"subscribers_count":36,"default_branch":"master","last_synced_at":"2025-04-09T11:03:41.324Z","etag":null,"topics":["java-interoperability","scala","scanner"],"latest_commit_sha":null,"homepage":"https://pathikrit.github.io/better-files/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pathikrit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-09-12T03:48:31.000Z","updated_at":"2025-04-04T03:42:04.000Z","dependencies_parsed_at":"2024-10-14T17:40:45.235Z","dependency_job_id":null,"html_url":"https://github.com/pathikrit/better-files","commit_stats":{"total_commits":949,"total_committers":62,"mean_commits":"15.306451612903226","dds":"0.14436248682824027","last_synced_commit":"cd4d3dc4f24a9f47546383b6f5f340a93e10b99c"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathikrit%2Fbetter-files","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathikrit%2Fbetter-files/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathikrit%2Fbetter-files/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pathikrit%2Fbetter-files/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pathikrit","download_url":"https://codeload.github.com/pathikrit/better-files/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254169262,"owners_count":22026209,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java-interoperability","scala","scanner"],"created_at":"2024-09-24T20:10:55.174Z","updated_at":"2025-05-14T15:06:55.284Z","avatar_url":"https://github.com/pathikrit.png","language":"Scala","readme":"# better-files [![Scaladex][scaladexImg]][scaladexLink] [![License][licenseImg]][licenseLink]\n\n`better-files` is a [dependency-free](build.sbt) *pragmatic* [thin Scala wrapper](src/main/scala/better/files/File.scala) around [Java NIO](https://docs.oracle.com/javase/tutorial/essential/io/fileio.html).\n\nConsult [the changelog](CHANGES.md) if you are upgrading your library.\n\n### Questions? [![Gitter][gitterImg]][gitterLink] [![Average time to resolve an issue][isItMaintainedImg]][isItMaintainedLink]\nAsk in our gitter channel or [file an issue with the question tag](https://github.com/pathikrit/better-files/labels/question)\n\n## Motivation\nImagine you have to write the following method:\n1) List all `.csv` files in a directory by increasing order of file size\n2) Drop the first line of each file and concat the rest into a single output file\n3) Split the above output file into `n` smaller files without breaking up the lines in the input files\n4) `gzip` each of the smaller output files\n\nNote: Your program should work when files are much bigger than memory in your JVM and must close all open resources correctly\n\nThe above task is not that easy to write in Java or shell or Python without a certain amount of Googling.\nUsing better-files, the above problem can be solved in a fairly straightforward way:\n```scala\nimport better.files._\n\ndef run(inputDir: File, outputDir: File, n: Int) = {\n  val count = new AtomicInteger()\n  val outputs = Vector.tabulate(n)(i =\u003e outputDir / s\"part-$i.csv.gz\")\n  for {\n    writers \u003c- outputs.map(_.newGzipOutputStream().printWriter()).autoClosed\n    inputFile \u003c- inputDir.list(_.extension == Some(\".csv\")).toSeq.sorted(File.Order.bySize)\n    line \u003c- inputFile.lineIterator.drop(1)\n  } writers(count.incrementAndGet() % n).println(line)\n}\n```\n\n## Tests [![Tests][githubActionsImg]][githubActionsLink] [![codecov][codecovImg]][codecovLink] [![Known Vulnerabilities][snykImg]][snykLink]\n* [FileSpec](src/test/scala/better/files/FileSpec.scala)\n* [FileWatcherSpec](src/test/scala/better/files/akka/FileWatcherSpec.scala)\n* [Benchmarks](#benchmarks)\n\n## Talks\n  - [ScalaDays NYC 2016][scalaDaysNyc2016Event] ([slides][scalaDaysNyc2016Slides])\n\n  \u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=uaYKkpqs6CE\" target=\"_blank\"\u003e\n    \u003cimg src=\"src/site/tech_talk_preview.png\" alt=\"ScalaDays NYC 2016: Introduction to better-files\" width=\"480\" height=\"360\" border=\"10\" /\u003e\n  \u003c/a\u003e\n\n  - [ScalaDays Berlin 2016][scalaDaysBerlin2016Event] ([video][scalaDaysBerlin2016Video], [slides][scalaDaysBerlin2016Slides])\n  - [Scalæ by the Bay 2016][scalæByTheBay2016Event] ([video][scalæByTheBay2016Video], [slides][scalæByTheBay2016Slides])\n\n## Tutorial\n  0. [Installation](#installation)\n  0. [Instantiation](#instantiation)\n  0. [Simple I/O](#file-readwrite)\n  0. [Resource APIs](#resource-apis)\n  0. [Streams](#streams)\n  0. [Encodings](#encodings)\n  0. [Java serialization utils](#java-serialization-utils)\n  0. [Java compatibility](#java-interoperability)\n  0. [Globbing](#globbing)\n  0. [File system operations](#file-system-operations)\n  0. [Checksums](#checksums)\n  0. [Temporary files](#temporary-files)\n  0. [UNIX DSL](#unix-dsl)\n  0. [File attributes](#file-attributes)\n  0. [File comparison](#file-comparison)\n  0. [Zip/GZip](#zip-apis)\n  0. [Automatic Resource Management](#lightweight-arm)\n  0. [Scanner](#scanner)\n  0. [File Monitoring](#file-monitoring)\n  0. [Reactive File Watcher](#akka-file-watcher)\n\n[licenseImg]: https://img.shields.io/github/license/pathikrit/better-files.svg\n[licenseImg2]: https://img.shields.io/:license-mit-blue.svg\n[licenseLink]: LICENSE\n\n[githubActionsImg]: https://github.com/pathikrit/better-files/actions/workflows/build.yml/badge.svg?branch=master\n[githubActionsLink]:https://github.com/pathikrit/better-files/actions/workflows/build.yml?query=branch:master\n\n[codecovImg]: https://img.shields.io/codecov/c/github/pathikrit/better-files/master.svg\n[codecovImg2]: https://codecov.io/github/pathikrit/better-files/coverage.svg?branch=master\n[codecovLink]: http://codecov.io/github/pathikrit/better-files?branch=master\n\n[gitterImg]: https://img.shields.io/gitter/room/pathikrit/better-files.svg\n[gitterImg2]: https://badges.gitter.im/Join%20Chat.svg\n[gitterLink]: https://gitter.im/pathikrit/better-files\n\n[scaladexImg]: https://index.scala-lang.org/pathikrit/better-files/better-files/latest.svg\n[scaladexLink]: https://index.scala-lang.org/pathikrit/better-files\n\n[scalaDaysNyc2016Event]: http://event.scaladays.org/scaladays-nyc-2016/#!#schedulePopupExtras-7664\n[scalaDaysNyc2016Video]: https://www.youtube.com/watch?v=uaYKkpqs6CE\n\u003c!--[scalaDaysNyc2016VideoPreview]: http://img.youtube.com/vi/uaYKkpqs6CE/0.jpg--\u003e\n[scalaDaysNyc2016VideoPreview]: src/site/tech_talk_preview.png\n[scalaDaysNyc2016Slides]: https://slides.com/pathikrit/better-files/\n\n[scalaDaysBerlin2016Event]: http://event.scaladays.org/scaladays-berlin-2016#!#schedulePopupExtras-7668\n[scalaDaysBerlin2016Video]: https://www.youtube.com/watch?v=m2YsD5cgnzI\n[scalaDaysBerlin2016Slides]: https://slides.com/pathikrit/better-files/\n\n[scalæByTheBay2016Event]: http://sched.co/7iUn\n[scalæByTheBay2016Video]: https://www.youtube.com/watch?v=bLiCE6NGjrk\u0026t=251s\n[scalæByTheBay2016Slides]: https://slides.com/pathikrit/better-files/\n\n[snykImg]: https://snyk.io/test/github/pathikrit/better-files/badge.svg?targetFile=build.sbt\n[snykLink]: https://snyk.io/test/github/pathikrit/better-files?targetFile=build.sbt\n\n[scalaStewardImg]: https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=\n[scalaStewardLink]: https://scala-steward.org\n\n[isItMaintainedImg]: http://isitmaintained.com/badge/resolution/pathikrit/better-files.svg\n[isItMaintainedLink]: http://isitmaintained.com/project/pathikrit/better-files\n\n[mavenLink]: https://repo1.maven.org/maven2/com/github/pathikrit/\n\n[scalaDocImg-3]: https://javadoc.io/badge2/com.github.pathikrit/better-files_3/javadoc.svg?label=scaladoc\u0026color=informational\n[scalaDocLink-3]: https://javadoc.io/doc/com.github.pathikrit/better-files_3/latest/better/files/File.html\n[mavenImg-3]: https://img.shields.io/maven-central/v/com.github.pathikrit/better-files_3.svg?label=maven\u0026color=brightgreen\n[mavenLink-3]: https://search.maven.org/artifact/com.github.pathikrit/better-files_3\n[snapshotImg-3]: https://img.shields.io/nexus/s/com.github.pathikrit/better-files_3?color=yellow\u0026server=https%3A%2F%2Foss.sonatype.org%2F\n[snapshotLink-3]: https://oss.sonatype.org/content/repositories/snapshots/com/github/pathikrit/better-files_3/\n\n[scalaDocImg-2.13]: https://javadoc.io/badge2/com.github.pathikrit/better-files_2.13/javadoc.svg?label=scaladoc\u0026color=informational\n[scalaDocLink-2.13]: https://javadoc.io/doc/com.github.pathikrit/better-files_2.13/latest/better/files/File.html\n[mavenImg-2.13]: https://img.shields.io/maven-central/v/com.github.pathikrit/better-files_2.13.svg?label=maven\u0026color=brightgreen\n[mavenLink-2.13]: https://search.maven.org/artifact/com.github.pathikrit/better-files_2.13\n[snapshotImg-2.13]: https://img.shields.io/nexus/s/com.github.pathikrit/better-files_2.13?color=yellow\u0026server=https%3A%2F%2Foss.sonatype.org%2F\n[snapshotLink-2.13]: https://oss.sonatype.org/content/repositories/snapshots/com/github/pathikrit/better-files_2.13/\n\n[scalaDocImg-2.12]: https://javadoc.io/badge2/com.github.pathikrit/better-files_2.12/javadoc.svg?label=scaladoc\u0026color=informational\n[scalaDocLink-2.12]: https://javadoc.io/doc/com.github.pathikrit/better-files_2.12/latest/better/files/File.html\n[mavenImg-2.12]: https://img.shields.io/maven-central/v/com.github.pathikrit/better-files_2.12.svg?label=maven\u0026color=brightgreen\n[mavenLink-2.12]: https://search.maven.org/artifact/com.github.pathikrit/better-files_2.12\n[snapshotImg-2.12]: https://img.shields.io/nexus/s/com.github.pathikrit/better-files_2.12?color=yellow\u0026server=https%3A%2F%2Foss.sonatype.org%2F\n[snapshotLink-2.12]: https://oss.sonatype.org/content/repositories/snapshots/com/github/pathikrit/better-files_2.12/\n\n[scalaDocImg-2.11]: https://javadoc.io/badge2/com.github.pathikrit/better-files_2.11/javadoc.svg?label=scaladoc\u0026color=informational\n[scalaDocLink-2.11]: https://javadoc.io/doc/com.github.pathikrit/better-files_2.11/latest/better/files/File.html\n[mavenImg-2.11]: https://img.shields.io/maven-central/v/com.github.pathikrit/better-files_2.11.svg?label=maven\u0026color=brightgreen\n[mavenLink-2.11]: https://search.maven.org/artifact/com.github.pathikrit/better-files_2.11\n[snapshotImg-2.11]: https://img.shields.io/nexus/s/com.github.pathikrit/better-files_2.11?color=yellow\u0026server=https%3A%2F%2Foss.sonatype.org%2F\n[snapshotLink-2.11]: https://oss.sonatype.org/content/repositories/snapshots/com/github/pathikrit/better-files_2.11/\n\n[scalaDocImg-2.10]: https://javadoc.io/badge2/com.github.pathikrit/better-files_2.10/javadoc.svg?label=scaladoc\u0026color=informational\n[scalaDocLink-2.10]: https://javadoc.io/doc/com.github.pathikrit/better-files_2.10/latest/better/files/File.html\n[mavenImg-2.10]: https://img.shields.io/maven-central/v/com.github.pathikrit/better-files_2.10.svg?label=maven\u0026color=brightgreen\n[mavenLink-2.10]: https://search.maven.org/artifact/com.github.pathikrit/better-files_2.10\n[snapshotImg-2.10]: https://img.shields.io/nexus/s/com.github.pathikrit/better-files_2.10?color=yellow\u0026server=https%3A%2F%2Foss.sonatype.org%2F\n[snapshotLink-2.10]: https://oss.sonatype.org/content/repositories/snapshots/com/github/pathikrit/better-files_2.10/\n\n-------\n### Installation\n`sbt`:\n```scala\nlibraryDependencies += \"com.github.pathikrit\" %% \"better-files\" % version\n```\n\n`mill`:    \n```scala\nimport mill._, scalalib._\n\nobject moduleName extends ScalaModule {\n  override def ivyDeps = Agg(\n    ivy\"com.github.pathikrit::better-files:${version}\"\n  )\n}\n```\nAvailable Versions:\n\n| Scala      | Scaladoc                                 | [Maven Releases][mavenLink]        | Sonatype Snapshots                       |\n| :---:      | :---:                                    | :---:                              | :---:                                    |\n| Scala 3    | [![scalaDocImg-3]][scalaDocLink-3]       | [![mavenImg-3]][mavenLink-3]       | [![snapshotImg-3]][snapshotLink-3]       |\n| Scala 2.13 | [![scalaDocImg-2.13]][scalaDocLink-2.13] | [![mavenImg-2.13]][mavenLink-2.13] | [![snapshotImg-2.13]][snapshotLink-2.13] |\n| Scala 2.12 | [![scalaDocImg-2.12]][scalaDocLink-2.12] | [![mavenImg-2.12]][mavenLink-2.12] | [![snapshotImg-2.12]][snapshotLink-2.12] |\n| Scala 2.11 | [![scalaDocImg-2.11]][scalaDocLink-2.11] | [![mavenImg-2.11]][mavenLink-2.11] | [![snapshotImg-2.11]][snapshotLink-2.11] |\n| Scala 2.10 | [![scalaDocImg-2.10]][scalaDocLink-2.10] | [![mavenImg-2.10]][mavenLink-2.10] | [![snapshotImg-2.10]][snapshotLink-2.10] |\n\n\n### Instantiation\nThe following are all equivalent:\n```scala\nimport better.files._\nimport File._\nimport java.io.{File =\u003e JFile}\n\nval f = File(\"/User/johndoe/Documents\")                      // using constructor\nval f1: File = file\"/User/johndoe/Documents\"                 // using string interpolator\nval f2: File = \"/User/johndoe/Documents\".toFile              // convert a string path to a file\nval f3: File = new JFile(\"/User/johndoe/Documents\").toScala  // convert a Java file to Scala\nval f4: File = root/\"User\"/\"johndoe\"/\"Documents\"             // using root helper to start from root\nval f5: File = `~` / \"Documents\"                             // also equivalent to `home / \"Documents\"`\nval f6: File = \"/User\"/\"johndoe\"/\"Documents\"                 // using file separator DSL\nval f7: File = \"/User\"/'johndoe/'Documents                   // same as above but using Symbols instead of Strings\nval f8: File = home/\"Documents\"/\"presentations\"/`..`         // use `..` to navigate up to parent\n```\n\n**Note**: Rename the import if you think the usage of the class `File` may confuse your teammates:\n```scala\nimport better.files.{File =\u003e ScalaFile, _}\nimport java.io.File\n```\nI personally prefer renaming the Java crap instead:\n```scala\nimport better.files._\nimport java.io.{File =\u003e JFile}\n```\n\n### File Read/Write\nDead simple I/O:\n```scala\nval file = root/\"tmp\"/\"test.txt\"\nfile.overwrite(\"hello\")\nfile.appendLine().append(\"world\")\nassert(file.contentAsString() == \"hello\\nworld\")\n```\nIf you are someone who likes symbols, then the above code can also be written as:\n```scala\nimport better.files.Dsl.SymbolicOperations\n\nfile \u003c \"hello\"     // same as file.overwrite(\"hello\")\nfile \u003c\u003c \"world\"    // same as file.appendLines(\"world\")\nassert(file! == \"hello\\nworld\")\n```\nOr even, right-associatively:\n```scala\nimport better.files.Dsl.SymbolicOperations\n\n\"hello\" `\u003e:` file\n\"world\" \u003e\u003e: file\nval bytes: Array[Byte] = file.loadBytes\n```\n[Fluent Interface](https://en.wikipedia.org/wiki/Fluent_interface):\n```scala\n (root/\"tmp\"/\"diary.txt\")\n  .createIfNotExists()\n  .appendLine()\n  .appendLines(\"My name is\", \"Inigo Montoya\")\n  .moveToDirectory(home/\"Documents\")\n  .renameTo(\"princess_diary.txt\")\n  .changeExtensionTo(\".md\")\n  .lines()\n```\n\n### Resource APIs\nConfused by the [various ways to load resources in Java](https://stackoverflow.com/questions/3861989/preferred-way-of-loading-resources-in-java)?\nWorry no more:\n```scala\nval resource        : InputStream   = Resource.getAsStream(\"foo.txt\") //Same as this.getClass().getResource(\"foo.txt\")\nval resourceURL     : java.net.URL  = Resource.getUrl(\"foo.txt\")\nval rootResourceURL : java.net.URL  = Resource.getUrl()\nval resourceAsStr   : String        = Resource.getAsString(\"foo.txt\")\n```\nThe above APIs can load from custom `ClassLoader`s too:\n```scala\nval resource        : InputStream   = Resource.at[MyClass].getAsStream(\"foo.txt\")\n``` \n\n### Streams\nVarious ways to slurp a file without loading its contents into memory:\n ```scala\nval bytes  : Iterator[Byte]            = file.bytes()\nval chars  : Iterator[Char]            = file.chars()\nval lines  : Iterator[String]          = file.lineIterator()     //file.lines loads all lines in memory\n```\nNote: The above APIs can be traversed at most once e.g. `file.bytes` is a `Iterator[Byte]` which only allows `TraversableOnce`.\nTo traverse it multiple times without creating a new iterator instance, convert it into some other collection e.g. `file.bytes.toStream`\n\nYou can write an `Iterator[Byte]` or an `Iterator[String]` back to a file:\n```scala\nfile.writeBytes(bytes)\nfile.printLines(lines)\n```\n\n[`tee`](http://stackoverflow.com/questions/7987395/) multiple outputstreams:\n```scala\nval s3 = s1 tee s2\ns3.printWriter.println(s\"Hello world\") // gets written to both s1 and s2\n```\n\n### Encodings\nYou can supply your own charset too for anything that does a read/write (it assumes `java.nio.charset.Charset.defaultCharset()` if you don't provide one):\n```scala\nval content: String = file.contentAsString()  // default charset\n\n// custom charset:\nimport java.nio.charset.Charset\nfile.contentAsString(charset = Charset.forName(\"US-ASCII\"))\n\n//or simply using implicit conversion from Strings\nfile.write(\"hello world\", charset = \"US-ASCII\")\n ```\n\nNote: By default, `better-files` [correctly handles BOMs while decoding](src/main/scala/better/files/UnicodeCharset.scala).\nIf you wish to have the [incorrect JDK behaviour](http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4508058),\nyou would need to supply Java's UTF-8 charset e.g.:\n```scala\nfile.contentAsString(charset = Charset.forName(\"UTF-8\"))    // Default incorrect JDK behaviour for UTF-8 (see: JDK-4508058)\n```\n\nIf you also wish to write BOMs while encoding, you would need to supply it as:\n```scala\nfile.write(\"hello world\", charset = UnicodeCharset(\"UTF-8\", writeByteOrderMarkers = true))\n```\n\n### Java serialization utils\nSome common utils to serialize/deserialize using Java's serialization util\n```scala\ncase class Person(name: String, age: Int)\nval person = new Person(\"Chris\", 24)\n\n// Write\nfile.newOutputStream().asObjectOutputStream.serialize(obj).flush()\n\n// Read\nval person2 = file.newInputStream().asObjectInputStream.deserialize[Person]\nassert(person == person2)\n\n// Read using custom class loader:\nfile.newInputStream().asObjectInputStreamUsingClassLoader(classLoader = myClassLoader).deserialize[Person]\n```\n\nThe above can be simply written as:\n```scala\nval person2: Person = file.writeSerialized(person).readDeserialized()\nassert(person == person2)\n```\n\n### Java interoperability\nYou can always access the Java I/O classes:\n```scala\nval file: File = tmp / \"hello.txt\"\nval javaFile     : java.io.File                 = file.toJava\nval uri          : java.net.URI                 = file.uri\nval url          : java.net.URL                 = file.url\nval reader       : java.io.BufferedReader       = file.newBufferedReader()\nval outputstream : java.io.OutputStream         = file.newOutputStream()\nval writer       : java.io.BufferedWriter       = file.newBufferedWriter()\nval inputstream  : java.io.InputStream          = file.newInputStream()\nval path         : java.nio.file.Path           = file.path\nval fs           : java.nio.file.FileSystem     = file.fileSystem\nval channel      : java.nio.channel.FileChannel = file.newFileChannel()\nval ram          : java.io.RandomAccessFile     = file.newRandomAccess()\nval fr           : java.io.FileReader           = file.newFileReader()\nval fw           : java.io.FileWriter           = file.newFileWriter(append = true)\nval printer      : java.io.PrintWriter          = file.newPrintWriter()\n```\nThe library also adds some useful [implicits](http://pathikrit.github.io/better-files/api/default/better/files/Implicits.html) to above classes e.g.:\n```scala\nfile1.reader() \u003e file2.writer ()      // pipes a reader to a writer\nSystem.in \u003e file2.out             // pipes an inputstream to an outputstream\nsrc.pipeTo(sink)                  // if you don't like symbols\n\nval bytes   : Iterator[Byte]        = inputstream.bytes()\nval bis     : BufferedInputStream   = inputstream.buffered()\nval bos     : BufferedOutputStream  = outputstream.buffered()\nval reader  : InputStreamReader     = inputstream.reader()\nval writer  : OutputStreamWriter    = outputstream.writer()\nval printer : PrintWriter           = outputstream.printWriter()\nval br      : BufferedReader        = reader.buffered()\nval bw      : BufferedWriter        = writer.buffered()\nval mm      : MappedByteBuffer      = fileChannel.toMappedByteBuffer()\nval str     : String                = inputstream.asString()  //Read a string from an InputStream\nval in      : InputStream           = str.inputStream()\nval reader  : Reader                = str.reader()\nval lines   : Seq[String]           = str.lines()\n```\n`better-files` also supports [certain conversions that are not supported out of the box by the JDK](https://stackoverflow.com/questions/62241/how-to-convert-a-reader-to-inputstream-and-a-writer-to-outputstream)\n\n### Globbing\nNo need to port [this](http://docs.oracle.com/javase/tutorial/essential/io/find.html) to Scala:\n```scala\nval dir = \"src\"/\"test\"\nval matches: Iterator[File] = dir.glob(\"**/*.{java,scala}\")\n// above code is equivalent to:\ndir.listRecursively().filter(f =\u003e f.extension == Some(\".java\") || f.extension == Some(\".scala\"))\n```\n\nYou can even use more advanced regex syntax instead of [glob syntax](http://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob):\n```scala\nval matches = dir.globRegex(\"^\\\\w*$\".r) //equivalent to dir.glob(\"^\\\\w*$\")(syntax = File.PathMatcherSyntax.regex)\n```\n\nBy default, glob syntax in `better-files` is [different from](https://github.com/pathikrit/better-files/issues/114)\nthe default JDK glob behaviour since it always includes path. To use the default behaviour:\n```scala\ndir.glob(\"**/*.txt\", includePath = false) // JDK default\n//OR\ndir.glob(\"*.txt\", includePath = true) // better-files default\n```\nYou can also extend the `File.PathMatcherSyntax` to create your own matchers.\n\nFor custom cases:\n```scala\ndir.collectChildren(_.isSymbolicLink) // collect all symlinks in a directory\n```\nFor simpler cases, you can always use `dir.list` or `dir.walk(maxDepth: Int)`\n\n### File system operations\nUtilities to `ls`, `cp`, `rm`, `mv`, `ln`, `md5`, `touch`, `cat` etc:\n```scala\nfile.touch()\nfile.delete()     // unlike the Java API, also works on directories as expected (deletes children recursively)\nfile.clear()      // If directory, deletes all children; if file clears contents\nfile.renameTo(newName: String)\nfile.moveTo(destination)\nfile.moveToDirectory(destination)\nfile.copyTo(destination)       // unlike the default API, also works on directories (copies recursively)\nfile.copyToDirectory(destination)\nfile.linkTo(destination)                     // ln destination file\nfile.symbolicLinkTo(destination)             // ln -s destination file\nfile.setOwner(user: String)      // chown user file\nfile.setGroup(group: String)     // chgrp group file\nSeq(file1, file2) `\u003e:` file3     // same as cat file1 file2 \u003e file3 (must import import better.files.Dsl.SymbolicOperations)\nSeq(file1, file2) \u003e\u003e: file3      // same as cat file1 file2 \u003e\u003e file3 (must import import better.files.Dsl.SymbolicOperations)\nfile.isReadLocked(); file.isWriteLocked(); file.isLocked()\nFile.numberOfOpenFileDescriptors()        // number of open file descriptors\n```\n\n### Checksums\nOne liner checksum for files:\n```scala\nfile.md5() // equivalent to file.checksum(\"md5\")\nfile.sha1()\nfile.sha256()\nfile.sha512()\n```\nNote: The above also works for directories (it recursively computes for each file in the directory).\n\nFor input/output streams:\n```scala\nval md5: String = inputstream.md5.hexDigest()\nval md5: String = outputstream.md5.hexDigest()\n```\nThe above consumes and closes the `inputstream`. If you want to write it to a file AND also compute the `sha512`, you can do:\n```scala\nval md5: String = inputstream.sha512.hexDigest(drainTo = someFile)\n```\n\n### Temporary files\nUtils to create temporary files:\n```scala\nFile.newTemporaryDirectory()\nFile.newTemporaryFile()\n```\nThe above APIs allow optional specifications of `prefix`, `suffix` and `parentDir`.\nThese files are [not deleted automatically on exit by the JVM](http://stackoverflow.com/questions/16691437/when-are-java-temporary-files-deleted) (you have to set `deleteOnExit` which adds to `shutdownHook`).\n\nA cleaner alternative is to use self-deleting file contexts which deletes the file immediately when done:\n```scala\nfor {\n tempFile \u003c- File.temporaryFile()\n} doSomething(tempFile) // tempFile is auto deleted at the end of this block - even if an exception happens\n```\n\nOR equivalently:\n```scala\nFile.usingTemporaryFile() {tempFile =\u003e\n  //do something\n}  // tempFile is auto deleted at the end of this block - even if an exception happens\n```\n\nYou can make any files temporary (i.e. delete after use) by doing this:\n```scala\nval foo = File.home / \"Downloads\" / \"foo.txt\"\n\nfor {\n temp \u003c- foo.toTemporary\n} doSomething(temp) // foo is deleted at the end of this block - even if an exception happens\n```\n\n### UNIX DSL\nAll the above can also be expressed using [methods](http://pathikrit.github.io/better-files/api/default/better/files/Dsl$.html) reminiscent of the command line:\n```scala\nimport better.files._\nimport better.files.Dsl._   // must import Dsl._ to bring in these utils\n\npwd / cwd     // current dir\ncp(file1, file2)\nmv(file1, file2)\nrm(file) /*or*/ del(file)\nls(file) /*or*/ dir(file)\nln(file1, file2)     // hard link\nln_s(file1, file2)   // soft link\ncat(file1)\ncat(file1) \u003e\u003e: file\ntouch(file)\nmkdir(file)\nmkdirs(file)         // mkdir -p\nchown(owner, file)\nchgrp(owner, file)\nchmod_+(permission, files)  // add permission\nchmod_-(permission, files)  // remove permission\nmd5(file); sha1(file); sha256(file); sha512(file)\nunzip(zipFile)(targetDir)\nzip(file*)(targetZipFile)\nungzip(gzipFile)(targetFile)\ngzip(file)(targetGZipFile)\n```\n\n### File attributes\nQuery various file attributes e.g.:\n```scala\nfile.name       // simpler than java.io.File#getName\nfile.extension()\nfile.contentType()\nfile.lastModifiedTime ()    // returns JSR-310 time\nfile.owner()\nfile.group()\nfile.isDirectory(); file.isSymbolicLink(); file.isRegularFile()\nfile.isHidden()\nfile.hide(); file.unhide()\nfile.isOwnerExecutable(); file.isGroupReadable() // etc. see file.permissions\nfile.size()                 // for a directory, computes the directory size\nfile.posixAttributes(); file.dosAttributes()  // see file.attributes\nfile.isEmpty(); file.nonEmpty()      // true if file has no content (or no children if directory) or does not exist and vice-versa\nfile.isParentOf(); file.isChildOf(); file.isSiblingOf(); file.siblings()\nfile(\"dos:system\") = true  // set custom meta-data for file (similar to Files.setAttribute)\n```\nAll the above APIs let you specify the [`LinkOption`](http://docs.oracle.com/javase/8/docs/api/java/nio/file/LinkOption.html) either directly:\n```scala\nfile.isDirectory(LinkOption.NOFOLLOW_LINKS)\n```\nOr using the [`File.LinkOptions`](http://pathikrit.github.io/better-files/api/default/better/files/File$$LinkOptions$.html) helper:\n```scala\nfile.isDirectory(File.LinkOptions.noFollow)\n```\n\n`chmod`:\n```scala\nimport java.nio.file.attribute.PosixFilePermission\nfile.addPermission(PosixFilePermission.OWNER_EXECUTE)      // chmod +X file\nfile.removePermission(PosixFilePermission.OWNER_WRITE)     // chmod -w file\nassert(file.permissionsAsString() == \"rw-r--r--\")\n\n// The following are all equivalent:\nassert(file.permissions contains PosixFilePermission.OWNER_EXECUTE)\nassert(file.testPermission(PosixFilePermission.OWNER_EXECUTE))\nassert(file.isOwnerExecutable())\n```\n\n### File comparison\nUse `==` to check for path-based equality and `===` for content-based equality:\n```scala\nfile1 == file2    // equivalent to `file1.isSamePathAs(file2)`\nfile1 === file2   // equivalent to `file1.isSameContentAs(file2)` (works for regular-files and directories)\nfile1 != file2    // equivalent to `!file1.isSamePathAs(file2)`\nfile1 !== file2   // equivalent to `!file1.isSameContentAs(file2)`\n```\nThere are also various [`Ordering[File]` instances](http://pathikrit.github.io/better-files/api/default/better/files/File$$Order$.html) included, e.g.:\n```scala\nval files = myDir.list.toSeq\nfiles.sorted(File.Order.byName)\nfiles.max(File.Order.bySize)\nfiles.min(File.Order.byDepth)\nfiles.max(File.Order.byModificationTime)\nfiles.sorted(File.Order.byDirectoriesFirst)\n```\n\n### Zip APIs\nYou don't have to lookup on StackOverflow \"[How to zip/unzip/gzip in Java/Scala?](http://stackoverflow.com/questions/9324933/)\":\n```scala\n// Unzipping:\nval zipFile: File = file\"path/to/research.zip\"\nval research: File = zipFile.unzipTo(destination = home/\"Documents\"/\"research\")\n\n// Zipping:\nval zipFile: File = directory.zipTo(destination = home/\"Desktop\"/\"toEmail.zip\")\n\n// Zipping in:\nval zipFile = File(\"countries.zip\").zipIn(Iterator(file\"usa.txt\", file\"russia.txt\"))()\n\n// Zipping/Unzipping to temporary files/directories:\nval someTempZipFile: File = directory.zip()\nval someTempDir: File = someTempZipFile.unzip()\nassert(directory === someTempDir)\n```\n\nMapping over each `ZipEntry`:\n```scala\nval fileNames = zipFile.newZipInputStream.mapEntries(_.getName) // gets the file names inside the zip file\n```\n\nGZIP handling:\n```scala\nFile(\"big-data.csv\").gzipTo(File(\"big-data.csv.gz\"))\nFile(\"big-data.csv.gz\").unGzipTo(File(\"big-data.csv\"))\n\n// GZIP stream handling:\nFile(\"countries.gz\").newInputStream().asGzipInputStream().lines.take(10).foreach(println)\n\ndef write(out: OutputStream, countries: Seq[String]) =\n  out.asGzipOutputStream().printWriter().printLines(countries).close()\n```\n\n### Lightweight ARM\nAuto-close Java closeables:\n```scala\nfor {\n  in \u003c- file1.newInputStream().autoClosed\n  out \u003c- file2.newOutputStream().autoClosed\n} in.pipeTo(out)\n// The input and output streams are auto-closed once out of scope\n```\n`better-files` provides convenient managed versions of all the Java closeables e.g. instead of writing:\n```scala\nfor {\n reader \u003c- file.newBufferedReader().autoClosed\n} foo(reader)\n```\nYou can write:\n```scala\nfor {\n reader \u003c- file.bufferedReader()    // returns Dispose[BufferedReader]\n} foo(reader)\n\n// or simply:\nfile.bufferedReader().foreach(foo)\n```\n\nSimilarly:\n```scala\nfor {\n reader \u003c- file.bufferedReader()\n} yield foo(reader)\n\n// Simpler\nfile.bufferedReader.map(foo).get()\n\n// Even simpler\nfile.bufferedReader.apply(foo)\n```\n\nIf `foo` itself is lazy and depends on `reader` being open, you should `flatMap` instead of `apply`:\n```scala\ndef lines(reader: BufferedReader): Iterator[String] = ???\n\nfor {\n  reader \u003c- file.bufferedReader()\n  line \u003c- lines(reader)\n} yield line\n\n// or simply\nfile.bufferedReader.flatMap(lines)\n```\n\nYou can also define your own custom disposable resources e.g.:\n```scala\ntrait Shutdownable {\n  def shutdown(): Unit = ()\n}\n\nobject Shutdownable {\n  implicit val disposable: Disposable[Shutdownable] = Disposable(_.shutdown())\n}\n\nval s: Shutdownable = ....\n\nfor {\n  instance \u003c- new Dispose(s)\n} doSomething(s)  // s is disposed after this\n```\n\n`using` syntax:\n```scala\nval lines: List[String] = using(file.newInputStream) { stream =\u003e\n  stream.lines.toList   // Must be eager so .toList\n}\n```\n\nAuto-closed instances also have a useful method to generate [self-closing iterators](src/main/scala/better/files/CloseableIterator.scala) that auto closes the parent resource on exhaustion:\n```scala\nval lines: Iterator[String] = file.lines()  // This will auto close the underlying stream on iterator exhaustion\n\nlines.find(_ == \"hello world\") //This will auto close the stream if nothing is found OR if the item is found\nlines.take(10).size //This will close the stream even if stream has \u003e10 lines\n\n// If you don't want this auto closing behaviour\nlines.nonClosing().take(10).size // This would leave stream open if it has \u003e10 lines and only close if stream has no more elements in it\n\n// If you don't even want it to close when underlying stream has no more elements\nlines.nonClosing(closeInTheEnd = false) // This will NEVER close the underlying stream - you should probably never need to do this!\n```\n\n### Scanner\nAlthough [`java.util.Scanner`](http://docs.oracle.com/javase/8/docs/api/java/util/Scanner.html) has a feature-rich API, it only allows parsing primitives.\nIt is also [notoriously slow](https://www.cpe.ku.ac.th/~jim/java-io.html) since it uses regexes and does un-Scala things like returns nulls and throws exceptions.\n\n`better-files` provides a [faster](#benchmarks), richer, safer, more idiomatic and compossible [Scala replacement](http://pathikrit.github.io/better-files/api/default/better/files/Scanner.html)\nthat [does not use regexes](src/main/scala/better/files/Scanner.scala), allows peeking, accessing line numbers, returns `Option`s whenever possible and lets the user mixin custom parsers:\n```scala\nval f1 = File(\"/tmp/temp.txt\")\nval data = f1.overwrite(s\"\"\"Hello World\n  | 1 true\n  | 2 3\n\"\"\".stripMargin)\nval scanner: Scanner = data.newScanner()\nassert(scanner.next[String] == \"Hello\")\nassert(scanner.lineNumber == 1)\nassert(scanner.next[String] == \"World\")\nassert(scanner.next[(Int, Boolean)] == (1, true))\nassert(scanner.nextLine() == \" 2 3\")\nassert(!scanner.hasNext)\n```\nIf you are simply interested in tokens, you can use `file.tokens()`\n\nWriting your own custom scanners:\n```scala\nsealed trait Animal\ncase class Dog(name: String) extends Animal\ncase class Cat(name: String) extends Animal\n\nimplicit val animalParser: Scannable[Animal] = Scannable {scanner =\u003e\n  val name = scanner.next[String]\n  if (name == \"Garfield\") Cat(name) else Dog(name)\n}\n\nval scanner = file.newScanner()\nprintln(scanner.next[Animal])\n```\n\nThe [shapeless-scanner](test/scala-2/better/files/ShapelessScannerSpec.scala) lets you scan [`HList`s](https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/hlists.scala):\n```scala\nval in = Scanner(\"\"\"\n  12 Bob True\n  13 Mary False\n  26 Rick True\n\"\"\")\n\nimport shapeless._\n\ntype Row = Int :: String :: Boolean :: HNil\n\nval out = Seq.fill(3)(in.next[Row])\nassert(out == Seq(\n  12 :: \"Bob\" :: true :: HNil,\n  13 :: \"Mary\" :: false :: HNil,\n  26 :: \"Rick\" :: true :: HNil\n))\n```\n\n[and case-classes](https://meta.plasm.us/posts/2015/11/08/type-classes-and-generic-derivation/):\n\n```scala\ncase class Person(id: Int, name: String, isMale: Boolean)\nval out2 = Seq.fill(3)(in.next[Person])\n```\n\nSimple CSV reader:\n```scala\nval file = \"\"\"\n  23,foo\n  42,bar\n\"\"\"\nval csvScanner = file.newScanner(StringSplitter.on(','))\ncsvScanner.next[Int]    //23\ncsvScanner.next[String] //foo\n```\n\n### File Monitoring\nVanilla Java watchers:\n```scala\nimport java.nio.file.{StandardWatchEventKinds =\u003e EventType}\nval service: java.nio.file.WatchService = myDir.newWatchService\nmyDir.register(service, events = Seq(EventType.ENTRY_CREATE, EventType.ENTRY_DELETE))\n```\nThe above APIs are [cumbersome to use](https://docs.oracle.com/javase/tutorial/essential/io/notification.html#process) (involves a lot of type-casting and null-checking),\nare based on a blocking [polling-based model](http://docs.oracle.com/javase/8/docs/api/java/nio/file/WatchKey.html),\ndoes not easily allow [recursive watching of directories](https://docs.oracle.com/javase/tutorial/displayCode.html?code=https://docs.oracle.com/javase/tutorial/essential/io/examples/WatchDir.java)\nand nor does it easily allow [watching regular files](http://stackoverflow.com/questions/16251273/) without writing a lot of Java boilerplate.\n\n`better-files` abstracts all the above ugliness behind a [simple interface](src/main/scala/better/files/File.scala#L1400):\n```scala\nval watcher = new FileMonitor(myDir, recursive = true) {\n  override def onCreate(file: File, count: Int) = println(s\"$file got created\")\n  override def onModify(file: File, count: Int) = println(s\"$file got modified $count times\")\n  override def onDelete(file: File, count: Int) = println(s\"$file got deleted\")\n}\nwatcher.start() \nThread.sleep(60 * 1000) // The above line starts the monitoring asynchronously \n```\nSometimes, instead of overwriting each of the 3 methods above, it is more convenient to override the dispatcher itself:\n```scala\nimport java.nio.file.{Path, StandardWatchEventKinds =\u003e EventType, WatchEvent}\n\nval watcher = new FileMonitor(myDir, recursive = true) {\n  override def onEvent(eventType: WatchEvent.Kind[Path], file: File, count: Int) = eventType match {\n    case EventType.ENTRY_CREATE =\u003e println(s\"$file got created\")\n    case EventType.ENTRY_MODIFY =\u003e println(s\"$file got modified $count\")\n    case EventType.ENTRY_DELETE =\u003e println(s\"$file got deleted\")\n  }\n}\n```\n\nThere is also an external module which gives high performance file monitoring and interpolates with better-files.\nSee: https://github.com/gmethvin/directory-watcher#better-files-integration-scala\n\n### Akka File Watcher\n`better-files` also provides an [example of how to build a reactive file watcher](src/test/scala/better/files/akka)\nbased on [Akka actors](http://doc.akka.io/docs/akka/snapshot/scala/actors.html) that supports dynamic dispatches:\n ```scala\nimport akka.actor.{ActorRef, ActorSystem}\nimport better.files.File.home\nimport better.files.FileWatcher._\nimport java.nio.file.{StandardWatchEventKinds =\u003e EventType}\n\nimplicit val system = ActorSystem(\"mySystem\")\n\nval watcher: ActorRef = (home/\"Downloads\").newWatcher(recursive = true)\n\n// register partial function for an event\nwatcher ! on(EventType.ENTRY_DELETE) {\n  case file if file.isDirectory =\u003e println(s\"directory $file got deleted\")\n  case file                     =\u003e println(s\"$file got deleted\")\n}\n\n// watch for multiple events\nwatcher ! when(events = EventType.ENTRY_CREATE, EventType.ENTRY_MODIFY) {\n  case (EventType.ENTRY_CREATE, file) =\u003e println(s\"$file got created\")\n  case (EventType.ENTRY_MODIFY, file) =\u003e println(s\"$file got modified\")\n}\n```\n\n## Benchmarks\n* [Scanner benchmarks](src/test/scala/better/files/benchmarks/Scanners.scala):\n```shell\n\u003e sbt \"testOnly better.files.benchmarks.*\"\nJavaScanner              : 2191 ms\nStringBuilderScanner     : 1325 ms\nCharBufferScanner        : 1117 ms\nStreamingScanner         :  212 ms\nIterableScanner          :  365 ms\nIteratorScanner          :  297 ms\nBetterFilesScanner       :  272 ms\nArrayBufferScanner       :  220 ms\nFastJavaIOScanner2       :  181 ms\nFastJavaIOScanner        :  179 ms\n```\n\n----\n[![Scala Steward badge][scalaStewardImg]][scalaStewardLink]\n\n[![YourKit](https://www.yourkit.com/images/yklogo.png)](https://www.yourkit.com/)\n\nYourKit supports better-files with its full-featured Java Profiler.\nYourKit, LLC is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/) and [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/),\ninnovative and intelligent tools for profiling Java and .NET applications.\n\n","funding_links":[],"categories":["Table of Contents","Extensions"],"sub_categories":["Extensions"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpathikrit%2Fbetter-files","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpathikrit%2Fbetter-files","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpathikrit%2Fbetter-files/lists"}