Permalänk
Medlem

Rekursiv metod (C#)

Tjena,

Jag skulle behöva hjälp med följande uppgift: Skapa en rekursiv metod som söker en fil med ett speciellt namn i en katalog och den katalogens underkataloger. Exceptions som kan kastas i samband med sökningen i filträdet ska hanteras på lämpligt sätt. Användaren av programmet ska ange namnet på den fil som ska sökas och den katalog i vilken sökningen ska börja.

Har aldrig skrivit en rekursiv metod tidigare men jag vet att en rekursiv metod/funktion är en funktion som anropar sig själv. Sen har jag lyckats googla fram att man ska tänka på 3 saker när man skriver rekursiva funktioner. Det är: när man ska avsluta funktionen, när man ska göra om samma sak igen dvs anropa sig själv, och vad man ska returnera och hur man ska göra det. Men vet inte riktigt hur jag ska börja på uppgiften ovan, någon som kan hjälpa mig?

Permalänk
Medlem

Starthjälp: kör funktionen i huvudmappen, därefter anropar du den igen på en mapp som eventuellt ligger i huvudmappen. Ligger det inga mappar i mappen funktionen körs i (och den har sökt igenom alla filer, alternativt hittat den eftersökta filen) avslutas metoden.
Är lite sömnig atm så du får ursäkta om det blev lite rörigt

Visa signatur

Dator: i7 4770K, MSI RTX 2060, 16 GB RAM, blandade SSD:s, en gul kabel från HDD till modermodemet

Permalänk
Medlem
Skrivet av JoeBlack:

Starthjälp: kör funktionen i huvudmappen, därefter anropar du den igen på en mapp som eventuellt ligger i huvudmappen. Ligger det inga mappar i mappen funktionen körs i (och den har sökt igenom alla filer, alternativt hittat den eftersökta filen) avslutas metoden.
Är lite sömnig atm så du får ursäkta om det blev lite rörigt

Vilken huvudmapp menar du?

Permalänk
Hedersmedlem
Skrivet av mhj:

Vilken huvudmapp menar du?

Den du startar sökningen i (och som kan innehålla undermappar i vilka du också vill söka).

Permalänk
Medlem
Skrivet av mhj:

Vilken huvudmapp menar du?

Skrivet av Elgot:

Den du startar sökningen i (och som kan innehålla undermappar i vilka du också vill söka).

Elgot svarade på frågan, förtydligar bara lite; Vill du söka på t.ex. hela C:-volymen är C:\ huvudmappen och t.ex. C:\users\ en undermapp där funktionen kommer köras igen (och C:\users\JoeBlack ytterligare ett steg i mappstrukturen där funktionen kommer anropas ännu en gång). Vill du bara söka i C:\users\mhj\bilder\ med undermappar är just C:\users\mhj\bilder huvudmappen, då du inte kommer att söka i andra mappar (eller kontrollera andra objekt) i t.ex. C:\users\JoeBlack\notIllegalStuff\.

Visa signatur

Dator: i7 4770K, MSI RTX 2060, 16 GB RAM, blandade SSD:s, en gul kabel från HDD till modermodemet

Permalänk
Medlem
Skrivet av JoeBlack:

Elgot svarade på frågan, förtydligar bara lite; Vill du söka på t.ex. hela C:-volymen är C:\ huvudmappen och t.ex. C:\users\ en undermapp där funktionen kommer köras igen (och C:\users\JoeBlack ytterligare ett steg i mappstrukturen där funktionen kommer anropas ännu en gång). Vill du bara söka i C:\users\mhj\bilder\ med undermappar är just C:\users\mhj\bilder huvudmappen, då du inte kommer att söka i andra mappar (eller kontrollera andra objekt) i t.ex. C:\users\JoeBlack\notIllegalStuff\.

Okej har börjat såhär, men vet inte om det är rätt eller hur jag ska gå vidare till nästa steg:

private string RekursivMetod(string path, string filnamn) { string [] getFiles = Directory.GetFiles(txtKatalog.Text); filnamn = txtFilnamn.Text; foreach (string file in getFiles) { if (File.Exists(filnamn)) { break; } } }

Permalänk
Medlem
Skrivet av mhj:

Okej har börjat såhär, men vet inte om det är rätt eller hur jag ska gå vidare till nästa steg:

private string RekursivMetod(string path, string filnamn) { string [] getFiles = Directory.GetFiles(txtKatalog.Text); filnamn = txtFilnamn.Text; foreach (string file in getFiles) { if (File.Exists(filnamn)) { break; } } }

Nu är jag inte sådär jättehaj på just C#, men vad jag kan se anropar du inte den rekursiva metoden i sig själv. Nu verkar det som du bara gör en lista av alla filer i den aktuella mappen, men inte kör metoden på eventuella undermappar. Som det är nu kommer du endast hitta filen om den är i huvudmappen.

Visa signatur

Dator: i7 4770K, MSI RTX 2060, 16 GB RAM, blandade SSD:s, en gul kabel från HDD till modermodemet

Permalänk
Hedersmedlem

Ponera att din root-mapp (mappen där du startar sökningen) innehåller blandade mappar och filer. Tänk att filsystemet är uppbyggt som ett vertikalt träd med root-mappen högst upp, och dess filer/mappar i nivån nedanför. Denna algoritm gör då en s k DFS (depth first search):

1. Sök igenom alla filer/mappar i root, om sökt fil hittas, returnera true (eller vad du nu ska returnera).
2. Om du stöter på en mapp, anropa funktionen igen (rekursivt anrop).
3. Returnera false.

Jag har inte testat detta, och det kan tänkas att det behövs lite mer logik, men konceptet bör vara rätt.

Permalänk
Medlem
Skrivet av widL:

Ponera att din root-mapp (mappen där du startar sökningen) innehåller blandade mappar och filer. Tänk att filsystemet är uppbyggt som ett vertikalt träd med root-mappen högst upp, och dess filer/mappar i nivån nedanför. Denna algoritm gör då en s k DFS (depth first search):

1. Sök igenom alla filer/mappar i root, om sökt fil hittas, returnera true (eller vad du nu ska returnera).
2. Om du stöter på en mapp, anropa funktionen igen (rekursivt anrop).
3. Returnera false.

Jag har inte testat detta, och det kan tänkas att det behövs lite mer logik, men konceptet bör vara rätt.

Okej blir det såhär då eller?

private bool RekursivMetod(string path, string filnamn) { string [] getFiles = Directory.GetFiles(txtKatalog.Text); filnamn = txtFilnamn.Text; if (File.Exists(Path.Combine(path, filnamn))) { return true; } foreach (string subDir in Directory.GetDirectories(path)) { return RekursivMetod(subDir, filnamn); } return false; }

Permalänk
Medlem
Skrivet av mhj:

Okej blir det såhär då eller?

private bool RekursivMetod(string path, string filnamn) { string [] getFiles = Directory.GetFiles(txtKatalog.Text); filnamn = txtFilnamn.Text; if (File.Exists(Path.Combine(path, filnamn))) { return true; } foreach (string subDir in Directory.GetDirectories(path)) { return RekursivMetod(subDir, filnamn); } return false; }

Det ser bättre ut! Du kanske dock vill returnera själva filen eller sökvägen istället för att bara returnera true som i princip säger: Ja, filen finns någonstans i den genomsökta mappstrukturen.

Skickades från m.sweclockers.com

Visa signatur

Dator: i7 4770K, MSI RTX 2060, 16 GB RAM, blandade SSD:s, en gul kabel från HDD till modermodemet

Permalänk
Medlem
Skrivet av mhj:

Okej blir det såhär då eller?

private bool RekursivMetod(string path, string filnamn) { string [] getFiles = Directory.GetFiles(txtKatalog.Text); filnamn = txtFilnamn.Text; if (File.Exists(Path.Combine(path, filnamn))) { return true; } foreach (string subDir in Directory.GetDirectories(path)) { return RekursivMetod(subDir, filnamn); } return false; }

Efter en snabbtitt så ser jag en sak du skulle behöva fixa. I foreach slingan så kör du direkt en return. Då kommer programmet hoppa ur funktionen efter första subdir du går igenom. Så om du då har t.ex. 10 subdirectories så kör du rekursiva funktionen på första subdiren och sedan returnerar du det värdet, dvs du skippar de efterföljande 9 subdirectories. Jag skulle nog ha ändrat foreach loopen till följande:

foreach (string subDir in Directory.GetDirectories(path)) { if (RekursivMetod(subDir, filnamn)) { return true; } }

Note: Ovanstående kod är "torrkodad" utan att jag själv har testat kompilera det. Så det kan förekomma små fel.

Permalänk
Medlem
Skrivet av JoeBlack:

Det ser bättre ut! Du kanske dock vill returnera själva filen eller sökvägen istället för att bara returnera true som i princip säger: Ja, filen finns någonstans i den genomsökta mappstrukturen.

Skickades från m.sweclockers.com

Okej men hur returnerar jag filen? För det går ju inte att skriva return filnamn?

Skrivet av Skyflyer:

Efter en snabbtitt så ser jag en sak du skulle behöva fixa. I foreach slingan så kör du direkt en return. Då kommer programmet hoppa ur funktionen efter första subdir du går igenom. Så om du då har t.ex. 10 subdirectories så kör du rekursiva funktionen på första subdiren och sedan returnerar du det värdet, dvs du skippar de efterföljande 9 subdirectories. Jag skulle nog ha ändrat foreach loopen till följande:

foreach (string subDir in Directory.GetDirectories(path)) { if (RekursivMetod(subDir, filnamn)) { return true; } }

Note: Ovanstående kod är "torrkodad" utan att jag själv har testat kompilera det. Så det kan förekomma små fel.

Okej tack för korrigeringen!

Permalänk
Hedersmedlem
Skrivet av mhj:

Okej men hur returnerar jag filen? För det går ju inte att skriva return filnamn?

Jodå, men du måste ändra lite mer än bara det (just nu förväntas funktionen returnera en bool, till exempel).

Permalänk
Medlem

*host*, *host*
Directory.GetFiles("c:\\", "file.txt", SearchOption.AllDirectories);

Det sagt löser det inte din uppgift I alla fall, lite psudokod som en start, dvs denna körs inte och inte alls

public List files = FindFilesInFolder(string file, string path) { List resultat = new list; foreach(newPath in GetFoldersInPath(path)) resultat.Add(FindFilesInFolder(file, newPath); foreach (files in GetFilesInCurrentPath(path)) if files == file resultat.Add(file); return resultat }

Visa signatur

Speldator: Ryzen 7800X3D, 64GB DDR5, RTX 5090
Server: i7-8700k, 32GB DDR4, RTX2080
Steam deck, Rog Ally + de fiesta konsoler.

Permalänk
Medlem
Skrivet av Elgot:

Jodå, men du måste ändra lite mer än bara det (just nu förväntas funktionen returnera en bool, till exempel).

Sant då kan man ju bara casta om stringen till en bool. Är det här en korrekt metod nu?

private bool RekursivMetod(string path, string filnamn) { string [] getFiles = Directory.GetFiles(txtKatalog.Text); filnamn = txtFilnamn.Text; if (File.Exists(Path.Combine(path, filnamn))) { return Convert.ToBoolean(filnamn); } foreach (string subDir in Directory.GetDirectories(path)) { if (RekursivMetod(subDir, filnamn)) { return true; } } return false; }

Skrivet av MugiMugi:

*host*, *host*
Directory.GetFiles("c:\\", "file.txt", SearchOption.AllDirectories);

Det sagt löser det inte din uppgift I alla fall, lite psudokod som en start, dvs denna körs inte och inte alls

public List files = FindFilesInFolder(string file, string path) { List resultat = new list; foreach(newPath in GetFoldersInPath(path)) resultat.Add(FindFilesInFolder(file, newPath); foreach (files in GetFilesInCurrentPath(path)) if files == file resultat.Add(file); return resultat }

Förstod inte riktigt ditt inlägg, fungerar det inte att göra som jag gjorde eller? Det där är väl bara ett annat sätt att lösa det på genom att lägga till filerna i en lista.

Permalänk
Hedersmedlem
Skrivet av mhj:

Sant då kan man ju bara casta om stringen till en bool. Är det här en korrekt metod nu?

Nja, en sådan konvertering känns väl kanske lite tveksam. Fundera istället på om man nödvändigtvis måste ha en bool.

Permalänk
Medlem
Skrivet av Elgot:

Nja, en sådan konvertering känns väl kanske lite tveksam. Fundera istället på om man nödvändigtvis måste ha en bool.

Menar du att det kanske är smidigare om metoden har en string returtyp?

Permalänk
Medlem
Skrivet av mhj:

Menar du att det kanske är smidigare om metoden har en string returtyp?

Det eller att skicka in en strängreferens i den rekursiva funktionen, om du nu vill fortsätta använda bool...

Permalänk
Medlem
Skrivet av Dalton Sleeper:

Det eller att skicka in en strängreferens i den rekursiva funktionen, om du nu vill fortsätta använda bool...

Okej då kör jag på en string returtyp i metoden istället för en bool. Men vad ska jag ändra i if-satsen(får error på denna rad: iff (RekursivMetod(subDir, filnamn))) då samt vad ska vara istället för false efter foreach loopen?

Permalänk
Medlem
Skrivet av mhj:

Okej då kör jag på en string returtyp i metoden istället för en bool. Men vad ska jag ändra i if-satsen(får error på denna rad: iff (RekursivMetod(subDir, filnamn))) då samt vad ska vara istället för false efter foreach loopen?

Exempelvis kan du ju kolla om det returneras en sträng alls (string.empty), eller längden > 0.

Permalänk
Medlem
Skrivet av Dalton Sleeper:

Exempelvis kan du ju kolla om det returneras en sträng alls (string.empty), eller längden > 0.

Om jag skriver if(string.Empty) så får jag error igen som säger att den inte kan konvertera string till bool. Ska jag konvertera den då eller har jag skrivit fel inuti parantesen?

Permalänk
Hedersmedlem
Skrivet av mhj:

Om jag skriver if(string.Empty) så får jag error igen som säger att den inte kan konvertera string till bool. Ska jag konvertera den då eller har jag skrivit fel inuti parantesen?

string.Empty är också en string, så det där är i princip som att skriva
if("ujuj")
Det du vill göra är att jämföra returvärdet med string.Empty