Implementarea cat cu Kotlin Native

  • 4 minute de citit
  • 9 exemple de cod în Kotlin și Shell
  • 2 imagini

Disponibil și în English

Pe această pagină

imagine de copertă a articolului

Introducere

Salut! 👋

În acest articol vom implementa utilitarul de linie de comandă cat, folosit în multe sisteme asemănătoare Unix, cum ar fi Linux și macOS. Pentru implementare vom folosi Kotlin și vom renunța la Java Virtual Machine (JVM). Vom construi un binar direct cu Kotlin Native. La final vom obține un executabil pe care îl putem rula simplu pe un calculator Linux sau macOS.

Ce este cat?

Utilitarul cat are scopul de a concatena fișiere. Îl poți folosi tastând cat file.txt și va afișa conținutul fișierului în terminal. Are și mai multe opțiuni, precum: usage: cat [-belnstuv] [file ...], care sunt documentate în paginile de manual.

Kotlin și Kotlin Native

imagine despre kotlin din articol

Kotlin este un limbaj de programare modern, lansat pentru prima dată de JetBrains în 2011 și susținut oficial mai târziu de Google pentru dezvoltarea de aplicații Android. Este proiectat să fie complet interoperabil cu Java, rezolvând în același timp unele dintre problemele frecvente ale Java, făcându-l mai concis, mai expresiv și mai plăcut de folosit.

Kotlin Native este o tehnologie pentru compilarea codului Kotlin în binare native, care pot rula fără o mașină virtuală precum Java Virtual Machine (JVM). Face parte din Kotlin Multiplatform, care este o tehnologie concepută pentru a simplifica dezvoltarea multiplatformă. Poți folosi Kotlin Multiplatform pentru a dezvolta software pentru Server, Android, iOS, Desktop și Web - JavaScript.

Implementarea cat cu Kotlin Native

Ca să urmărești pașii, îți recomand să folosești mediul de dezvoltare integrat (IDE) IntelliJ IDEA Community sau Professional.

Clonarea șablonului Kotlin Native

Începe prin a clona șablonul de proiect Kotlin Native. Eu am pregătit totul, ca să poți începe imediat.

git clone https://github.com/Kotlin/kmp-native-wizard k-cat

Apoi deschide proiectul în IDE.

Modificarea șablonului

Am început modificarea șablonului prin schimbarea numelui proiectului în k-cat.

Deschide .../IdeaProjects/k-cat/settings.gradle.kts și setează rootProject.name

rootProject.name = "k-cat"

Și cam atât am nevoie deocamdată ca să dăm proiectului nostru un nume potrivit! 🔩

Implementarea cat

Utilitarul cat este un program complex. Vom implementa o versiune simplificată, care are următoarele cerințe.

Cerințe

Programul acceptă un parametru de tip șir obligatoriu, implicit, care reprezintă calea către fișier, și un parametru opțional de tip flag, -n.

Va deschide fișierul indicat prin parametrul cu calea și va afișa conținutul său pe ecranul terminalului.

Când este apelat cu parametrul opțional -n, va afișa numărul liniei înainte de conținutul fișierului.

Exemplu de utilizare:

./k-cat.kexe -n file.txt0  Hello,1  From nuculabs.dev 

Implementarea

Pentru a începe implementarea, deschide: .../IdeaProjects/k-cat/src/nativeMain/kotlin/Main.kt.

Citirea argumentelor din linia de comandă

Pentru a citi argumentele din linia de comandă, trebuie să adăugăm parametrul args: Array<String> la funcția main. Array-ul args va conține argumentele din linia de comandă transmise programului.

