Introducció

13025

És habitual treballar amb un conjunt de bytes que es gestionen com un tot.

Aquest bytes poden representar una imatge, una película, una cançó, però des del punt de vista del sistema operatiu són bytes i el que representen no l'interessa, només que molts cops els vols guardar en un sistema de fitxers del disc dur o d'una unitat extraible.

És molt important que entenguis que és un fitxer pel sistema operatiu per entender perqué escribim programes d'una manera determinada.

Què és un fitxer

El sistema de fitxers treballa amb inodes (index nodes) que són un conjunt de metadades com el nom del fitxer, permisos d'accés, etc. que s'associen al conjunt de bytes que és el contingut del fitxer.

Encara que les dades més importants d'un inode són les adreces físiques on està el contigut del fitxer!

Com ja saps una de les metadates més importants és el nom perquè et dona molta informació respecte el contingut del fitxer.

Però és important per a tú, al sistema operatiu no l'interessa per res.

L'única limitació es respecte els noms que pots utilitzar, no pel seu significat (pots escriure una paraulota si vols), sinó perquè no permeen alguns caracters com espais en blanc, etc. coses que pel sistema operatiu són importants i que abans o després tindras que apendre.

Extensions de fitxers

Com hem explicat abans un inode té moltes metadades, però resulta que no en té una de molt important per a nosaltres: quin és el format del fitxer.

Com ja deus saber un fitxer de text no el puc obrir amb el reproductor de música, i una canço de "Supertramp" (ep, ja tinc una certa edat!) no la puc obrir amb l'Office.

Quan es van dissenyar el primers sistemes operatius van pensar que sabner quina aplicació has de fer servir per obrir un fitxer és assumpte teu.

Genial! Excepte que això és una mic caòtic i van apareixer les extensions de fitxers, que es afegir un . i un codi d'extensió al nom del fitxer per indicar quin és el format del contingut del fitxer.

A continuació tens alguns exemples;

| Extensió | Exemple | Descripció | |-|-| | txt | informet.txt | Fitxers de text sense format | | jpg | everest.jpg | Imatge amb format jpg | | mp3 | the-river.mp3 | Fitxer de música |

Però l'extensió de fitxer no converteix un conjunt de dades en un format concrect, és només una informació que pot estar equivocada.

Als Windows no els importa, és el que fan servir per decidir quin format té un fitxer.

Mira que Microsoft té diners, però clients poc exigents.

En canvi els sistemes operatius Linux no fan cas de l'extensió i fan servir el que s'anomena "magic numbers.

Per exemple, un fitxer jpg sempre comenca pels bytes FFD8, i un Linux sempre reconeixerà que el contingut d'un fitxer és jpg encara que l'entensió del nom del fitxer no sigui .jpg.

Arrenca una màquina WSL i verifica el que acabem d'explicar.

Metadades del fitxer

Metadades significa "dades sobre dades".

El contingut del fitxer són dades, encara que per ser exactes són una seqüència de bytes.

Una canço és una seqüeǹcia de bytes, el mateix una película, però com que al principi els ordinadors només treballaven amb dades, aquest és el nom que ha quedat.

No és una cosa tant extranya si penses que la potència d'un motor es mesura en cavalls, de quan es comparava un cotxe de gasolina amb un carro tirat per cavalls.

Quan escrivim o llegim el contingut de fitxers, escribim o llegim bytes, no dades!

Les metadates més habituals d'un fitxer, a més del nom que només ens interessa a nosaltres, són permisos d'accés, escritura i execució, la mida del fitxer, l'hora de creació, l'hora de l'últim accés, etc.

Camins absoluts i relatius

Un sistema de fitxers s'organitza mitjançant carpetes que poden tenir fitxers o carpetes.

És una analogia de quan els documents es guardaven en arxivadors físics.

Quan vols accedir a un fitxer, has d'indicar la localització del fitxer mitjançant un "path", que és el camí que ha de recorrer el sistema operatiu per localitzar el fitxer.

