CodeGym /Kurslar /Kotlin SELF /Mutleq ve nisbi yollar

Mutleq ve nisbi yollar

Kotlin SELF
Səviyyə, Dərs
Mövcuddur

1. Giris

Biz “fayli oxu” dedikde, eslinde bunu deyirik: “emeliyyat sistemine bu faylin deqiq harada yerlesdiyini izah etmek lazimdir”. Bunun ucun de yol (path) movcuddur — fayl/qovluq unvanini fayl sisteminde tesvir eden setr (ve ya obyekt). Praktikada yol cox vaxt "data/input.txt" veya "C:\\Projects\\App\\data\\input.txt" kimi gorunur — ve maceralar da mehz burada baslayir.

Problem ondadir ki, eyni yol setri, proqramin haradan ise salindigindan asili olaraq diskde ferqli yerleri ifade ede biler. Ona gore de yol movzusu — bezdirici detal deyil, ozune bir-nece saat heyat ve bir eseb tikini qenaet etmek usuludur. Cunki “fayl tapilmadi” xetasi adeten faylin olmamasini yox, proqramin sehv yere baxmasini bildirir.

Mutleq yol

Mutleq yol — faylin yerlesdiyi yeri fayl sisteminin “koku”nden baslayaraq tam tesvir eden yoldur. Siz harada olsaniz da, proqrami haradan ise salmis olsaniz da ondan asili deyil. Mutleq yol duzgundurse, fayl ya hemin noqte-de movcuddur, ya da yox — hec bir “sehr” ve “mende kompyuterde islemişdi axı” yoxdur.

Windows-da mutleq yol adeten disk herfi ile baslayir (meselen, C:\...), macOS/Linux-da ise /... ile. Amma bize daha vacib olan fikir budur: mutleq yol — seher, kuce, bina ve menzil olan unvan kimidir, “teqriben evin yaxinligi” deyil.

Kotlin/JVM-de biz tez-tez mutleq yollarla java.io.File vasitəsile isleyirik, ve en faydali diaqnostik triklerden biri — absolutePath-e baxmaqdir.

import java.io.File

fun main() {
    val f = File("data/input.txt")

    println("relative = ${f.path}")         // relative = data/input.txt
    println("absolute = ${f.absolutePath}") // absolute = .../data/input.txt
}

Bu numunede fayl hetta movcud olmaya da biler — bu normaldir. File("...") burada “fayli acmir”, sadəcə yolu saxlayir ve onu muxtelif formalarda gostere bilir.

Nisbi yol

Nisbi yol — tam unvan olmayan, “hardansa” hesablanan yoldur. Ve mehz bu “hardansa” — butun sirrin acari olur.

Meselen, "data/input.txt" yolu bele demekdir: “prosesin cari isci direktoriyasinda data qovlugunu tap, onun icinde ise input.txt faylini”.

Yəni nisbi yol, “kunc arxasindaki magazin” ifadəsi kimidir. Biz biləndə işləyir ki, hansi kuncun arxasindan danisilir. Siz “teleport” edib basqa rayona kecdinizse (proqrami basqa cur ise saldinizsa) — “kunc arxasi” bir anda tamam basqa kunc olur.

Nisbi yollar cox rahatlidir, cunki layiheni dasinabilən edir: siz melumatlari layihenin yaninda saxlaya, qovluq strukturunu commit ede, koda C:\Users\... kimi seyleri “tikir-tikir” yazmaya bilersiniz. Amma onlarla duzgun “dostlasmaq” lazimdir: her zaman xatirlamaq ki, indi isci direktoriya hansidir.

2. Isci direktoriya ve faylin axtarisi

Isci direktoriya

Isci direktoriya — nisbi yollarin serh olundugu qovluqdur. Ve bu, IDE-de her sey isleyir, amma terminaldan ise salanda — “fayl tapilmadi” (ve ya eksine) kimi hallarin en tez-tez rast gelinen sebeblerinden biridir.

Cari olaraq “biz haradayıq?” sualini yoxlamaq maksimum sade edilir:

import java.io.File

fun main() {
    val wd = File(".").absolutePath
    println("Working dir = $wd") // Working dir = ... (absolute path of current folder)
}

File(".") “cari qovluq” demekdir. absolutePath ise JVM-in bu ise salmada cari isci direktoriyanı konkret olaraq ne hesab etdiyini gormek usuludur.

Niye bu qeder vacibdir? Cunki siz tam duzgun olaraq File("data/input.txt") yaza, data qovlugunun olduguna emin ola bilersiniz, amma proqram yenə de “tapilmadi” deyə biler — eger o, basqa direktoriyadan ise dusubse ve data-ni sizin gozlediyiniz yerde yox, basqa yerde axtarirsa.

Niye IDE-de ve terminalda ferqli ola biler

Klassik bir senari var.

Siz layihe yaradırsınız, struktur bele dir:

MyApp/
  src/
  data/
    input.txt

Siz IDE-den ise salirsiniz — ve IDE isci direktoriyanı MyApp/ kimi teyin ede biler. Nisbi "data/input.txt" yolu MyApp/data/input.txt kimi resolve olunur — her sey yaxsidir.

