четвртак, 08. септембар 2016.

Rad sa tekstualnim i binarnim datotekama u C# programskom jeziku


Zahvaljujući neverovatnom brzom i velikom progresu C# programskog jezika sa svim njegovim novim promenama i mogućnostima; nikada nije bilo lakše raditi sa tekstualnim i binarnim datotekama. Ukoliko niste pročitali dva predhodna poglavlja ( pogledajte u sadržaju ) kako se radi sa direktorijumima i fajlovima, savetujem vam da to uradite pre nego što krenete sa navedenom temom. Za čitanje i pisanje datoteka se koriste više klasa, ali opšti koncept za realizovanje istih nazivamo stream - strim – tok podataka. Tok podataka vam je u suštini serija bajtova koji se kreću od jedne tačke do druge. Tok podataka se može prenositi iz nekog spoljašnjeg izvora u program i tada govorimo o čitanju iz toka podataka ali ako se podaci prenose iz programa u neki spoljašnji izvor data govorimo o pisanju u tok podataka. Uglavnom vas tako u školama uče. Spoljašnji izvor može da bude datoteka, mrežni protokol ili neko područje memorije. Čak i neka promenjiva unutar vašeg programa može da se tretira kao spoljašnji izvor. Bitno je da uvek razmislite gde i kako će vaš program pamtiti podatke koje koristi i pre nego što počnete da programirate program.


( C# programskim jezik pojednostavljuje rad sa tekstualnim i binarinim datotekama )
                                          
Za pisanje i čitanje datoteka, Microsoft vam nudi bazne klase imenskog prostora System.IO kao i System.Net.Sockets za mrežne podatke, ali tri najbitnije klase za ovaj posao definitivno jesu StreamReader i StreamWriter koje su namenjene isključivo za pisanje i čitanje tekstualnih datoteka dok je klasa FileStream služi za čitanje, pisanje ali i pozicioniranje binarnih podataka unutar strima. Druge klase poput BinaryReader-a i BinaryWriter-a pružaju dodatne mogućnosti formatiranja binarnih podataka. Sve ovo teoretski može da zvuči konfuzno, za sve klase uvek trebate da istražujete MSDN i da svaku klasu prostudirate u potpunosti radi vas; ali najbolje će te programiranje naučiti kroz konkretne primere i kod. Podaci mogu da se čuvaju i u drugačijim formatima poput XML-a, o kojima ću posvetiti sledeći post u sekciji sadržaja bloga C# Advanced – Napredni Nivo. Danas se skoro svi podaci uglavnom čuvaju u bazama podataka. Svaki C# programer mora da poznaje i rad sa bazama podataka iako ne mora biti stručnjak za baze podataka. Iz tog razloga pored C# postova, uporedo možete pratiti i SQL postove sa rad sa podacima, jer ako ne naučite baze podataka nećete umeti ni da im pristupate, niti da koristite i skladištite podatke iz vaših programa, sistema i servera.

Upisivanje i čitanje teksta iz tekstualnih fajlova

Zbog promena koje su usledile dolaskom C# programskog jezika verzije 6.0 vi ćete biti u prilici da često naletite na mnogobrojne praktične primere na celom Internet-u koji se razlikuju jedan od drugih dok svi oni rade istu stvar, tj. upisuju i čitaju tekst u tekstualni fajl. Ako pažljivije pogledate sledeći primer, primetiće te da program u jednoj liniji koda upisuje ili čita tekst iz fajla. To pre C# 6.0  nije bio slučaj. Pogledajte sledeći kod najjednostavnijeg upisivanja i čitanja teksta iz tekstualnih fajlova.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace WriteTextToFile
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("Input some text to write into the text file: " + Environment.NewLine);
            string text = ReadLine();

            if (String.IsNullOrWhiteSpace(text))
            {
                WriteLine("There is no text for writing into the text file.");

            }
            else
            {
                WriteText(text);
                ReadText();
            }

            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();

        }

        static void WriteText(string text)
        {
            var dir = Directory.GetCurrentDirectory();
            var file = Path.Combine(dir, "File.txt");

            try
            {
                File.WriteAllText(file, text);
               
            }
            catch (Exception ex)
            {
                WriteLine(Environment.NewLine + ex.Message);
               
            }
        }

        static void ReadText()
        {
            var dir = Directory.GetCurrentDirectory();
            var file = Path.Combine(dir, "File.txt");

            string text = File.ReadAllText(file);

            WriteLine();
            WriteLine("Text from the text file is: " + Environment.NewLine + text);

        }
    }
}

Program prvo prihvata tekst od korisnika, zatim proverava da li je korisnik uopšte i uneo tekst. Na primer mogao je samo da pritisne tipku Enter na tastaturi ili da samo pritisne tipku za razmak. Zato ne možete to utvrditi tako što će te proveriti da li promenjiva text već to morate utvrditi kao što je u kodu. Inače će te imati propust u kodu. Ukoliko nema šta da se upiše u datoteku onda nema potrebe ni pokušavati upis. Zatim program kreira datoteku ako već nije kreirana i u nju upisuje tekst. On takođe i otvara istu datoteku i prikazuje vam sadržaj datoteke na konzoli kako bi vam prikazao šta je upisano u datoteku. Takođe obratite pažnju na metodu Combine klase Path. To je izuzetna stvar koju trebate uvek koristiti i radi čitljivosti koda. Kad pokrenete navedeni program rezultat će biti sličan u zavisnosti kakav ste uneli tekst.

Input text to write into the text file:

 My name is Manuel Radovanovic. You can visit my blog on www.manuelradovanovic.com

Text from the text file is:

 My name is Manuel Radovanovic. You can visit my blog on www.manuelradovanovic.com

Press any key to continue...


Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 46. Read And Write Into The Text File )

Prethodni primer jeste najjednostavniji način da upišete i pročitate tekst iz tekstualnog fajla, ali nije najpraktičniji. Prethodni program ukoliko nema datoteke File.txt kreira novu datoteku, tj. on uvek kreira novu datoteku ali ako datoteka postoji on će je nadjačati. Što znači kad sledeći put pokrenete program i upišete u datoteku novi tekst, vaš prethodni tekst će biti zamenjen novim. Zato morate koristiti drugačiji način upisa teksta u tekstualni fajl koji će ukoliko postoji neki tekst u tekstualnom fajlu samo dodati novi tekst iza već postojećeg. Kao uspešan i profesionalan programer kada radite sa datotekama treba odmah da razmislite i o asinhronizovanom programiranju. Zato je sledeći primer za većinu situacija mnogo bolji.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace AsynchWriteAndReadTextFile
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("Input some text to write into the text file: " + Environment.NewLine);
            string text = ReadLine();

            if (String.IsNullOrWhiteSpace(text))
            {
                WriteLine("There is no text for writing into the text file.");

            }
            else
            {
                WriteText(text);
                ReadText();
            }

            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();

        }

        static async void  WriteText(string text)
        {
            var dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            var file = Path.Combine(dir, "File.txt");

            try
            {
                using (StreamWriter outputFile = new StreamWriter(file, true))
                {
                    await outputFile.WriteAsync(text);

                }

            }
            catch (Exception ex)
            {
                WriteLine(Environment.NewLine + ex.Message);

            }
        }

        static async void ReadText()
        {
            var dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            var file = Path.Combine(dir, "File.txt");

            string result;
            try
            {
                using (StreamReader reader = File.OpenText(file))
                {
                    result = await reader.ReadLineAsync();

                    WriteLine();
                    WriteLine("Text from the text file is: " + Environment.NewLine + result);

                }
            }
            catch (Exception ex)
            {
                WriteLine(ex.Message);

            }                  
        }
    }
}

U navedenom kodu možete videti da koristimo klasu StreamWriter za upis teksta u tekstualnu datoteku. To vam već omogućava da boolean-ovom opcijom true omogućite dodavanje teksta na već postojeći tekst. Takođe u navedenom primeru se za razliku od tekućeg direktorijuma tekstualni fajl nalazi na lokaciji specijalnog foldera Documents vašeg računara. I što je najbitnije i upis i čitanje se vrši asinhronizovano. Kad pokrenete program prvi put, dobićete sličan rezultat u zavisnosti od teksta koji unesete. Za ovaj primer dva puta ćemo pokretati program. U prvom pokretanja programa unosimo samo jednu rečenicu:

Input text to write into the text file:

My name is Manuel Radovanovic.

Press any key to continue...

Text from the text file is:

My name is Manuel Radovanovic.


Sad kad zatvorimo program i ponovo ga pokrenemo, upišemo drugu rečenicu:

Input text to write into the text file:

You can visit my blog on www.manuelradovanovic.com

Press any key to continue...

Text from the text file is:

My name is Manuel Radovanovic. You can visit my blog on www.manuelradovanovic.com

