JavaRush /Java блогы /Random-KK /Java файлдары, жол

Java файлдары, жол

Топта жарияланған
Сәлеметсіз бе! Бүгін біз файлдармен және каталогтармен жұмыс істеу туралы айтатын боламыз. Сіз файлдардың мазмұнын қалай басқару керектігін білесіз: бізде бұған арналған көптеген сабақтар болды :) Менің ойымша, сіз осы мақсаттар үшін қажет бірнеше сыныптарды оңай есте сақтай аласыз. Бүгінгі дәрісте біз файлдарды басқару - құру, атын өзгерту және т.б. туралы арнайы айтатын боламыз. Java 7-ге дейін мұндай операциялардың барлығы File. Оның жұмысы туралы мына жерден оқи аласыз . Бірақ Java 7-де тілді жасаушылар файлдармен және каталогтармен жұмыс істеу тәсілін өзгертуге шешім қабылдады. FileБұған сыныптың бірқатар кемшіліктері себеп болды . copy()Мысалы, оның файлды бір орыннан екінші орынға көшіруге мүмкіндік беретін әдісі болмады (бұл анық қажет болып көрінетін мүмкіндік). Сонымен қатар, сыныпта - мәндерін Fileқайтаратын көптеген әдістер болды boolean. Қате орын алса, мұндай әдіс қателерді диагностикалауды және олардың себептерін анықтауды өте қиын ететін ерекше жағдайды тастамай, жалған мәнін қайтарады. Бір сыныптың орнына File3 сынып пайда болды: Paths, Pathжәне Files. Дәлірек айтсақ, Pathбұл сынып емес, интерфейс. Олардың бір-бірінен қалай ерекшеленетінін және олардың әрқайсысы не үшін қажет екенін анықтайық. Ең оңай нәрседен бастайық - Paths.

Жолдар

Pathsбір статикалық әдісі бар өте қарапайым класс get(). Ол тек өткізілген жолдан немесе URI түріндегі нысанды алу үшін жасалған Path. Оның басқа функционалдығы жоқ. Міне, оның жұмысының мысалы:
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {

   public static void main(String[] args) {

       Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
   }
}
Ең қиын сынып емес, солай ма? :) Ал, бізде нысан түрі бар болғандықтан , оның не екенін және не үшін қажет екенін Pathанықтайық :)Path

Жол

Path, жалпы алғанда, қайта жасалған аналогы болып табылады File. Онымен жұмыс істеу -ге қарағанда әлдеқайда оңай File. Біріншіден , одан көптеген утorталық (статикалық) әдістер жойылып, сыныпқа көшірілді Files. Екіншіден , Pathәдістердің қайтару мәндері реттелген. Сыныпта Fileәдістер мынаны String, мынаны boolean, мынаны қайтарды File- оны анықтау оңай болмады. Мысалы, getParent()жол ретінде ағымдағы файлдың ата-аналық жолын қайтаратын әдіс болды. Бірақ сонымен бірге getParentFile()бірдей нәрсені қайтаратын әдіс болды, бірақ нысан түрінде File! Бұл артық екені анық. Сондықтан интерфейсте файлдармен жұмыс істеудің Pathәдісі және басқа әдістері жай ғана нысанды қайтарады . Көптеген опциялар жоқ - бәрі оңай және қарапайым. Оның қандай пайдалы әдістері бар ? Міне, олардың кейбіреулері және олардың жұмысының мысалдары: getParent()PathPath
  • getFileName()— жолдан файл атын қайтарады;

  • getParent()— ағымдағы жолға қатысты «ата-ана» каталогын қайтарады (яғни каталогтар ағашында жоғары тұрған каталог);

  • getRoot()— «түбір» каталогын қайтарады; яғни каталогтар ағашының жоғарғы жағында орналасқан;

  • startsWith(), endsWith()— жолдың өткен жолдан басталатынын/аяқталатынын тексеріңіз:

    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           Path fileName = testFilePath.getFileName();
           System.out.println(fileName);
    
           Path parent = testFilePath.getParent();
           System.out.println(parent);
    
           Path root = testFilePath.getRoot();
           System.out.println(root);
    
           boolean endWithTxt = testFilePath.endsWith("Desktop\\testFile.txt");
           System.out.println(endWithTxt);
    
           boolean startsWithLalala = testFilePath.startsWith("lalalala");
           System.out.println(startsWithLalala);
       }
    }

    Консоль шығысы:

    testFile.txt
    C:\Users\Username\Desktop
    C:\
    true
    false

    Әдістің қалай жұмыс істейтініне назар аударыңыз endsWith(). Ол ағымдағы жолдың өткен жолмен аяқталуын тексереді . Ол таңбалар жиынында емес , жолда .

    Осы екі қоңыраудың нәтижелерін салыстырыңыз:

    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath.endsWith("estFile.txt"));
           System.out.println(testFilePath.endsWith("Desktop\\testFile.txt"));
       }
    }

    Консоль шығысы:

    жалған
    шын

    endsWith()Тек таңбалар жиынын емес, әдіске толық жолды беру керек: әйтпесе ағымдағы жол шын мәнінде таңбалар тізбегімен аяқталса да нәтиже әрқашан жалған болады («estFile.txt жағдайындағыдай) ” жоғарыдағы мысалда).

    Сонымен қатар, абсолютті (толық) және салыстырмалы жолдармен жұмыс істеуді жеңілдететін әдістер тобы барPath .