Sonra basqa cur ise salirsiniz: meselen, terminaldan MyApp/ qovlugundan yox, valideyn qovluqdan ve ya tamam basqa direktoriyadan. Onda "data/input.txt" yolu basqa_qovluq/data/input.txt kimi resolve olunur — ve tebiidir ki, orada fayl yoxdur.

Eyni mentiq yalnız proqramin ise salinmasinda deyil, nisbi yollarla fayl oxuyan istənilən alətdə rast gəlinir: nisbi yollar demek olar ki, hemişe prosesin cari isci direktoriyasindan hesablanir.

Nəticə sadədir: nisbi yol — “layihe daxilinde yol” deyil, “ise salinma isci direktoriyasindan olan yol”dur.

“Fayl tapilmadi” diaqnostikasi

Siz FileNotFoundException gorəndə və ya sadəcə proqramın “fayli gormediyini” anlayanda, yeni baslayan insanin ilk reaksiyasi — “niye Kotlin fayli tapa bilmir, axı buradadır!” olur. Bir az daha tecrubeli insanin reaksiyasi ise — “proqram fayli harada axtarir?” olur.

Ve budur, diaqnostika ucun qizil mini-sablon:

import java.io.File

fun main() {
    val relative = "data/input.txt"
    val f = File(relative)

    println("Working dir = ${File(".").absolutePath}")
    println("Trying file = ${f.absolutePath}")
}

Məna ondadır ki, siz iki vacib ipucu alırsınız:

  • Working dir — nisbi yollar ucun istinad noqtesi
  • Trying file — proqramin real olaraq baxdigi mutleq yol

Bundan sonra “sehr” yox olur: siz proqramin fayli hansi unvanla axtardigini birbasa gorursunuz. Ve sonra ortaya cixir ki, siz .../MyApp/data/input.txt gozleyirdiniz, amma .../MyApp/src/data/input.txt alindi veya .../SomeOtherFolder/data/input.txt.

Mini-sxem: nisbi yol nece mutleq yola cevrilir

Soz-soz, amma bir defe bunu proses kimi gormek faydalidir. Serti olaraq:

flowchart TD
    A["Nisbi yol: data/input.txt"] --> B["Prosesin isci direktoriyasi: WD"]
    B --> C["Birlestirme: WD + data/input.txt"]
    C --> D["Mutleq yol: /.../WD/data/input.txt"]

Bu sxem OS səviyyəsində “sadə setr toplanmasi” demek deyil, amma bizim ucun məna tam da budur: nisbi yol mutleq yola isci direktoriya vasitəsile cevrilir.

3. File yol obyekt kimi ve yollarin yigimi

File(path) — bu yol obyektidir

File sozu adami yanilda biler: ele bil File("data/input.txt") “fayli acir”. Eslinde Java/Kotlin-da File ilk novbede yol obyektidir. O, yol setrini saxlayir ve bacarir:

  • path-i vermek (sizin teyin etdiyiniz kimi),
  • absolutePath-i vermek (OS bu yolu nece gorecek),
  • sonra — movcudlugu yoxlamaq, oxumaq, yazmaq ve s. kimi emeliyyatlar.

Yəni File “Unvan” obyektine bənzəyir, “Baglamanin mehzmunu”na yox. Siz File("data/input.txt") yarada bilersiniz, fayl movcud olmasa bele: bu sadəcə “istifade etmek istediyiniz unvan”dir.

Yollari setrlerle “yapisdirmaq”dan nece qacinmali

Yeni baslayan adam fayllarla islemeye baslayanda, adeten bele etmek isteyi yaranir:

val path = "data/" + "input.txt"

Bu hələ nisbətən zərərsizdir. Sonra bu baslayir:

val path = "data" + "/" + "input.txt"

Sonra bir də baxırsan Windows, əks sleşlər, ikiqat sleşlər, boşluqlar — ve her sey arxeoloji qazişlara cevrilir.

Bu leksiyanin ozunde bele (hele sonra olacaq Path.of(...)-den once) daha seliqeli etmek olar: yolu File(parent, child) ile yigmaq. Bu “sehrli” funksiya deyil, sadəcə hisseleri daha tehlukesiz birlesdirmek usuludur.

import java.io.File

fun main() {
    val dir = File("data")
    val file = File(dir, "input.txt")

    println(file.path)         // data/input.txt
    println(file.absolutePath) // .../data/input.txt
}

Beləliklə kod “data direktoriyasinin icinde input.txt fayli” kimi oxunur, “sehrbazliqla yigilmis setr” kimi yox.

4. Yollari tətbiqə daxil edirik ve cixisi faydali edirik

“Layihenin melumatlari haradadır?”

Numuneler abstrakt olmasin deyə, praktiki CLI-tətbiqimizi tesəvvür edək: deyək ki, bu kiçik xərclər trekeridir. Fayllara qeder her sey yaddaşda idi, indi isə melumatlari data/ qovlugunda saxlamaq istəyirik.