Kao što vidite druga rečenica je dopisana iza prve. Ali će te isto primetiti da se program pre izvršio do kraja nego što je pročitao tekst iz tekstualne datoteke zato što su metode za upis i za čitanje teksta u tekstualni fajl asinhronizovane. Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 47. Asynchronous Read And Write Into The Text File )

Čitanje i pisanje binarnih datoteka

Binarne datoteke su vam kao što i njihovo ime znači binarne datoteke, tj. datoteke sa nefiltriranim bajtovima. Konvertovanje bilo koje vrste podataka ali i objekata u bajtove radi smeštanje istih u binarne datoteke zovemo serialization – serijalizacija dok obratni proces, tj. ekstrakcija objekta ili strukture podataka iz sekvence bitova zovemo deserialization – deserijalizacija. Ovaj proces je apstraktna i sekvencionalna reprezentacija, znači linearna; red po red, bit po bit; koju nam sa lakoćom omogućava klasa FileStream pre svega svojim mnogobrojnim konstruktorima koje sadrže i podrazumevane vrednosti. Ako se pitate zašto je korisnije da podatke smeštate u binarne datoteke, razlog je očigledan. Vi u binarne datoteke možete da smestite bilo koji tip podataka, znači slike, video i audio fajlove što nije moguće smestiti u tekstualne datoteke. Postoji više načina kako možete upisivati i čitati podatke iz binarnih datoteka, to čak možete raditi red po red u binarnoj datoteci ali u principu suština je ista. Pogledajte prvo upisivanje i čitanje običnog teksta u binarni fajl pomoću FileStream-a u ASCII kodu, čisto da bi ste se upoznali sa osnovama binarnih datoteka.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace WriteAndReadBinaryFile
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("Input some text to write into the binary file: " + Environment.NewLine);
            string text = ReadLine();

            if (String.IsNullOrWhiteSpace(text))
            {
                WriteLine("There is no text for writing into the binary file.");

            }
            else
            {
                WriteFile(text);
                ReadFile();

            }

            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();

        }

        static void WriteFile(string text)
        {
            var dir = Directory.GetCurrentDirectory();
            var file = Path.Combine(dir, "File.dat");

            try
            {
                FileStream fs = new FileStream(file, FileMode.Append, FileAccess.Write);

                if (fs.CanWrite)
                {
                    // usually use Encoding.Default but we want ASCII
                    byte[] buffer = Encoding.ASCII.GetBytes(text);
                    fs.Write(buffer, 0, buffer.Length);

                }

                fs.Flush();
                fs.Close();
            }
            catch (Exception ex)
            {
                WriteLine(Environment.NewLine + ex.Message);

            }
        }

        static void ReadFile()
        {
            var dir = Directory.GetCurrentDirectory();
            var file = Path.Combine(dir, "File.dat");
            string text = String.Empty;

            try
            {
                FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
                byte[] buffer = new byte[1024];
                int bytesread = fs.Read(buffer, 0, buffer.Length);

                text = Encoding.ASCII.GetString(buffer, 0, bytesread);

                fs.Flush();
                fs.Close();

            }
            catch (Exception ex)
            {
                WriteLine(Environment.NewLine + ex.Message);

            }
                   
            WriteLine();
            WriteLine("Text from the text file is: " + Environment.NewLine + text);

        }
    }
}

Prvo kreirate FileStream, zatim mu odredite mod i pristup. Podesite mu odgovarajući bafer u obliku niza bajtova i niz možete i upisati u datoteku ili čitati iz nje. U principu niz bajtova se podešava na neku maksimalnu veličinu od 1024 bajta, ali smo mi naveli da upiše dužinu našeg niza buffer jer je mnogo manja od 1024 bajta. Kad pokrenete navedeni program rezultat će biti sličan u zavisnosti šta želite da vam program upiše u binarni fajl.

Input text to write into the binary file:

My name is Manuel Radovanovic. I am C# programmer. You can visit my blog on www.manuelradovanovic.com

Text from the text file is:

My name is Manuel Radovanovic. I am C# programmer. You can visit my blog on www.manuelradovanovic.com

Press any key to continue…


Kada otvorite datoteku File.txt videćete da on sadrži ASCII tekst. Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 48. Simple Read And Write Into The Binary File )

Za upis i čitanje podataka iz binarne datoteke možete koristiti i klase BinaryWriter i BinaryReader. Ovaj način upisivanja je odličan za unos i čitanje podataka različitih tipova. Pogledajte sledeći primer.

using System;
using System.Collections.Generic;
using static System.Console;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace BinaryWriterAndReaderClass
{
    class Program
    {
        struct Blog
        {
            public string name;
            public string blog;
            public int year;
                            
        }
        static void Main(string[] args)
        {                
            WriteFile();
            ReadFile();
           
            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();

        }

        static void WriteFile()
        {
            var dir = Directory.GetCurrentDirectory();
            var file = Path.Combine(dir, "File.dat");

            try
            {
                using (BinaryWriter writer = new BinaryWriter(File.Open(file, FileMode.Create)))
                {
                    writer.Write("Manuel Radovanovic");
                    writer.Write("www.manuelradovanovic.com");
                    writer.Write(2015);
                                     
                }

            }
            catch (Exception ex)
            {
                WriteLine(Environment.NewLine + ex.Message);

            }
        }

        static void ReadFile()
        {
            var dir = Directory.GetCurrentDirectory();
            var file = Path.Combine(dir, "File.dat");

            Blog blog = new Blog();

            try
            {
                if (File.Exists(file))
                {
                    using (BinaryReader reader = new BinaryReader(File.Open(file, FileMode.Open)))
                    {
                      

                        blog.name = reader.ReadString();
                        blog.blog = reader.ReadString();
                        blog.year = reader.ReadInt32();

                    }
                }             
            }
            catch (Exception ex)
            {
                WriteLine(Environment.NewLine + ex.Message);

            }

            WriteLine("Owner: " + blog.name);
            WriteLine("Blog: " + blog.blog);
            WriteLine("Year: " + blog.year);
           
        }
    }
}

Kad pokrenete navedeni program rezultat će biti sledeći:

Owner: Manuel Radovanovic
Blog: www.manuelradovanovic.com
Year: 2015

Press any key to continue...


Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 49. BinaryReader And BinaryWriter Classes )

Međutim šta ako želite da kreirate binarni fajl asinhronično? Tada je potrebno malo više da se potrudite. Pogledajte sledeći primer.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace AsyncBinaryWriteAndReadFile
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("Input some text to write into the binary file: " + Environment.NewLine);
            string text = ReadLine();

            if (String.IsNullOrWhiteSpace(text))
            {
                WriteLine("There is no text for writing into the binary file.");

            }
            else
            {
                ProcessWrite(text);
                ProcessRead();

            }
                      
            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();

        }

        public static async void ProcessWrite(string text)
        {
            string dir = Directory.GetCurrentDirectory();
            string file = Path.Combine(dir, "File.dat");

            try
            {
                await WriteFile(file, text);

            }
            catch (Exception ex)
            {

                WriteLine(Environment.NewLine + ex.Message);
            }
          
        }
        private static async Task WriteFile(string file, string text)
        {         
                byte[] encodedText = Encoding.Unicode.GetBytes(text);

                using (FileStream sourceStream = new FileStream(file, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
                {
                    await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);

                }

        }              
        public static async void ProcessRead()
        {
            string dir = Directory.GetCurrentDirectory();
            string file = Path.Combine(dir, "File.dat");

            if (File.Exists(file) == false)
            {
                WriteLine("File not found: " + file);
            }
            else
            {
                try
                {
                    string text = await ReadFile(file);
                    WriteLine(text);
                }
                catch (Exception ex)
                {
                    WriteLine(Environment.NewLine + ex.Message);

                }
            }
        }
        private static async Task<string> ReadFile(string file)
        {  
                using (FileStream sourceStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true))
                {
                    StringBuilder sb = new StringBuilder();

                    byte[] buffer = new byte[0x1000];
                    int numRead;
                    while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                    {
                        string text = Encoding.Unicode.GetString(buffer, 0, numRead);
                        sb.Append(text);
                    }

                    return sb.ToString();
                }
                            
        }

    }
}

Kad pokrenete navedeni program rezultat će biti sličan u zavisnosti šta želite da vam program upiše u binarni fajl.

Input some text to write into the text file:

My name is Manuel Radovanovic. I am C# programmer. You can visit my blog on www.manuelradovanovic.com

Press any key to continue...

My name is Manuel Radovanovic. I am C# programmer. You can visit my blog on www.manuelradovanovic.com




Kako navedeni program izgleda možete pogledati i na video-u:


( C# 6.0 Tutorial - Advanced - 50. Asynchronous Read And Write Into The Binary File )