fun main(args: Array<String>) { //...}

Analizarea argumentelor din linia de comandă

Pentru a analiza argumentele din linia de comandă, am folosit construcții simple din Kotlin.

Codul se explică aproape singur. Mai întâi căutăm argumentul obligatoriu filePath, apoi îi stocăm valoarea într-o variabilă. În al doilea rând, căutăm argumentul opțional în forma scurtă -n și în forma lungă --numbering, și dacă este găsit în array-ul argumentelor din linia de comandă, setăm variabila booleană numbering pe true.

    // Preia argumentul obligatoriu al căii fișierului    val fileArgs = args.filterNot { it.startsWith("-") }    if (fileArgs.size != 1) {        println("Usage: missing path to file: ./k-cat <file>")        return    }    val filePath = fileArgs.first()    // Analizează argumentul opțional `-n`    var numbering = false;    args.forEach {        when (it) {            "-n" -> numbering = true            "--numbering" -> numbering = true        }    }

Citirea fișierelor

Citirea fișierelor se poate face folosind o funcție deja disponibilă sau cu ajutorul unei biblioteci. Eu am ales a doua variantă și am adăugat dependența okio în proiect.

Pentru a adăuga o dependență, deschide .../IdeaProjects/k-cat/build.gradle.kts și modifică sourceSets astfel:

kotlin {    //...    sourceSets {        val nativeMain by getting        val nativeTest by getting        val commonMain by getting {            dependencies {                implementation("com.squareup.okio:okio:3.7.0")            }        }    }}

Apoi poți importa și folosi biblioteca în funcția main pentru a citi fișiere. De exemplu:

import okio.FileSystemimport okio.Path.Companion.toPathimport okio.useimport okio.bufferFileSystem.SYSTEM.source("file.txt".toPath()).use { fileSource ->    fileSource.buffer().use { bufferedFileSource ->        while (true) {          val line = bufferedFileSource.readUtf8Line() ?: break          // fă ceva cu linia        }    }}

Implementarea unui k-cat simplu

Implementarea este acum simplă, deoarece știm cum să analizăm argumentele din linia de comandă și cum să citim fișiere.

Următorul bloc de cod implementează k-cat. Citește toate liniile dintr-o cale de fișier și, dacă numbering este true, afișează numărul liniei și linia, altfel afișează doar linia.

    FileSystem.SYSTEM.source(filePath.toPath()).use { fileSource ->        fileSource.buffer().use { bufferedFileSource ->            var number = 0            while (true) {                val line = bufferedFileSource.readUtf8Line() ?: break                if (numbering) {                    val formattedNumber = number.toString().padEnd(2, ' ')                    println("$formattedNumber $line")                } else {                    println(line)                }                number += 1            }        }    }

Concluzie

Pe scurt, Kotlin Native extinde capabilitățile Kotlin, permițând dezvoltatorilor să compileze cod Kotlin în binare native pentru o varietate de platforme, inclusiv desktop, web, Android, iOS și sisteme embedded. Această extensie oferă performanța aplicațiilor native, păstrând în același timp expresivitatea și funcțiile de siguranță ale Kotlin.

Mulțumesc că ai citit! Sărbători fericite! 🎄✨

Lectură suplimentară / Referințe

Codul complet: Main.kt

import okio.FileSystemimport okio.Path.Companion.toPathimport okio.useimport okio.bufferfun main(args: Array<String>) {    // Preia argumentul obligatoriu al căii fișierului    val fileArgs = args.filterNot { it.startsWith("-") }    if (fileArgs.size != 1) {        println("Usage: missing path to file: ./k-cat <file>")        return    }    val filePath = fileArgs.first()    // Analizează argumentul opțional `-n`    var numbering = false;    args.forEach {        when (it) {            "-n" -> numbering = true            "--numbering" -> numbering = true        }    }    FileSystem.SYSTEM.source(filePath.toPath()).use { fileSource ->        fileSource.buffer().use { bufferedFileSource ->            var number = 0            while (true) {                val line = bufferedFileSource.readUtf8Line() ?: break                if (numbering) {                    val formattedNumber = number.toString().padEnd(2, ' ')                    println("$formattedNumber $line")                } else {                    println(line)                }                number += 1            }        }    }}