Bu gun hele oxumuruq ve yazmiriq (bunlar novbeti leksiyalarda olacaq), amma vacib addim ata bilərik: proqramin melumat faylini “harada gozlediyini” mərkəzləşdirmək ve diaqnostika əlavə etmək.

import java.io.File

private fun dataFile(): File {
    return File("data/expenses.txt")
}

fun main() {
    val f = dataFile()

    println("Working dir = ${File(".").absolutePath}")
    println("Data file (relative) = ${f.path}")         // Data file (relative) = data/expenses.txt
    println("Data file (absolute) = ${f.absolutePath}") // Data file (absolute) = .../data/expenses.txt
}

Burada biz eyni anda iki yaxsi sey etdik.

Birincisi, faylin yolu indi bir yerde saxlanilir, ve sonra (real oxuma/yazmaya baslayanda) “bir yerde data/expenses.txt, basqa yerde ./data/expenses.txt, ucuncu yerde ise umumen data\expenses.txt” kimi vəziyyətləri tutub qovalamayacağıq.

Ikincisi, Working dir ve fayla gedən mutleq yolun cixishi — bu, daxili diaqnostikadir. “Fayl tapilmadi” alanda, proqramın hara baxdığını artıq goreceksiniz.

Yolu elə çap edək ki, onu əl ilə yoxlamaq olsun

Yollari çap edəndə, onları elə çap etmək faydalidir ki, insan onu birbasa kopyalayıb yapışdıra bilsin, fayl meneceri/terminalda acsin ve yoxlasin. Yəni daha yaxşısı mutleq yolu və yanında qısa izahı çap etməkdir.

import java.io.File

fun main() {
    val reportFile = File("data/reports/today.txt")

    println("I will look for report at:")
    println(reportFile.absolutePath)
    // .../data/reports/today.txt
}

Beli, bu həddən artıq sadə gorunur. Amma bu elə haldır ki, sadəlik zəiflik əlaməti deyil — bu, “gizli” yolu debuq etməyi istəmədiyinizin əlamətidir.

5. Yollarla isleyende tipik sehvler

Sehv №1: nisbi yolun kod olan qovluqdan hesablandigini dusunmek.
Cox mentiqli fərziyyədir, cunki beyin-de layihə = src olan qovluqdur. Amma JVM bele dusunmur. Nisbi yol ise salinmanin isci direktoriyasindan hesablanir; bu da IDE/terminal/Run Configuration sazlamalarindan asılıdır. En yaxsi “dərman” — problem olanda File(".").absolutePath-i çap etməkdir.

Sehv №2: “layihe daxilinde yol” ile “ise salinma zamani yol”u qarisdirmak.
data/ qovlugu src/-un yaninda ola biler, amma isci direktoriya src/-e işarə edirsə, onda data/input.txt fayli src/data/input.txt kimi gozlenilecek. Kənardan baxanda yol “duzgun” gorunur, amma o, yalnız sizin beyninizde duzgundur. absolutePath vasitəsile mutleq yol tez bir zamanda reallığa qaytarir.

Sehv №3: yolları setrlerle yapisdirmaq ve qeribe sleşlerle uzlesmek.
Setr konkatenasiyasi tez bir zamanda ikiqat ayiricilara, buraxilmis /-lere, ve “Windows-da duzeltdim — Linux-da sinirdi” halina getirir. Hele siz Path.of(...) istifade etmirsinizse bele, yolu File(parent, child) ile yigmaq kodu daha etibarli ve oxunaqli edir.

Sehv №4: yolu diaqnostika etmeden fayl oxunmasini diaqnostika etmeye calismaq.
Cox vaxt yeniler dərhal try/catch-e girir, istisnani tutur ve “oxuma xetasi” cap edir. Amma isci direktoriyani ve mutleq yolu cap etmədən bu mesaj demek olar ki, faydasizdir: siz “tapilmadi”ni bilirsiniz, amma “harada axtardilar”i bilmirsiniz. Duzgun dusunce ardicilliqi: once “hara baxiriq?”i aydinlasdirmaq, sonra oxumani arashdirmaq.

Sehv №5: fayla gedən yolu butun layihə boyunca “yaymaq”.
"data/expenses.txt" setri bes yerde kecəndə, bir gun onu dort yerde deyisdirib bir bug alacaqsiniz; o da mistika kimi gorunecek: “bazen kohneni oxuyur, bazen yenisine yazir”. Daha sakit variant — yolun bir yerde (funksiya veya konstant) yaradılması, sonra ise kod boyunca File kimi ötürülməsidir.

1
Tapşırıq
Kotlin SELF, səviyyə, dərs
Bağlanıb
Yol naviqatoru
Yol naviqatoru
1
Tapşırıq
Kotlin SELF, səviyyə, dərs
Bağlanıb
İstifadəçidən gələn yol
İstifadəçidən gələn yol
1
Tapşırıq
Kotlin SELF, səviyyə, dərs
Bağlanıb
Ünvan yığan
Ünvan yığan
1
Tapşırıq
Kotlin SELF, səviyyə, dərs
Bağlanıb
Xəta hesabatı şablonu
Xəta hesabatı şablonu
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION