Avatar
Loupak
Člen
Avatar
Loupak:

Ahoj,
jazyk C# jsem se začal učit nedávno a tak bych potřeboval poradit ohledně jednoho problémů, který řeším delší dobu. Problém spočívá v obnově čteného textu z logu, který mi funguje jen v CMD ale když to zkusím implementovat do GUI "WPF" tak mi to přečte jen jeden řádek. Problém je v loopu, který se dále po prvním řádku neopakuje. Zkoušel jsem plno různých řešení jak Thread tak i Dispatcher ale ještě jsem na to nepřešel. Problém bude nejspíše ve vlákně, které aplikace využívá a to čtení potřebuje svoje vlastní. Přidávám i Project do VS. Project do VS

Děkuji za pomoc.

// MainWindow.xaml.cs
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // Your Path to main Log directory
            string Path = @"YOUR_PATH";

            reader status = new reader(Path);
            Dispatcher.Invoke((Action)(() => textBlock.Text = status.ReadFile()));
        }
    }


// Class/reader.cs
class reader
    {
        private string path;

        public reader(string path)
        {
            this.path = path;
        }

        public string ReadFile()
        {
            var directory = new DirectoryInfo(path);
            var myFile = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();
            string logFile = myFile.ToString();
            string filePath = System.IO.Path.Combine(path, logFile);

            try
            {
                using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                using (StreamReader sr = new StreamReader(fs))
                {
                    string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        while (!sr.EndOfStream)
                        {
                            return sr.ReadLine();

                        }
                        while (sr.EndOfStream)
                        {
                            Thread.Sleep(100);
                        }
                    }
                    return sr.ReadLine();
                }
            }
            catch (Exception)
            {
                string ex = "Could not read the file";
                return ex;
            }
        }
    }
 
Odpovědět 12. července 16:45
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na Loupak
ostrozan:

Jen tak zběžně na první pohled :
Příkazem "return" ukončíš čtení souboru hned po prvním řádku - to se pak nediv že tam pak máš jen ten jeden :-)
Máš nějaký důvod číst soubor po řádcích a ne celý (ReadToEnd())?

 
Nahoru Odpovědět  +1 12. července 18:36
Avatar
Loupak
Člen
Avatar
Loupak:

Zkoušel jsem oboje a mám stejný problém. Problém bude někde jinde. Sice mi to přečte všechny řádky v souboru a zobrazí, ale po úpravě souboru už se nezmění v GUI.

 
Nahoru Odpovědět 12. července 21:52
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na Loupak
ostrozan:

Jak mám rozumět tomu

ale po úpravě souboru už se nezmění v GUI

?
to jako v jiné aplikaci upravuješ soubor a tady v tom sleduješ jak se mění?

 
Nahoru Odpovědět 13. července 5:28
Avatar
Loupak
Člen
Avatar
Loupak:

Ano přesně tak. Vysvětlím to lépe. Mám servery, kde při každém zapnutí se vygeneruje nový Log soubor s vlastním ID. Proto mám jako první funkci aby mi to našlo ve složce nejnovější soubor a pak ho předal funkci StreamReader. StreamReader má svůj vlastní loop, který se ale nějak neopakuje. Když se aplikace předělá jenom do Console, tak funguje a čte to ze souboru po úpravě. To že jsem tam mám ReadLine je proto, aby to vracelo jenom ten poslední upravený řádek a soubor se nečetl znovu a znovu. Pokud jsem to dobře pochopil. , Může tam být i ReadToEnd. Po přečtení ze souboru by měl StreamReader vracet hodnotu do GUI "WPF" což se taky stane, ale jenom jednou po startu, pak už to nevrací nic ani po úpravě souboru. Jinak jsem tam přidal Project z VS tak tam to uvidíš co asi myslím. :)

 
Nahoru Odpovědět 13. července 13:28
Avatar
Andrej Farkaš:

V skratke nejako takto:

  1. ReadFile() je napísaná tak, že ti neprečíta celý súbor ako napovedá jej názov, ale vždy len druhý riadok daného súboru (ak existuje). Presvedč sa debugovaním. Prvý riadok vráti Readline() v prvom while a je uložený do premennej line.
  2. Všetky tie while loopy sú úplne zbytočné a zle navrhnuté, keďže ti tá slučka zbehne iba raz alebo sa to zasekne v nekonečnej slučke.
  3. Ak máš v logu len jeden riadok, dostaneš sa do nekonečnej slučky, kde budeš uspávať thread na sekundu do nekonečna.
  4. Z tej WPF aplikácie tú metódu voláš len raz a to z konštruktora WPF okna.
  5. Načo máš premennú line? Používaš ju len vo while a tam môžeš rovno kontrolovať či sr.ReadLine() != null.
Editováno 13. července 14:11
Nahoru Odpovědět 13. července 14:09
Live. Love. Learn.
Avatar
Loupak
Člen
Avatar
Loupak:

Dobře kouknu na to a zkusím to předělat jinak.

 
Nahoru Odpovědět 13. července 14:40
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na Loupak
ostrozan:

Není mi jasné, jak toto může fungovat v konzoli - můžeš sem dát i ten funkční konzolový projekt? To by mně fakt zajímalo - bude to buď geniální řešení, nebo totální prasárna - tipuju žes to někde obšlehnul.

Jinak trochu (no trochu víc) jsem popravil ten tvůj projekt, aby dělal to co chceš - to jsem ovšem ještě nevěděl, že chceš použít jen poslední řádek ze souboru - mimochodem podle toho co píšeš

kde při každém zapnutí se vygeneruje nový Log soubor s vlastním ID.

bych si myslel, že ten řádek tam bude vždy jen jeden / ale i tak jsem tam dal úpravu, že ti to vypreparuje ze souboru poslední řádek .

Nevím jak se ta tvoje konstrukce měla dozvědět , že se ve složce něco děje - já jsem to vyřešil tak, že se každých 100 ms dotazuju jestli v posledně přidaném souboru ve složce nedošlo k novému zápisu.
Ten čas si samozřejmě nastav jaký chceš - sekundu - minutu - jak je libo, ale pod těch 100 ms bych radši nešel

Reader.cs

class Reader
{
    private string path,logFile;
    public string filePath { get; set; }
    public Reader(string path)
    {
        this.path = path;
        var directory = new DirectoryInfo(path);
        logFile =  directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First().ToString();
        filePath = Path.Combine(path, logFile);
    }

    public string ReadFile()
    {

        try
        {
            using (StreamReader sr = new StreamReader(filePath))
            {
                string data =sr.ReadToEnd();
                string[] rows = data.Split('\n');
                return rows.Last();
            }
        }
        catch (Exception)
        {
            string ex = "Could not read the file";
            return ex;
        }
    }
}

MainWindow.xaml.cs

public partial class MainWindow : Window
{
        // Your Path to main Log directory
    string Path = @"C:\Users\Admin\Desktop\Test";
    private DateTime lastLogTime;

    public MainWindow()
    {
        InitializeComponent();

        DispatcherTimer timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromMilliseconds(100);
        timer.Tick += timer_Tick;
        timer.Start();
    }

    void timer_Tick(object sender, EventArgs e)
    {
        Reader reader = new Reader(Path);
        DateTime logTime = File.GetLastWriteTime(reader.filePath);
        if (logTime == lastLogTime) return;
        else
        {
            lastLogTime = logTime;
            textBlock.Text += reader.ReadFile()+'\n';
        }
    }
}

Jak vidíš, poměrně dost jsem ti toho vyházel - budeš li mít nějaké dotazy co,proč a jak - tak se klidně zeptej.

tady máš tvůj upravený projekt

Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět  +1 13. července 15:40
Avatar
Loupak
Člen
Avatar
Loupak:

Toho, že se mi snažíte pomoct si vážím. Jak již jsem psal nahoře, C# jsem se začal učit nedávno ze své zajímavosti a nejsem v tom moc velký expert. Navíc jsem frontend web developer a backend v Laravelu takže C# je pro mě něco jiného. Jinak samozřejmě jsem to nepsal celé sám, ale hledal i na ostatních webech a na stackoverflow. Takže ano je to z toho co jsem se dočetl a pochopil a neni to jediný co jsem zkoušel stejně tak to udělat přes FileWatcher. K těm logům. Ten log má například název "Log_21565.txt" a po restartování serveru se vytvoří nový pod jiným ID např. "Log_85624.txt". Jinak ty logy nemaj jeden řádek ale třeba 20 Mb po delší době. Jinak to konzolové řešení je ZDE

Editováno 13. července 16:39
 
Nahoru Odpovědět 13. července 16:37
Avatar
Loupak
Člen
Avatar
Loupak:

Po vyzkoušení tvého kódu funguje všechno jak jsem chtěl a potřeboval. Moc ti za to děkuji. :)

 
Nahoru Odpovědět 13. července 18:06
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na Loupak
ostrozan:

Nemáš zač :-)
já jsem zase zkusil tu konzoli a když pominu, že to vůbec není stejný kód tak mně to fungovalo jen při změně stávajícího souboru - při vytvoření nového se nestalo nic

A teď dovol pár rad začátečníkovi:
uč se od píky - jedna věc je C# jako jazyk a konzolové aplikace a něco jiného jsou knihovny frameworku .Net
ještě něco jiného je WPF
bez urážky - a neber si to osobně, ale při tvojí metodě učení moc nechápeš jak to všechno funguje - zkřížením kódu z konzole, rad ze StackOverFlow a vlastní invence jsi spáchal strašný bastl - ale ještě jednou upozorňuju - nechci tě tím nijak urážet - ne všechno je špatně - zjištění posledně přidaného souboru je super :-)

k chybám které vypsal Andrej Farkaš ještě jednu - i když není fatální:
pro čtení souboru kombinuješ dva nástroje pro čtení FileStream a StreamReader - buď jedno, nebo druhé.

Ale oboje ti bude soubor číst od začátku do konce ať už celé naráz, nebo po řádcích takže i kdyby měl soubor gigabajt , tak bohužel.(Opravte mně někdo, jestli se mýlím)

Zmínkou o FileWatcheru - správně FileSystemWatcher jsi mi připomněl že ani moje řešení není dokonalé -
takže jsem ho ještě vylepšil.

Reader.cs

class Reader
{
    public Reader()
    {
    }

    public string ReadFile(string filePath)
    {
        try
        {
            using (StreamReader sr = new StreamReader(filePath))
            {
                string data =sr.ReadToEnd();
                string[] rows = data.Split('\n');
                return rows.Last();
            }
        }
        catch (Exception)
        {
            string ex = "Could not read the file";
            return ex;
        }
    }
}

MainWindow.xaml.cs

public partial class MainWindow : Window
{
        // Your Path to main Log directory
    string folderPath = @"C:\Users\Admin\Desktop\Test";
    private FileSystemWatcher fileSystemWatcher;

    public MainWindow()
    {
        InitializeComponent();
        StartFileSystemWatcher();
    }



    private void StartFileSystemWatcher()
    {

       fileSystemWatcher = new FileSystemWatcher();

        // Set folder path to watch
        fileSystemWatcher.Path = folderPath;

        // Gets or sets the type of changes to watch for.
        // In this case we will watch change of filename, last modified time, size and directory name
        fileSystemWatcher.NotifyFilter = NotifyFilters.FileName |NotifyFilters.LastWrite |
                                         NotifyFilters.Size |NotifyFilters.DirectoryName;

        // Event handler
        fileSystemWatcher.Changed += new FileSystemEventHandler(fileSystemWatcher_Changed);

        // START watching
        fileSystemWatcher.EnableRaisingEvents = true;
    }

    private void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
    {
        fileSystemWatcher.EnableRaisingEvents = false;
        Reader reader = new Reader();
        Dispatcher.Invoke((Action)(() => textBlock.Text += reader.ReadFile(e.FullPath) + '\n'));
        fileSystemWatcher.EnableRaisingEvents = true;
    }


}

jak jistě poznáš, taky jsem si pomohl na netu, ale když si chceš ulehčit práci a využít cizí kód, musíš vědět co děláš ne jen doufat,že to bude fungovat

A nakonec ještě odkaz na slušný tutor WPF ten zdejší není úplně špatný, ale jaksi zamrzl v začátcích.

 
Nahoru Odpovědět  +2 13. července 20:23
Avatar
Loupak
Člen
Avatar
Loupak:

Ten WPF tutoriál si určitě zkusím a díky za ty nějaké rady do začátků. :)

 
Nahoru Odpovědět 13. července 21:48
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 12 zpráv z 12.