Per exemple, si el "path" és aquest:

$ cat /home/david/hello.txt

El sistema operatiu:

  1. Ha d'accedira a la carpeta arrel "/" per saber quin inode correspon el nom home
  2. A continuació accedir al inode "home" per saber quin inode correspon la carpeta david
  3. Finalment accedir al inode "david" per saber quin inode correspon el nom hello.txt :
/ -> home -> david -> hello.txt

El path que hem descrit és absolut perquè descriu una ruta desde el directori arrel.

També pots utilitzar un "path" relatiu:

$ pwd
/home/david
$ cat hello.txt

Quan obres una sessió de shell, aquest sempre té un directori de treball (path working directory).

Per aquest motiu pots utilitzar la ruta relativa hello.txt perqué el shell ja sap com transformar aquesta ruta en una ruta absoluta.

I per acabar, una fet bastant evident, no pot haver-hi dos fitxers amb el mateix nom en una mateixa carpeta.

Reading

6351

Java té dos classes per llegir fitxers:

  1. java.io.File és la classe que es fa servir des de que es va crear Java, i que encara es pot fer servir per motius de compatibilitat i suport a codi antic.

  2. java.nio.file.Path va apareixer en la versió 7 de Java per tal de millorar diversos aspeces com la gestió d'errors o el suport de metadata específic de diferents sistemes operatius.

Amb els mètodes toPath() i toFile() pots convertir un objecte en l'altre.

Si pots, utilitza Path com farem en aquesta activitat, entre altres coses perqué és més un intuitiu: un path pot ser un fitxer o un directori, o una altra cosa, ves a saber!

Kotlin té la classe kotlin.io.path.Path que equival a la classe java.nio.file.Path.

Com ja saps, un dels motius d'utilitzar Kotlin és que proporciona funcionalitat addicional sobre la implementació de Java mitjançant les funcions d'extensió.

readText()

Ja saps que una seqüència de bytes pot representar qualsevol cosa, entre altres coses un text.

El mètode readText() llegeix tot el fitxer i el converteix en un objecte String.

Com que el mètode readText() per defecte "llegeix" el contingut en format UTF-8, l'únic que fa és copiar el bytes a un atribut intern de l'objecte String.

Com ja saps els fitxers .kt i .java estan en format UTF-8!

Crea un fitxer reading.txt a la carpeta src amb aquest text:

Kotlin or Java,
Java or C++.

A continuació crea el fitxer read.kt:

import java.nio.file.Path

fn main() {

    val file = Path("src") / "reading.txt" // relative path
 
    val lines = file.readText()
    print(lines)
}

Com era de preveure, aquesta serà la sortida si la ruta relativa és correcta:

Kotlin or Java,
Java or C++.

Si la ruta relativa no és correcta, o el fitxer no existeix, es produirà una FileNotFoundException.

De totes maneres, el més habitual és verificar primer que el fitxer existeix.

Una excepció es fa servir quan esperes que un fitxer existeixi: l'acabes de crear, has fet un llistat del contingut d'un directori, i resulta que quan el vas a fer servir aquest no existeix perquè algún altre procés la borrat!.

import java.nio.file.Path