Мына әдістерді қарастырайық:
  • boolean isAbsolute()— ағымдағы жол абсолютті болса, true мәнін қайтарады:

    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath.isAbsolute());
       }
    }

    Консоль шығысы:

    рас

  • Path normalize()— ағымдағы жолды «қалыпты етеді», одан қажет емес элементтерді алып тастайды. Танымал операциялық жүйелер жолдарды белгілеу кезінде «.» таңбаларын жиі қолданатынын білуіңіз мүмкін. («ағымдағы каталог») және «..» (тектік каталог). Мысалы: “ ./Pictures/dog.jpg ” біз қазір орналасқан каталогта Суреттер қалтасы және оның ішінде “dog.jpg” файлы бар екенін білдіреді.

    Солай. Егер сіздің бағдарламаңызда «.» қолданатын жол болса. немесе «..», әдіс normalize()оларды жояды және оларды қамтымайтын жолды алады:

    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
    
           Path path5 = Paths.get("C:\\Users\\Java\\.\\examples");
    
           System.out.println(path5.normalize());
    
           Path path6 = Paths.get("C:\\Users\\Java\\..\\examples");
           System.out.println(path6.normalize());
       }
    }

    Консоль шығысы:

    C:\Users\Java\мысалдар
    C:\Users\мысалдар

  • Path relativize()— ағымдағы және өткен жол арасындағы салыстырмалы жолды есептейді.

    Мысалы:

    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath1 = Paths.get("C:\\Users\\Users\\Users\\Users");
           Path testFilePath2 = Paths.get("C:\\Users\\Users\\Users\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath1.relativize(testFilePath2));
       }
    }

    Консоль шығысы:

    Пайдаланушы аты\Жұмыс үстелі\testFile.txt

Әдістердің толық тізімі Pathөте үлкен. Сіз олардың барлығын Oracle құжаттамасынан таба аласыз . Біз шолуға көшеміз Files.

Файлдар

