Podstawy C# - Obsługa I/O - StreamWriter nie działa poprawnie

0
namespace ConsoleApp124
{
    class Program
    {
        static void Main(string[] args)
        {
            if (!File.Exists("C:\\foo.txt"))
            {
                StreamWriter sw = new StreamWriter("C:\\foo.txt");

                sw.Write("Hello World!");
                sw.Flush();
                return;
            }
        }
    }
}

Gdy zamiast Flush jest Close - również nie zapisuje do pliku. Czemu tak jest? Write i WriteLine nie działają

3

U mnie działa, zapisuje. Może nie masz praw do zapisu do C:?

Ale i tak używaj raczej konstrukcji using, przy której masz pewność co do prawidłowego zwolnienia zasobów:

using (StreamWriter sw = new StreamWriter("foo.txt"))
{
    sw.Write("Hello World!");                
}
0
Ktos napisał(a):

U mnie działa, zapisuje. Może nie masz praw do zapisu do C:?

Ale i tak używaj raczej konstrukcji using, przy której masz pewność co do prawidłowego zwolnienia zasobów:

using (StreamWriter sw = new StreamWriter("foo.txt"))
{
    sw.Write("Hello World!");                
}

mi też zaczęło działać, dopiero uczę sie podstaw i takie coś dla mnie to standard, z góry przepraszam jeśli kogoś tym uraziłem :P

1

Panowie

I zawsze na koniec sw.Close().. Dane do pliku txt może i sie zapiszą ale przy kolejnej próbie zapisania czy np usunięcia pliku otrzymacie bład.. plik zajęty przez inny proces.. zawsze zamykamy zapis do pliku

0

Właśnie trafiłem na ten dziwny przypadek gdy StreamWriter zapisywał aż przestał i nie mogę znaleźć powodu dlaczego?

public void ZapiszDoPlikuCSV(string nazwaPliku, List<string> linieZNaglowkiem)
        {            
            using (Stream fileStream = new FileStream(nazwaPliku, FileMode.Create))
            {
                StreamWriter sw = new StreamWriter(fileStream, Encoding.UTF8);
                //foreach (var item in linieZNaglowkiem)
                //    sw.WriteLine(item);
                    sw.WriteLine("teskst");
            }
        }

przy iteracji po przekazanej kolekcji podglądam i item ma wartość tekstową ale mimo wszystko plik jest tworzony ale jest pusty, gdy zakomentowałem pętle i dałem bezpośrednio string jako argument do WriteLine to również plik jest tworzony ale jest pusty

Podglądając obiekt sw, po trzeciej iteracji widzę ze są jakieś wyjątki "WriteTimeout" i "ReadTimeout" czy one mogą być powodem? ale one nie są rzucane podczas pracy aplikacji bez debugera, aplikacja działa stabilnie dopiero w debugerze je zobaczyłem. Czy one maja związek z tym ze zapisuje mi puste pliki?

obraz_2022-05-05_080342536.png

w ten sposób też nie łapie żadnego wyjątku:

public void ZapiszDoPlikuCSV(string nazwaPliku, List<string> linieZNaglowkiem)
        {
            try
            {
                using (Stream fileStream = new FileStream(nazwaPliku, FileMode.Create))
                {
                    StreamWriter sw = new StreamWriter(fileStream, Encoding.UTF8);
                    foreach (var item in linieZNaglowkiem)
                        sw.WriteLine(item);
                    //sw.WriteLine("teskst");
                }
            }
            catch (Exception e)
            {

                throw new Exception("zonk", e);
            }
            
        }  ```
1

Każdy strumień powinien być zwalniany (using), inaczej to co znajduję się w StreamWriter nie jest zapisywane do FileStream.
Czyli tworzenie StreamWriter musi być w using( ... ):

        using (Stream fileStream = new FileStream(nazwaPliku, FileMode.Create))
        {
            using (StreamWriter sw = new StreamWriter(fileStream, Encoding.UTF8))
            {
                //foreach (var item in linieZNaglowkiem)
                //    sw.WriteLine(item);
                sw.WriteLine("teskst");
            }
        }

Warto sprawdzać czy klasa której używasz implementuję IDisposable wtedy musisz używać using.

Ewentualnie w C# 8.0 można używać usingów bez klamer, co trochę ułatwia:

    public static void ZapiszDoPlikuCSV(string nazwaPliku, List<string> linieZNaglowkiem)
    {
        using Stream fileStream = new FileStream(nazwaPliku, FileMode.Create);
        using StreamWriter sw = new StreamWriter(fileStream, Encoding.UTF8);

        //foreach (var item in linieZNaglowkiem)
        //    sw.WriteLine(item);
        sw.WriteLine("teskst");
    }

Ewentualnie można jeszcze bardziej uprościć i skorzystać z File:

    public static void ZapiszDoPlikuCSV(string nazwaPliku, List<string> linieZNaglowkiem)
    {
        File.WriteAllText(nazwaPliku, "teskst");
        //File.WriteAllText(nazwaPliku, String.Join(";",linieZNaglowkiem);
    }

Jeżeli chodzi o wyjątek to tutaj jest ok:

screenshot-20220505093623.png

0

faktycznie StreamWriter w usingu zadziałał od razu.

@Wilktar
A podpowiesz dlaczego wcześniej mi działało pomimo tego ze StreamWriter nie był w usingu? cos ze strumieniem się zmieniło gdy otwierałem pliki czy cos? Jedyna rzecz jaka zmieniałem to Filemode z OpenOrcreate na Create.

1

Ciężko jednoznacznie na to odpowiedzieć, to zależy jak działa StreamWriter. Przypuśćmy że StreamWriter ma wewnętrzny buffor danych który jest opróżniany w pewnych okolicznościach i np może to być przekroczenie wielkości bufora albo wtedy kiedy zapiszesz całą linię (czyli pojawi się znak '\n'), albo jeżeli minie jakiś okres czasu. I teraz program może poprawnie zadziałać bo jako parametr było zapisywane 500 bajtów, albo było zapisane 'abc\n' albo minęła 1 sekunda.

Czyli żeby mieć pewność że buffor zostanie opróżniony trzeba wywołać Flush. Żeby było zgodnie ze 'sztuką' najlepiej użyć Flush i Close, a najlepiej użyć using który wykonuję i Flush i Close.

0

ok dzięki wielkie, właśnie przypomniałem sobie objaw którego nie potrafiłem wytłumaczyć i odwlekałem dociekanie skąd to się bierze na później.

Do tej pory StreamWriter bez usinga nie wiedzieć czemu ostatnią linię nie pełną zapisywał, tzn albo 1/3 albo w połowie urywał - może faktycznie cos mu się przepełniało.

tak czy inaczej dzięki, już działa tak jak trzeba i w usingu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1