fn main() {

    val file = Path("src") / "reading.txt" // relative path
    if (!file.exists()) { // checks if file exists
        println("No such file: ${file}")
        return
    }

    val lines = file.readText()
    print(lines)

Observacions:

  • L'objecte Path només representa un ruta a alguna cosa: potser no existeix, potser és un directori, etc.

  • El mètode readText() ha obert i tancat de manera automàtica el fitxer.

    Per accedir a un fitxer el sistema operatiu ha de proporcionar un "file descriptor", i com que és un recurs limitat (només hi ha uns milers) s'ha d'eliminar quan ja no es fa servir.

  • Com ja saps, encara que el mètode exists() torni true, en el moment de llegir el text el fitxer potser ja no existeix i es pot produir una FileNotFoundException.

Si tenim un fitxer de text "pla" que no és UTF-8 podem especificar el charset que ha de fer servir Kotlin per transformar el bytes en bytes codificats en format UTF-8.

A continuació tens un exemple:

import kotlin.text.Charsets

val line = Path(fileName).readText(Charsets.US_ASCII)

Encara que la funció readText() és molt útil, si llegeixes un fitxer de 2 Gb tindrás un objecte String de 2 Gb en memòria.

No t'ha d'extranyar que en casos com aquest es produeixi un error de tipus OutOfMemoryError.

I com ja s'ha explicat abans un objecte File és una referècia a un fitxer, que pot o no pot existir, i que té mètodes com length() o delete() entre altres.

readLines()

La funció readLines() fa el mateix que la funció readText() excepte que en lloc de tornar un String torna una List<String> .

Com el nom indica, cada String de la llista és una línia de text:

val file = Path("src") / "reading.txt"
val lines = path.readLines()
for (line in lines){
    println(line)
}

La sortida serà:

Kotlin or Java,
Java or C++.

Potser no t'impresiona massa a no ser que t'expliquin que el final de linia es pot marcar amb \n o \r\n, i que implementis tu l'algortime.

readBytes()

Potser et pot sorprendre que la majoria d'accesos a fitxers no són a fitxers de text!

A més, el mètode readText() d'abans fa servir la funció readBytes() per obtenir els bytes.

El mètode readBytes() llegeix el fitxer i torna un ByteArray:

val lines = Path(fileName).readBytes()

Si vols pots modificar el contingut del ByteArray, però no la seva mida.

Si també vols modificar la seva mida, per exemple afegint més bytes, primer l'has de convertir a un MutableList amb el mètode toMutableList() ( i de nou a ByteArray amb toByteArray() ).

Important!. Estem modificant el bytes que estan a la memòria principal de l'ordinador, en cap cas el contingut del fitxer.

forEachLine()

Si vols llegir un fitxer gran no té sentit carregar-lo tot directament a memòria principal, és millor anar llegint per parts.

Amb una funció lambda podem processar el contingut linia per linia sense saturar la memòria principal:

val path = Path("src") / "reading.txt"
path.forEachLine { println(it) }

File separator

Amb Koltin tenim l'extensió / a la classe Path que ens petmet construir una ruta de manera segura:

val path = Path("docs") / "hello.txt"

La manera tradicional de Java és ún únic String que fa servir el caràcter / per separar els diferents elements que formen part del "path" d'un fitxer:

val path = "docs/hello.txt"

Com que la majoria de vosaltres feu servir Windows, segur que us pregunteu perquè fem servir el caràcter / enlloc de \ com fa Windows.

La resposta senzilla és que el shell de Windows ja admet /, i els entorns Linux i Mac només fan servir /.

No obstant, si vols evitar possibles problems de portabilitat, pots utilitzar la constant separator de la classe java.io.File, que té el caràcter / o \ en funció del sistema operatiu on s'estigui executant el programa:

import java.io.File

val separator = File.separator
val fileName = "src${separator}reading.txt"
val lines = File(fileName).readLines()
for (line in lines){
    println(line)
}

Writing

De la mateixa manera que hi ha moltes maneres de llegir un fitxer, tenim mètodes equivalents per escriure al fitxer.

writeText()

Suposem que vols crear un fitxer de text anomenat work.txt amb aquest contingut:

"It is awfully hard work doing nothing!"

Per fer-ho, hem de crear un Path i utilitzar la funció writeText(text: String) per escriure el contingut del fitxer:

val path = Path("work.txt")
path.writeText("It is awfully hard work doing nothing!")

Missió complerta: tenim un fitxer amb aquest contingut!

On està el fitxer? En el directori des d'on has executat el codi, si és una IDE en el directori del projecte.

I per ser més exactes?.

La variable d'entorn user.dir té aquesta informació:

val pwd = System.getProperty ("user.dir")
println(pwd)

Però aquest mètode sempre funciona?

Si el fitxer té restriccions d'accés o ja s'ha obert en un altre procés no s'escriurà res perqué es produirà una AccessDeniedException per posar un exemple.

appendText()

La funció writeText sobreescriu el contingut d'un fitxer.

Executa aquest codi:


val path = Path("work.txt")
path.writeText("Beware of bugs in the above code; I have only proved it correct, not tried it")

I com era d'esperar si obres el fitxer el contingut serà ...

Si el que vols és afegir contingut al final del fitxer has d'utilitzarla funció appendText(text: String):

val path = Path("work.txt")
path.appendText(", Donald E. Knuth said.")

Verifica que ara el contingut del fitxer és aquest:

Beware of bugs in the above code; I have only proved it correct, not tried it, Donald E. Knuth said.

writeBytes

Com era d'esperar el mètode `writeText() no escriu text, sinò els bytes que corresponen al text codificat.

I fa servir, com pots fer tu quan et fa falta, el mètode writeBytes(array: ByteArray):

val bytes = byteArrayOf(1,2,3)
Path("bytes").writeBytes(bytes)

Si vols afegir bytes a un fitxer tens la funció `appendBytes(array: ByteArray):

Path("bytes").appendBytes(byteArrayOf(5,6,7)

Jerarquies de fitxers

15171

Com ja hem explicat al principi per organitzar les dades que emmagatzemes en un disc pots organitzar els fitxers en directoris: un directori principal pot incloure altres directoris o subdirectoris, donant lloc a una jerarquia de fitxers.

Per exemple, si observes la jerarquia del sistema de fitxers a Linux, el directori arrel / inclou tots els altres fitxers i directoris, independentment del seu emmagatzematge en diferents dispositius físics. .

La classe Path proporciona funcions per treballar amb un Path quan aquest representa un directori.

Fitxers i directoris

Un fitxer és una secció de dades anomenada en un suport d'emmagatzematge. És la unitat fonamental d'interacció de dades en els sistemes operatius.

Un directori és una entitat d'un sistema de fitxers que ajuda a organitzar fitxers. Atès que un sistema de fitxers típic conté un nombre massiu de fitxers, els directoris ajuden a l'organització agrupant-los. També podeu interactuar amb ells com a Fileentitats.

Kotlin ofereix diversos mètodes per treballar amb directoris i fitxers. Per exemple, podeu utilitzar la java.iobiblioteca. Anem a repassar alguns dels mètodes més populars:

File.isDirectorycomprova si Fileés un directori.

File.isFileverifica si Fileés realment un fitxer.

File.exists()comprova l'existència del fitxer.

File.resolve(String)retorna un fitxer amb el nom Stringen un directori.

File.resolve(File)retorna un fitxer Filed'un directori.

File.createNewFile()crea un fitxer nou.

File.mkdir()crea un nou directori.

Aquí teniu un exemple per il·lustrar:

val outDir = File("outDir") println(outDir.exists()) // false outDir.mkdir() println(outDir.exists()) // true println(outDir.isDirectory) // true

val file = outDir.resolve("newFile.txt") // outDir/newFile.txt println(file.exists()) // false file.createNewFile()
println(file.exists()) // true println(file.isFile) // true

Mètodes per iterar a través de jerarquies de fitxers

Podeu navegar per la jerarquia de fitxers mitjançant els java.io.Filemètodes:

  • File.getParentFile()retorna una instància java.io.Fileque representa el directori principal d'un fitxer, o nullsi el fitxer no té cap pare (indicant que és l'arrel).

File.getParent()retorna una representació de cadena del directori principal d'un fitxer, o nullsi el fitxer no té cap pare.

File.listFiles()retorna una matriu de fitxers ubicats en un directori determinat, o nullsi la instància no és un directori.

File.list()retorna una matriu de fitxers i directoris al directori especificat per aquest nom de ruta abstracte.

El kotlin.ioproporciona mètodes especials que us permeten passar per tota la jerarquia de fitxers. Examinem tres mètodes fonamentals:

File.walk(direction): FileTreeWalkproporciona els fitxers i directoris d'aquest directori que podeu visitar; heu d'especificar la manera com repetireu (amunt o avall de l'estructura de la jerarquia).

File.walkBottomUp(): FileTreeWalkofereix els directoris i fitxers d'aquest directori que podeu visitar. Utilitza una cerca en profunditat, visitant directoris després d'haver visitat tots els seus fitxers.

File.walkTopDown(): FileTreeWalkofereix els directoris i fitxers d'aquest directori perquè els visiteu. S'utilitza una cerca en profunditat i es visiten els directoris abans que tots els seus fitxers.

La FileTreeWalkclasse està dissenyada per iterar a través d'un sistema de fitxers determinat. Us permet recórrer tots els fitxers d'un directori. El mètode iterador retorna un iterador que travessa els fitxers. Podeu recórrer aquesta estructura o convertir-la en una llista amb la funció toList().

A la secció següent, mostrarem aquests mètodes utilitzant un exemple simple de jerarquia de fitxers.

Un exemple de jerarquia

Considereu una jerarquia de fitxers amb el directori arrel anomenat Files. Conté dos subdirectoris: CompletedProjectsi Music. També contenen subdirectoris: el HelloWorlddirectori conté dos fitxers relacionats amb el projecte, el JCalculatordirectori només conté un fitxer i el Soundtracksdirectori està buit.

Aquí teniu un diagrama que representa aquesta jerarquia:

En els segments següents, utilitzarem aquesta jerarquia de fitxers com a exemple per il·lustrar com maniobrar el sistema de fitxers.

Path

Quan crees un objecte Path aquest por representar un fitxer o un directori: és la teva responsabilitat saber que representa i utilitzar les funcions corresponents.

Desde el punt de vista del sistema operatiu només hi ha fitxers: un directori és un fitxer "especial" que ell gestiona a la seva manera.

Per aquest motiu desde el punt de vista del sistema operatiu el nom deFile està molt bé, i a principis dels anys 90 quan es va dissenyar Java era un nom adequat.

Però desde el punt de vista d'un programa són dues coses molt diferents, i el que tenen en comú es que comparteixen la mateixa forma d'accés: un "path".

Funcions d'extensió

La llibreria estàndar de Kotlin proporciona algunes funcions que extenen la classe `java.nio.file.Path.

Pots trobar la llista completa a la documentació oficial.

Per exemple, podem obtenir una llista del contingut que està directament indexat per un path que representa un directori (pot ser un fitxer o un directori):

Path("~/Dowloads").listDirectoryEntries()`

Si necessites copiar un fitxer a un altre path pots utilitza la funció copyTo() tal com es mostra a continuació:

Path("source.txt").copyTo(Path("destination.txt")

Si vols moure un fitxer o un directori, pots utilitzar la funció copyTo():

Path("source.txt").moveTo(Path("destination.txt")

Si mirem més a fons la implemetació de moveTo:

public inline fun Path.moveTo(target: Path, overwrite: Boolean = false): Path {
    val options = if (overwrite) arrayOf<CopyOption>(StandardCopyOption.REPLACE_EXISTING) else emptyArray()
    return Files.move(this, target, *options)
}

Pots veure que la funcionalitat de moure ja està implementada en la classe Files, però utilitzant parametres per defecte, moveTo és molt més fàcil d'utilitzar en la majoria dels casos.

Per exemple, si volem reemplaçar un fitxer si aquest existeix, és més fàcil escriure aquest codi:

path.moveTo(dst, overwrite=true)

Que no pas aquest:

Files.move(dst, arrayOf<CopyOption>(StandardCopyOptions.REPLACE_EXISTING)

L'operador div(), que s'invoca amb el simbol / et permet especificar un path de manera molt semblant a como ho faries amb una URI.

Amb aquest operador és molt fàcil de navegar pel sistema de fitxers tal com es mostar en l'exemple que tens a continuació:

fun softDelete(path: String) {
    val fileToDelete = Path(path)

    val destinationDirectory = fileToDelete.parent / ".deleted"
    if (destinationDirectory.notExists()) {
        destinationDirectory.createDirectory()
    }

    fileToDelete.moveTo(destinationDirectory / fileToDelete.name)
}

Aquesta funció, enlloc de borrar un fitxer, el mou a un directori que té el nom .deleted.

Gestionar fitxers de manera recursiva

El paquet kotlin.io.path[https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io.path/#package-kotliniopath] inclous funcions que et permeten gestionar fitxers de manera recursiva.

Alguns exemples són:

  • walk(): per recorrer tot el contingut accesible des d'aquest path.
  • copyToRecursively(): per copiar tot el contingut accesible des d'aquest path a una altre path.

A continuació tens un exemple:

fun main() {
    val sourcePath = Path("sourceDir/file.txt")
    val destinationPath = Path("destDir/file.txt")

    // List all files in the 'sourceDir' directory
    val filesInDir = Path("sourceDir").listDirectoryEntries()
    println("Files in sourceDir: $filesInDir")

    // Copy the file from 'sourceDir' to 'destDir'
    sourcePath.copyTo(destinationPath, overwrite = true)
    println("File copied from sourceDir to destDir.")

    // Move the file within 'destDir' to a new file
    val movedPath = destinationPath.moveTo(Paths.get("destDir/movedFile.txt"), overwrite = true)
    println("File moved to: $movedPath")

    // Write text to the moved file
    movedPath.writeText("Hello, world!")
    println("Text written to file.")

    // Read the text from the file
    val text = movedPath.readText()
    println("Text read from file: $text")

    // Read the lines from the file as a collection
    val lines = movedPath.readLines()
    println("Lines read from file: $lines")

    // Walk all files and directories within 'destDir'
    val walkedPaths = Path("destDir").walk().toList()
    println("Walked paths: $walkedPaths")

    // Copy 'sourceDir' to 'copyDir' recursively
    Path("sourceDir").copyToRecursively(
        target = Paths.get("copyDir"),
        overwrite = true,
        followLinks = true,
    )
    println("Directory copied recursively.")
}

Files in sourceDir: File copied from sourceDir to destDir. File moved to: destDir/movedFile.txt Text written to file. Text read from file: Hello, world! Lines read from file: [Hello, world!] Walked paths: [destDir, destDir/movedFile.txt] Directory copied recursively.

Funciona amb jerarquies de fitxers

Obtenció del contingut del directori i el pare del directori /fitxer amb Fitxer i Ruta

La Files.listFiles()funció imprimeix el contingut (fitxers i directoris) d'un directori escollit també podeu utilitzar Path.listDirectoryEntries().

// with File val helloWorld = File("/Files/CompletedProjects/HelloWorld") val helloWorldFilesNames = helloWorld.listFiles().map{it.name} // [Doc.pdf, Reviews.txt]

val reviews = File("/Files/CompletedProjects/HelloWorld/Reviews.txt") val reviewsFiles = reviews.listFiles() // null

val soundtracks = File("/Files/Music/SoundTracks") val soundtracksFiles = soundtracks.listFiles() // []

// with Path val helloWorld = Path("/Files/CompletedProjects/HelloWorld") val helloWorldFilesNames = helloWorld.listDirectoryEntries().map { it.name } // [Doc.pdf, Reviews.txt]

val reviews = Path("/Files/CompletedProjects/HelloWorld/Reviews.txt") val reviewsFiles = if (reviews.isDirectory()) reviews.listDirectoryEntries() else emptyList() // []

val soundtracks = Path("/Files/Music/SoundTracks") val soundtracksFiles = if (soundtracks.isDirectory()) soundtracks.listDirectoryEntries() else emptyList() // []

reviewsFilesés nullperquè reviewsno és un directori en absolut i no pot incloure altres fitxers o subdirectoris. D'altra banda, soundtracksés un directori sense fitxers, de manera que el resultat és una matriu buida.

La propietat File.parentor Path.parentus dóna el nom del pare d'un fitxer o directori.

La propietat File.parentFileor Path.parentFileproporciona el pare d'un fitxer o directori com a File.

// with File val files = File("/Files") print(files.parent) // the result is "/" print(files.parentFile.name) // the result is ""

val reviews = File("/Files/CompletedProjects/HelloWorld/Reviews.txt") print(reviews.parent) // the result is "/Files/CompletedProjects/HelloWorld" print(reviews.parentFile.name) // the result is "HelloWorld"

val root = File("/") print(root.parent) // the result is "null" print(root.parentFile.name) // throws java.lang.NullPointerException

// with Path val files = Path("/Files") println(files.parent) // the result is "/" println(files.parent.fileName) // the result is ""

val reviews = Path("/Files/CompletedProjects/HelloWorld/Reviews.txt") println(reviews.parent) // the result is "/Files/CompletedProjects/HelloWorld" println(reviews.parent.fileName) // the result is "HelloWorld"

val root = Path("/") println(root.parent) // the result is "null" println(root.parent.fileName) // This line won't be executed because root.parent is null

Si un directori és l'arrel d'una jerarquia de fitxers, obtindreu null. Aneu amb compte de no activar excepcions!

Iteració en ambdues direccions

Abans hem esmentat el File.walk(direction)mètode per passar per la jerarquia de fitxers . L' directionatribut descriu la manera com travessem la nostra jerarquia de fitxers; pot ser FileWalkDirection.BOTTOM_UPo bé FileWalkDirection.TOP_DOWN. A més, podeu utilitzar Path.walk()i utilitzar la seqüència o l'opció iterable per realitzar la direcció.

// with File val files: File = File("/Files") println("TOP_DOWN: ") files.walk(FileWalkDirection.TOP_DOWN).forEach

println("BOTTOM_UP: ") files.walk(FileWalkDirection.BOTTOM_UP).forEach

// with Path val dir: Path = Path("/Files") println("TOP_DOWN: ") dir.walk().forEach

println("BOTTOM_UP: ") dir.walk().asIterable().reversed().forEach

La sortida d'aquest programa serà:

TOP_DOWN: /Files /Files/Music /Files/Music/SoundTracks /Files/CompletedProjects /Files/CompletedProjects/HelloWorld /Files/CompletedProjects/HelloWorld/Doc.pdf /Files/CompletedProjects/HelloWorld/Reviews.txt /Files/CompletedProjects/JCalculator /Files/CompletedProjects/JCalculator/Doc.pdf BOTTOM_UP: /Files/Music/SoundTracks /Files/Music /Files/CompletedProjects/HelloWorld/Doc.pdf /Files/CompletedProjects/HelloWorld/Reviews.txt /Files/CompletedProjects/HelloWorld /Files/CompletedProjects/JCalculator/Doc.pdf /Files/CompletedProjects/JCalculator /Files/CompletedProjects /Files

Podeu arribar al mateix resultat mitjançant aquests mètodes:

File.walkBottomUp()(el mateix que File.walk(FileWalkDirection.BOTTOM_UP));

File.walkTopDown()(el mateix que File.walk(FileWalkDirection.TOP_DOWN)).

Per tant, aquests tres mètodes ens permeten recórrer tota l'estructura de fitxers de forma recursiva.

Treball amb jerarquies

Suposant que tenim una instància de java.io.Filenamed completedProjects, que fa referència al CompletedProjectsdirectori. Ara obtindrem els dos subdirectoris que contenen les dades del projecte.

// with File val completedProjects: File = File("/Files/CompletedProjects") val projects = completedProjects.walk() projects.maxDepth(1) // HelloWorld and JCalculator

// with Path val completedProjects: Path = Path("/Files/CompletedProjects") val projects = Files.walk(completedProjects, 1)

No es promet l'ordre específic dels fitxers de la matriu. Per trobar el HelloWorldprojecte, recorrerem l'arbre de fitxers:

val helloWorldProject: File = projects.first

També podeu obtenir el HelloWorlddirectori utilitzant el File.listFiles()mètode:

val helloWorldProject: File = completedProjects.listFiles().first

Només suposem que no és nullper simplificar el nostre codi amb finalitats educatives. Però sempre és millor comprovar inicialment, ja que la jerarquia real pot canviar.

Ara, intentem navegar fins al Soundtracksdirectori a partir del Reviews.txtfitxer:

val reviews = File("/Files/CompletedProjects/HelloWorld/Reviews.txt") var parent = reviews.parentFile while (parent.name != "Files"){ parent = parent.parentFile }

val soundTracks: File = parent.walkTopDown().first

Còpia de fitxers

Si necessiteu copiar un fitxer, utilitzeu la copyTo()funció:

// with File val fileIn = File("newFile.txt") val fileOut = File("copyNewFile") fileIn.copyTo(fileOut)

// with Path val fileIn = Path("newFile.txt") val fileOut = Path("copyNewFile.txt") fileIn.copyTo(fileOut)

Tingueu en compte que si necessiteu sobreescriure el fitxer, haureu d'incloure un overwriteparàmetre:

// With File val fileIn = File("newFile.txt") val fileOut = File("copyNewFile") fileIn.copyTo(fileOut, overwrite = true)

// With Path val fileIn = Path("newFile.txt") val fileOut = Path("copyNewFile.txt") fileIn.copyTo(fileOut, overwrite = true)

Per copiar tot un directori de forma recursiva, podeu utilitzar la copyRecursively()funció:

// with File val fileIn = File("outDir") val fileOut = File("newDir") fileIn.copyRecursively(fileOut)

// with Path val pathIn = Path("outDir") val pathOut = Path("newDir") pathIn.copyToRecursively(pathOut)

Recordeu que si necessiteu sobreescriure carpetes i fitxers, també heu d'introduir un overwriteparàmetre o seguir els enllaços (només a Path) amb followLinks l'opció:

// with File val fileIn = File("outDir") val fileOut = File("newDir") fileIn.copyRecursively(fileOut, overwrite = true)

// with Path val pathIn = Path("outDir") val pathOut = Path("newDir") pathIn.copyToRecursively(pathOut, overwrite = true, followLinks = true)

Activitats

1.- Què passa quan fas servir src.copyTo(dst, overwrite = true)?

  1. Esborra dst i després copia src a la mateixa ubicació
  2. Mou src a dst sense sobreescriure
  3. Copia src a dst sense sobreescriure els fitxers existents
  4. Copia src a dst i sobreescriu els fitxers existents

  1. Copia src a dst i sobreescriu els fitxers existents

2.- Modifica el codi per mostrar el directori principal del fitxer reviews.

func main(args: Array<String>) {
   val reviews = File("/home/david/reviews.txt")
}

func main(args: Array<String>) {
   val reviews = File("/home/david/reviews.txt")
   print(reviews.parent)
}

3.- Quin mètode pots utilitzar per recórrer una jerarquia de fitxers de baix a dalt?

  1. File.walk(FileWalkDirection.TOP_DOWN)
  2. File.walkBottomUp()
  3. File.walk(FileWalkDirection.BOTTOM_UP)
  4. File.walkTopDown()

  1. `File.walkBottomUp()

4.- Reordena el codi per copiar newFile.txt a copyNewFile.txt; sobreescriu si cal.

func main(args: Array<String>) {
   val fileIn = File("input.txt")
   val out = File("output.txt")
   in.copyTo(out, overwrite = true)
}

5.- Què espereu helloWorldFilesNamesque sigui la sortida al fragment de codi següent?

val helloWorld = File("/Files/CompletedProjects/HelloWorld") val helloWorldFilesNames = helloWorld.listFiles().map

["Doc.pdf", "Revisions.txt"] nul [] X ["HelloWorld"]