Files- бұл сыныптан статикалық әдістер жылжытылған қызметтік сынып File. - бұл шамамен немесе Files-мен бірдей , тек массивтермен және жинақтармен емес, файлдармен жұмыс істейді :) Ол файлдар мен каталогтарды басқаруға бағытталған. Статикалық әдістерді қолдану арқылы біз файлдар мен каталогтарды жасай, жоя және жылжыта аламыз. Бұл операциялар үшін әдістер қолданылады (каталогтар үшін - ) және . Міне, оларды пайдалану жолы: ArraysCollectionsFilescreateFile()createDirectory()move()delete()
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public class Main {

   public static void main(String[] args) throws IOException {

       //file creation
       Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
       System.out.println("Was the file created successfully?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       // create directory
       Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
       System.out.println("Was the directory successfully created?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));

       //move file from desktop to testDirectory. You need to move with the name of the file in the folder!
       testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Is our file left on the desktop?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       System.out.println("Has our file been moved to testDirectory?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));

       //remove file
       Files.delete(testFile1);
       System.out.println("Does the file still exist?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
   }
}
Files.createFile()Мұнда алдымен жұмыс үстелінде файл жасаймыз (әдіс ), содан кейін сол жерде қалта жасаймыз (әдіс Files.createDirectory()). Осыдан кейін біз файлды (әдіс Files.move()) жұмыс үстелінен осы жаңа қалтаға жылжытамыз және соңында файлды жоямыз (әдіс Files.delete()). Консоль шығысы: файл сәтті жасалды ма? true Каталог сәтті жасалды ма? true Біздің файл әлі жұмыс үстелінде ме? false Біздің файл testDirectory ішіне жылжытылды ма? true Файл әлі де бар ма? жалған Назар аударыңыз:PathИнтерфейс әдістері сияқты көптеген әдістер Filesнысанды қайтарадыPath . Көптеген класс әдістері Filesде қабылдайды Path. Мұнда әдіс сіздің сенімді көмекшіңіз болады Paths.get()- оны белсенді пайдаланыңыз. Тағы не қызық Files? Ескі сыныпқа шынымен жетіспейтін нәрсе - ! Fileәдісі. copy()Біз ол туралы дәрістің басында айттық, енді онымен кездесудің уақыты келді!
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public class Main {

   public static void main(String[] args) throws IOException {

       //file creation
       Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
       System.out.println("Was the file created successfully?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       // create directory
       Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
       System.out.println("Was the directory successfully created?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));

       //copy the file from the desktop to the directory testDirectory2.
       testFile1 = Files.copy(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Is our file left on the desktop?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       System.out.println("Has our file been copied to testDirectory?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
   }
}
Консоль шығысы: файл сәтті жасалды ма? true Каталог сәтті жасалды ма? true Біздің файл әлі жұмыс үстелінде ме? true Біздің файл testDirectory-ге көшірілді ме? true Енді файлдарды бағдарламалы түрде көшіруге болады! :) Бірақ сынып Filesфайлдарды өздері басқаруға ғана емес, оның мазмұнымен де жұмыс істеуге мүмкіндік береді. Файлға деректерді жазу үшін оның әдісі бар write(), ал оқу үшін - 3: read()және readAllBytes()біз readAllLines() соңғысына егжей-тегжейлі тоқталамыз. Неге онда? Өйткені оның өте қызықты қайтару түрі бар - List<String>! Яғни, ол бізге файлдағы жолдардың тізімін қайтарады. Әрине, бұл мазмұнмен жұмыс істеуді өте ыңғайлы етеді, өйткені бүкіл файл, мысалы, консольге тұрақты циклде шығарылуы мүмкін for:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Main {

   public static void main(String[] args) throws IOException {

       List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);

       for (String s: lines) {
           System.out.println(s);
       }
   }
}
Консольдің шығарылымы: Бір тамаша сәт есімде: Көз алдымда көріндің, Өтпелі аяндай, Мөлдір сұлудың данышпанындай. Өте ыңғайлы! :) Бұл мүмкіндік Java 7-де пайда болды. Java 8-де Stream API пайда болды , ол Java-ға функционалдық бағдарламалаудың кейбір элементтерін қосты. Оның ішінде файлдарды басқарудың бай мүмкіндіктері. Бізде тапсырма бар деп елестетіп көріңіз: файлдағы «Қалай» сөзінен басталатын барлық жолдарды тауып, оларды БАС РЕГИПКЕ түрлендіріңіз және оларды консольге шығарыңыз. Java 7-де сыныпты пайдаланатын шешім қандай болады Files? Сол сияқты бірнәрсе:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Main {

   public static void main(String[] args) throws IOException {

       List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);

       List<String> result = new ArrayList<>();

       for (String s: lines) {
           if (s.startsWith("How")) {
               String upper = s.toUpperCase();
               result.add(upper);
           }
       }

       for (String s: result) {
           System.out.println(s);
       }
   }
}
Консоль шығысы: ОРАЗАҚ КӨРІНІСІНДЕГІ, ТАЗА СҰЛУЛЫҚТЫҢ ДЕНІСІ СИЯҚТЫ. Біз мұны істеген сияқтымыз, бірақ мұндай қарапайым тапсырма үшін біздің codeымыз аздап ... толық болып шықты деп ойламайсыз ба? Java 8 Stream API көмегімен шешім әлдеқайда талғампаз көрінеді:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

   public static void main(String[] args) throws IOException {

       Stream<String> stream = Files.lines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"));

       List<String> result  = stream
               .filter(line -> line.startsWith("How"))
               .map(String::toUpperCase)
               .collect(Collectors.toList());
       result.forEach(System.out::println);
   }
}
Біз бірдей нәтижеге қол жеткіздік, бірақ әлдеқайда аз codeпен! Оның үстіне, біз «оқылатындықтан» жоғалттық деп айтуға болмайды. Stream API-мен таныс болмасаңыз да, бұл codeтың не істейтіні туралы оңай түсініктеме бере аласыз деп ойлаймын. Бірақ қысқаша айтқанда, ағын - бұл әртүрлі функцияларды орындауға болатын элементтер тізбегі. Біз әдістен Stream нысанын аламыз Files.lines(), содан кейін оған 3 функцияны қолданамыз:
  1. Әдістің көмегімен filter()біз файлдан «Қалай» деп басталатын жолдарды ғана таңдаймыз.

  2. Біз барлық таңдалған жолдарды әдісті қолданып өтіп map(), олардың әрқайсысын ҮЛКЕН РЕГИПКЕ дейін азайтамыз.

  3. Біз барлық алынған жолдарды Listпайдалану үшін біріктіреміз collect().

Шығуда біз бірдей нәтиже аламыз: ОРАЗА КӨРІНДІ, ТАЗА СҰЛУЛЫҚТЫҢ ДАНЫШЫ СИЯҚТЫ. Егер сіз осы кітапхананың мүмкіндіктері туралы көбірек білгіңіз келсе, осы мақаланы оқуды ұсынамыз . Біз өз қойларымызға, яғни файлдарға ораламыз :) Бүгін біз қарастыратын соңғы мүмкіндік - файл ағашы арқылы жүру . Қазіргі операциялық жүйелердегі файл құрылымы көбінесе ағаш түрінде болады: оның түбірі бар және басқа тармақтарды бөлуге болатын тармақтар бар және т.б. Каталогтар түбір мен тармақтардың рөлін атқарады. Мысалы, « C:// » каталогы түбір ретінде әрекет ете алады . Одан екі тармақ бөлінеді: “ C://Жүктеулер ” және “ C:// Пайдаланушылар ”. Осы фorалдардың әрқайсысынан тағы 2 тармақ бар: “ C://Жүктеулер/Суреттер ”, “ C://Жүктеулер/Бейне ”, “ C://Users/JohnSmith ”, “ C://Users/Pudge2005 ” . Осы тармақтардан басқа тармақтар тарайды, т.б. - ағаш осылай болып шығады. Linux жүйесінде ол шамамен бірдей көрінеді, тек сонда ғана каталог түбір ретінде әрекет етеді / Файлдар, жол - 2 Енді бізде тапсырма бар деп елестетіңіз: түбірлік каталогты біле отырып, біз оны өтуіміз керек, барлық деңгейдегі қалталарды қарап шығыңыз және олардан мазмұны бар файлдарды табуымыз керек. бізге керек. Біз «Бұл бізге қажет файл!» жолы бар файлдарды іздейміз. Біздің түбірлік каталог жұмыс үстелінде орналасқан «testFolder» қалтасы болады. Оның ішінде келесі мазмұндар бар: Файлдар, жол - 3деңгей1-а және деңгей1-b қалталарының ішінде де қалталар бар: Файлдар, жол - 4Файлдар, жол - 5Бұл «екінші деңгейлі қалталардың» ішінде басқа қалталар жоқ, тек жеке файлдар бар: Файлдар, жол - 6Файлдар, жол - 7Біз мазмұны бар 3 файлды арнайы белгілейміз. анық атаулары бар қажет - FileWeNeed1.txt , FileWeNeed2.txt, FileWeNeed3.txt Бұл Java көмегімен мазмұн арқылы табуымыз керек. Мұны қалай істей аламыз? Файл ағашын айналып өтудің өте күшті әдісі құтқаруға келеді - Files.walkFileTree(). Міне, бізге не істеу керек. Біріншіден, бізге қажет FileVisitor. FileVisitorфайл ағашын айналып өтудің барлық әдістерін сипаттайтын арнайы интерфейс болып табылады. Атап айтқанда, файлдың мазмұнын оқып, онда бізге қажетті мәтін бар-жоғын тексеру үшін логиканы қоямыз. Біздікі мынандай болады FileVisitor:
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;

public class MyFileVisitor extends SimpleFileVisitor<Path> {

   @Override
   public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {

       List<String> lines = Files.readAllLines(file);
       for (String s: lines) {
           if (s.contains("This is the file we need")) {
               System.out.println("Required file found!");
               System.out.println(file.toAbsolutePath());
               break;
           }
       }

       return FileVisitResult.CONTINUE;
   }
}
Бұл жағдайда біздің сынып -дан мұра алады SimpleFileVisitor. FileVisitorБұл тек бір әдісті қайта анықтау қажет болатын іске асыратын сынып : visitFile(). Мұнда біз әр каталогтағы әрбір файлмен не істеу керектігін сипаттаймыз. Егер сізге күрделі өту логикасы қажет болса, өзіңіздің іске асыруыңызды жазуыңыз керек FileVisitor. Онда сізге тағы 3 әдісті енгізу қажет:
  • preVisitDirectory()— папкаға кірер алдында орындалуы керек логика;

  • visitFileFailed()— файлды енгізу мүмкін болмаса не істеу керек (қолжетімсіздігі немесе басқа себептер);

  • postVisitDirectory()— папкаға енгеннен кейін орындалуы керек логика.

Бізде ондай логика жоқ, сондықтан бізге жеткілікті SimpleFileVisitor. Әдістің ішіндегі логика visitFile()өте қарапайым: файлдағы барлық жолдарды оқып шығыңыз, оларда бізге қажет мазмұн бар-жоғын тексеріңіз, егер бар болса, консольге абсолютті жолды басып шығарыңыз. Сізге қиындық тудыруы мүмкін жалғыз жол мынау:
return FileVisitResult.CONTINUE;
Шындығында бәрі қарапайым. Мұнда файл енгізілгеннен кейін және барлық қажетті операциялар аяқталғаннан кейін бағдарлама не істеу керек екенін сипаттаймыз. Біздің жағдайда біз ағашты айналып өтуді жалғастыруымыз керек, сондықтан біз опцияны таңдаймыз CONTINUE. Бірақ бізде, мысалы, басқа тапсырма болуы мүмкін: «Бұл бізге қажет файл» бар барлық файлдарды емес, тек бір файлды табу . Осыдан кейін бағдарламаны тоқтату керек. Бұл жағдайда біздің code дәл солай көрінеді, бірақ үзіліс орнына; болар еді:
return FileVisitResult.TERMINATE;
Ал, codeымызды іске қосып, оның жұмыс істейтінін көрейік.
import java.io.IOException;
import java.nio.file.*;

public class Main {

   public static void main(String[] args) throws IOException {

       Files.walkFileTree(Paths.get("C:\\Users\\Username\\Desktop\\testFolder"), new MyFileVisitor());
   }
}
Консоль шығысы: қажетті файл табылды! C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt Қажетті файл табылды! C:\Users\Username\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt Қажетті файл табылды! C:\Users\Username\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt Керемет, біз мұны жасадық! :) Егер сіз туралы көбірек білгіңіз келсе walkFileTree(), мен сізге осы мақаланы ұсынамын . Сіз сондай-ақ шағын тапсырманы орындай аласыз - оны SimpleFileVisitorәдеттегіге ауыстырыңыз FileVisitor, барлық 4 әдісті орындаңыз және осы бағдарламаның мақсатын анықтаңыз. Мысалы, сіз барлық әрекеттеріңізді тіркейтін бағдарламаны жаза аласыз: консольде файлдың немесе қалтаның атын оларды енгізер алдында/кейін көрсету. Барлығы - кейінірек кездескенше! :)
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION