Obsługa plików C# - problem

0

Witam. Szukałem po różnych forach i stronach internetowych, ale nigdzie nie mogłem znaleźć odpowiedzi na moje pytania.
Aktualnie na zajęciach mamy aproksymację funkcji w programie VisualStudio. Prowadzący kazał nam pobrać współrzędne punktów z pliku tekstowego (żeby np przygotować program do obsługi wyników pomiarów). W jednym wierszu pliku tekstowego maja być kolejne punkty X oraz Y oddzielone tabulatorem lub spacją. Tego właśnie dotyczy mój problem. Jak odczytać kolejne X i Y oddzielone tabulatorem z pliku tekstowego i wpisać je po kolei do dwóch tablic? Potrafię odczytać kolejne linie natomiast nie wiem jak poradzić sobie z rozdzieleniem linii na kolumny i kolejno je wczytać.Do odczytania danych z pliku użyłem klasy OpenFileDialog.
Drugi problem dotyczy kilkukrotnego wciśnięcia przycisku. Otóż za pomocą numericUpDown mamy wybrać stopień wielomianu i narysować wykres właśnie tego rzędu. Chciałbym wczytać plik tylko raz przy pierwszym naciśnięciu przycisku i zachować go, a nie wczytywać do na nowo przy każdej zmianie stopnia wielomianu i ponownym naciśnięciu przycisku. Czy jest to możliwe w tej metodzie, czy muszę wczytać go w inny sposób? Może łatwiej będzie to zrealizować oknem openFileDIalog z przybornika programu?
Aby lepiej zobrazować to o co mi chodzi wrzucam fragment kodu oraz załącznik z wycinkiem okna programu.

int licznik = 0;
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = String.Format("Plik tekstowy (*.txt)|*.txt;");
            if(dialog.ShowDialog()==System.Windows.Forms.DialogResult.OK)
            {
                string plik = dialog.FileName;
                label1.Text = plik;
                if (label1.Text != "Nie wybrano pliku")
                {
                    //tu chciałbym aby był cały czas wczytywany ten sam plik;
                    foreach (string pelnaLinia in File.ReadLines(plik))
                    {
                        if (pelnaLinia != String.Empty) licznik++;
                    }
                    double[] linia = new double[licznik];
                    for (int i = 0; i < licznik; i++)
                    {
                        linia[i] = Convert.ToDouble(File.ReadLines(plik).Skip(i).Take(1).First());
                        
                    }
                }
            }

Z góry dziękuję za pomoc.
Pozdrawiam.

1
"5	 1".Split(new char[] {' ','\t'}, StringSplitOptions.RemoveEmptyEntries);

A wynikiem jest string[] = {"5", "1"}, więc zostaje Ci zamienić na pożądany typ.

Jak odczytać kolejne X i Y oddzielone tabulatorem z pliku tekstowego i wpisać je po kolei do dwóch tablic?

Takie chyba ułatwienie sobie życia - możesz zrobić sobie klasę lub użyć ValueTuple i zamiast dwóch tablic, to mieć np. listę punktów zamiast dwóch tablic

var punkty = new List<(int X, int Y)>();

punkty.Add((5, 1));

Console.WriteLine(punkty[0].X);
Console.WriteLine(punkty[0].Y);
0
WeiXiao napisał(a):
"5	 1".Split(new char[] {' ','\t'}, StringSplitOptions.RemoveEmptyEntries);

A wynikiem jest string[] = {"5", "1"}, więc zostaje Ci zamienić na pożądany typ.

Jak odczytać kolejne X i Y oddzielone tabulatorem z pliku tekstowego i wpisać je po kolei do dwóch tablic?

Takie chyba ułatwienie sobie życia - możesz zrobić sobie klasę lub użyć ValueTuple i zamiast dwóch tablic, to mieć np. listę punktów zamiast dwóch tablic

var punkty = new List<(int X, int Y)>();

punkty.Add((5, 1));

Console.WriteLine(punkty[0].X);
Console.WriteLine(punkty[0].Y);

Czy jednak jest możliwość dodania kolejno do tablic X[] oraz Y[]? Nie ogarniam ValueTuple i muszę potem znaleźć minimum i maksimum wartości z tablicy X i zrobić na ich podstawie wykres. Jest jakaś łatwa opcja na wpisanie tych danych w tablice oraz jak wykorzystać Split do kilku wierszy w pliku?

3

@Dawid16397:

No generalnie możesz po prostu przejść po każdej linii z pliku i na każdej zrobić split i wpisać wartości do tych swoich kolekcji

Możesz to po prostu foreachem przejść

foreach (var linia in ...)
{
	var rozdzielone = linia.Split(new char[] {' ','\t'}, StringSplitOptions.RemoveEmptyEntries);
	
	int X = Convert.ToInt32(rozdzielone[0]);
	int Y = Convert.ToInt32(rozdzielone[1]);

	wrzuc w tablice/liste czy co tam chcesz
}
0
WeiXiao napisał(a):

@Dawid16397:

No generalnie możesz po prostu przejść po każdej linii z pliku i na każdej zrobić split i wpisać wartości do tych swoich kolekcji

Możesz to po prostu foreachem przejść

foreach (var linia in ...)
{
	var rozdzielone = linia.Split(new char[] {' ','\t'}, StringSplitOptions.RemoveEmptyEntries);
	
	int X = Convert.ToInt32(rozdzielone[0]);
	int Y = Convert.ToInt32(rozdzielone[1]);

	wrzuc w tablice/liste czy co tam chcesz
}

Dobrze, powiedzmy że to jest ogarnięte. A czy można zrobić tak, aby po ponownym kliknięciu przycisku nie wybierać ponownie nowego pliku tylko pracować na tym wczytanym poprzednio?? Czy trzeba w takim wypadku wczytać plik za pomocą klasy FileStream a nie za pomocą OpenFileDIalog?

0

Nie, po prostu musisz sobie zapamiętać ścieżkę do pliku, który otwierałeś. Na początku ustawiasz ją na pustą albo na null, a potem już przy każdym kliknięciu "otwórz" sprawdzasz czy jest ustawiona i jeśli tak to otwierasz plik od razu, a jak nie, to uruchamiasz OpenFileDialog - i zapisujesz ścieżkę z OpenFileDialogu.

0
Ktos napisał(a):

Nie, po prostu musisz sobie zapamiętać ścieżkę do pliku, który otwierałeś. Na początku ustawiasz ją na pustą albo na null, a potem już przy każdym kliknięciu "otwórz" sprawdzasz czy jest ustawiona i jeśli tak to otwierasz plik od razu, a jak nie, to uruchamiasz OpenFileDialog - i zapisujesz ścieżkę z OpenFileDialogu.

Obsługa plików to jest rzecz której kompletnie nie ogarniam. Czy mógłbyś mi mniej wyjaśnić jak to ma wyglądać posługując się moim kodem który wstawiłem? Będę bardzo wdzięczny. Czy mam przepisać drugi raz program który wykonuje się po otwarciu pliku ale tym razem znając ścieżkę? Bo mój program wykona się w sytuacji gdy otworze plik za pomocą OpenFileDialog. Nie rozumiem jak ma to wyglądać

0

Teraz mam inny problem. Gdy otwieram plik za pomocą OpenFileDIalog to wszystko ładnie działa. Natomiast gdy znając ścieżkę chce do otworzyć za pomocą FileStream to wyskakuje mi błąd System.IO.IOException: „Proces nie może uzyskać dostępu do pliku „C:\Users\admin\Desktop\KpwE\aproksymacja\123.txt”, ponieważ jest on używany przez inny proces.”
Używająć using program nanosi mi jedynie punkty na wykres, lecz nie wykonuje dalszych operacji gdyż widzi tylko operacje wykonywane w petli foreach. Jak mam zrobić żeby program widział dalsze instrukcje?

 else
            {
                czySciezka = true;

                FileStream plik = new System.IO.FileStream(sciezka, FileMode.Open);
                        foreach (var linia in File.ReadLines(sciezka))
                        {
                            nrLinii++;
                            var rozdzielone = linia.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                            x = Convert.ToDouble(rozdzielone[0]);
                            y = Convert.ToDouble(rozdzielone[1]);
                            X[nrLinii] = Convert.ToDouble(x);
                            Y[nrLinii] = Convert.ToDouble(y);
                            textBox1.Text += Convert.ToString(X[nrLinii]) + "\t" + Convert.ToString(Y[nrLinii] + "\r\n");
                        }
}

Nie mam pomysłu co mogę zrobić w tej sytuacji. Pomoże ktoś? Czy jest inny sposób na otwarcie pliku znając do niego ścieżkę. Dalsze instrukcje są wykonywane poza instrukcją else i mają być wykonywane zarówno gdy otwieram plik za pomoca OpenFileDIalog jak i FIleStream

2

A po co próbujesz otwierać ten plik 2 razy, raz przy uzyciu Filestream, pozniej przy uzyciu File.Readlines ? Olej tego Filestreama i uzyj samo File.Readlines

0
kzkzg napisał(a):

A po co próbujesz otwierać ten plik 2 razy, raz przy uzyciu Filestream, pozniej przy uzyciu File.Readlines ? Olej tego Filestreama i uzyj samo File.Readlines

W instrukcji warunkowej if chce albo wybrać plik albo otworzyć ten, którego ścieżkę już znam. To zrobiłem i niby jest ok. Poza instrukcjami warunkowymi mam dalszą część programu która odpowiada za utworzenie równania oraz rysowanie wykresu. I jeśli wczytuje nowy plik za pomocą OpenFileDialog to wykres się ładnie tworzy. Natomiast jeśli otwieram ten plik za pomocą File.ReadLines to nie rysują się ani punkty ani wykres. Wygląda to tak jakby program czytał tylko instrukcje zawarte w pętli foreach. Ma ktoś może jakiś pomysł jak to zmienić? Przepraszam że tyle pytań, ale jeśli chodzi o obsługę plików to naprawdę jestem słaby.
Poniżej dodaję część skryptu (bez napisanych wcześniej funkcji funkcji)

public void button1_Click(object sender, EventArgs e)
{
    int licznik = 0;
    if (czySciezka != true)
    {
        OpenFileDialog dialog = new OpenFileDialog();
        dialog.Filter = String.Format("Plik tekstowy (*.txt)|*.txt;");
        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            plik = dialog.FileName;
            label1.Text = plik;
            foreach (string pelnaLinia in File.ReadLines(plik))
            {
                if (pelnaLinia != String.Empty) licznik++;
            }
            foreach (var linia in File.ReadLines(plik))
            {
                nrLinii++;
                var rozdzielone = linia.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                x = Convert.ToDouble(rozdzielone[0]);
                y = Convert.ToDouble(rozdzielone[1]);
                X[nrLinii] = Convert.ToDouble(x);
                Y[nrLinii] = Convert.ToDouble(y);
            }
            nrLinii = 0;
        }
        sciezka = plik;
        czySciezka = true;
    }
    else
    {
        licznik = 0;
        nrLinii = 0;
        czySciezka = true;

        foreach (var linia in File.ReadLines(sciezka))
        {
            nrLinii++;
            var rozdzielone = linia.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
            x = Convert.ToDouble(rozdzielone[0]);
            y = Convert.ToDouble(rozdzielone[1]);
            X[nrLinii] = Convert.ToDouble(x);
            Y[nrLinii] = Convert.ToDouble(y);
            textBox1.Text += Convert.ToString(X[nrLinii]) + "\t" + Convert.ToString(Y[nrLinii] + "\r\n");
        }
    }
    double min = X[0];
    double max = X[0];
    N = licznik;
    for (int i = 0; i < N; i++)
    {
        chart1.Series[0].Points.AddXY(X[i], Y[i]);
        if (X[i] < min)
            min = X[i];
        if (X[i] > max)
            max = X[i];
    }
    stWiel = Convert.ToInt32(nUD1.Value);

    //macierz wyrazow wolnych [B]
    for (int k = 0; k <= stWiel; k++)
    {
        B[k] = 0;
        for (int i = 0; i < N; i++)
        {
            B[k] += Y[i] * Math.Pow(X[i], k);
        }
    }

    //macierz A
    for (int k = 0; k <= stWiel; k++)
    {
        for (int i = 0; i <= stWiel; i++)
        {
            A[k, i] = 0;
            for (int j = 0; j < N; j++)
            {
                A[k, i] += Math.Pow(X[j], k + i);
            } //for j
        } //for i
    } //for k

    RozwiazUkladRownan();

    labelRow.Visible = true;
    labelRow.Text = "";

    labelRow.Text += "W(x)=";
    for (int j = stWiel; j >= 2; j--)
    {
        labelRow.Text += Math.Round(Wsp[j], 2) + "x^" + j + "+";
    }

    labelRow.Text += Math.Round(Wsp[1], 2) + "x+" + Math.Round(Wsp[0], 2);

    chart1.Series[1].Points.Clear();
    for (double xx = 0.9 * min; xx <= 1.1 * max; xx += 0.01)
    {
        chart1.Series[1].Points.AddXY(xx, W(xx));
    }
}
2

Zamiast dwa razy powtarzać twój kod do wczytywania, w zależności od tego, czy otwierasz już otwarty plik, czy nowy, możesz to zredukować do jednego:

if (!czySciezka)
{
    OpenFileDialog dialog = new OpenFileDialog();
    dialog.Filter = String.Format("Plik tekstowy (*.txt)|*.txt;");
    if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
        plik = dialog.FileName;
        label1.Text = plik;
        czySciezka = true;
    }
}
else
{
    return;
}

int licznik = 0;
foreach (string pelnaLinia in File.ReadLines(plik))
{
    if (pelnaLinia != String.Empty) licznik++;
}

nrLinii = 0;
foreach (var linia in File.ReadLines(plik))
{
    nrLinii++;
    var rozdzielone = linia.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
    x = Convert.ToDouble(rozdzielone[0]);
    y = Convert.ToDouble(rozdzielone[1]);
    X[nrLinii] = Convert.ToDouble(x);
    Y[nrLinii] = Convert.ToDouble(y);
}

// i dalej już wyświetlanie na wykresie i tak dalej
0

Dobra już wszystko działa jak powinno. Dziękuję wszystkim za pomoc :)

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