- Kansion luominen
- Tiedoston luominen
- kansioissa navigointi
- Listaus
- ei komentoa tiedoston poistamiselle (ihan vaan siksi, että jonkun koneelta ei poistuisi mitään tärkeää virheellisen koodin takia)
using System; using System.IO; using System.Text; using System.Collections.Generic; namespace ConsoleUI { class Program { static void Main(string[] args) { Variables variables = new Variables(askDrive()); do { variables.getDir(); Console.WriteLine("\nSyötä komento: "); Console.ForegroundColor = ConsoleColor.Red; variables.command = Console.ReadLine().ToLower(); Console.ResetColor(); if (variables.command == "/help") { Console.Write("\nmkDir [kansion nimi] - luo kansion\nmkFile [tiedoston nimi + pääte] - luo tiedoston\ncd .. - edellinen kansio\ncd [kansio] - menee kansioon\nls - listaa nykyisen kansion sisällön\nPaina jotain jatkaaksesi..."); Console.ReadKey(true); } else if (variables.command.Contains("mkdir")) { makeDirectory(variables); } else if (variables.command == "ls") { printDirectory(variables); } else if (variables.command.Contains("cd")) { variables.Dir = changeDirectory(variables); } else if (variables.command.Contains("mkfile")) { makeFile(variables); } else { Console.Write("\nKomentoa ei tunnistettu.\n/help - lista komennoista\n\nPaina jotain jatkaaksesi..."); Console.ReadKey(true); } Console.Clear(); } while (variables.going == true); } static void makeFile(Variables variables) { try { do { try { string[] parts = variables.command.Split(); StringBuilder sb = new StringBuilder(); if (parts[0] != "mkfile") { break; } else { for (int i = 1; i < parts.Length; i++) { sb.Append(parts[i] + " "); } File.Create(variables.Dir + sb.ToString().Trim()); Console.Write("Tiedosto on luotu onnistuneesti..."); Console.ReadKey(true); } } catch (Exception) { Console.Write("Error 404..."); Console.ReadKey(true); } } while (false); } catch (Exception) { Console.Write("Error 404..."); Console.ReadKey(true); } } static string changeDirectory(Variables variables) { try { do { string[] parts = variables.command.Split(' '); if (parts[0] != "cd") break; DirectoryInfo dirInfo = new DirectoryInfo(variables.Dir); StringBuilder sb = new StringBuilder(); if(parts.Length == 2 && parts[1] == "..") { if (variables.lastDir != null) { variables.Dir = variables.lastDir[variables.lastDir.Count - 1]; variables.lastDir.RemoveAt(variables.lastDir.Count - 1); } else { Console.Write("Et voi pakittaa enempää..."); Console.ReadKey(true); } } for (int i = 1; i < parts.Length; i++) { sb.Append(parts[i] + " "); } string directory = sb.ToString().Trim(); foreach (DirectoryInfo dInfo in dirInfo.GetDirectories()) { if(dInfo.Name == directory) { variables.lastDir.Add(variables.Dir); return variables.Dir + dInfo.Name + @"\"; } } } while (false); } catch (Exception) { Console.Write("error 404..."); Console.ReadKey(true); } return variables.Dir; } static void printDirectory(Variables variables) { DirectoryInfo dirInfo = new DirectoryInfo(variables.Dir); foreach (FileInfo f in dirInfo.GetFiles()) { Console.WriteLine(f.Name); } foreach (DirectoryInfo dInfo in dirInfo.GetDirectories()) { Console.Write(dInfo.Name); Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(" [kansio]"); Console.ResetColor(); } Console.ReadKey(true); } static void makeDirectory(Variables variables) { try { do { try { string[] parts = variables.command.Split(); StringBuilder sb = new StringBuilder(); if (parts[0] != "mkdir") { break; } else { for (int i = 1; i < parts.Length; i++) { sb.Append(parts[i] + " "); } Directory.CreateDirectory(variables.Dir + sb.ToString().Trim()); Console.Write("Kansio on luotu onnistuneesti..."); Console.ReadKey(true); } } catch (Exception) { Console.Write("Error 404..."); Console.ReadKey(true); } } while (false); } catch (Exception) { Console.Write("Error 404..."); Console.ReadKey(true); } } static string askDrive() { string[] drives = Directory.GetLogicalDrives(); string selectedDrive = @"Error:\"; bool going = true; if (drives.Length == 0) { Console.WriteLine("Levyjä ei löytynyt"); } else { string command; do { Console.WriteLine("Levyt:\n----------"); foreach (string drive in drives) { Console.WriteLine(drive); } Console.WriteLine("----------"); Console.WriteLine("Valitse levy:"); Console.ForegroundColor = ConsoleColor.Red; command = Console.ReadLine().ToUpper(); Console.ResetColor(); foreach (string drive in drives) { if(drive.Contains(command) && command != ":" && command != @"\" && command != @":\") { selectedDrive = command; going = false; break; } } Console.Clear(); } while (going == true); } return selectedDrive; } } }
using System; using System.Collections.Generic; using System.IO; using System.Text; namespace ConsoleUI { class Variables { public List<string> lastDir; public string Dir { get; set; } public bool going { get; set; } public string command { get; set; } public Variables(string drive) { if (drive.Contains(@":\")) { Dir = drive; } else { Dir = drive + @":\"; } going = true; lastDir = new List<string>(); } public void getDir() { Console.WriteLine($"Polku: { Dir }"); } } }
Voisitteko kommentoida koodia :D
AtskaFin kirjoitti:
Voisitteko kommentoida koodia :D
En löytänyt koodistasi virheitä, mutta osaan C#:tä vain vähän enkä ole varma siitä, ettei siinä olisi virheitä. Hyvä koodi, mutta en ole siitä kuitenkaan varma, koska ohjelmoin enemmän PHP:llä, C:llä ja Pythonilla.
Voisiko viisaampi vastata, että onko viisasta luoda variables - luokka, vai onko se huonoa ohjelmointityyliä. Otin kyseisen menetelmän käyttöön yhdestä ohjelmointivinkistä.
Ei ole viisasta luoda Variables-luokkaa. (Millaisesta vinkistä olet tuon keksinyt?) Yhden luokan kuuluu kuvata ainakin jossain määrin itsenäistä kokonaisuutta, ja ”muuttujat” ei ole itsenäinen kokonaisuus. Muuttujien kuuluu olla siellä, missä niitä käytetään. Erityisen epäviisasta on, että kyseinen luokka kuitenkin tulostaa polun komentoriville...
Tässä tapauksessa sinulla on vain kaksi laajemmin käytettyä muuttujaa, command ja lastDir. Voisit hyvin välittää ne parametreina niille metodeille, joissa niitä tarvitaan, tai voisit laittaa ne luokan omiksi jäseniksi.
Muuttuja variables.going näyttää olevan täysin turha, koska et missään muuta sen arvoa falseksi eli se on aina true.
Turhat do-while-silmukkasi vain sotkevat koodia. Rakenteessa on siis siltä osin parannettavaa. Esimerkiksi metodissa makeDirectory on aivan turhaan kolme sisäkkäistä lohkoa (try, do, try), kun yksi try-lohko ajaisi aivan saman asian.
Joka metodissa on turha tarkastaa enää komentoa, koska olet jo päättänyt aiemmin, että kyseinen komento kuuluu kyseiseen metodiin. Voisit välittää parametrina vain komennon loppuosan. Melko pöljää on myös ensin katkaista komento ja sitten yhdistää palat uudestaan silmukalla. Turhaa työtä!
Toistuvat ReadKey-kohdat lähinnä vaikeuttavat ohjelman käyttöä, varsinkin, kun tulosteesta ei käy ilmi, että ohjelma odottaa napinpainallusta.
Useiden if-lauseiden sijaan komentojen lisääntyessä käytettäisiin usein jonkinlaista taulukkoa tai listaa, jossa olisi listattu komennot ja niitä vastaavat käsittelyfunktiot.
Pienissä ohjelmissa pelkkien staattisten metodien käyttö on ihan toimivaa, mutta isommassa ohjelmassa kannattanee vähitellen pyrkiä siihen, että Main-metodissa luodaan olio, jossa varsinainen ohjelman toiminta on.
Tällä hetkellä ohjelmasi mahtuu yhteen luokkaan, mutta jos haluat harjoitella koodin paloittelua, voisit jakaa tämän käyttöliittymään, jossa olisi kaikki komennon lukemiseen ja tiedon tulostamiseen liittyvä, ja taustatoteutukseen, jossa olisi kaikki muu toiminta mutta ei mitään suoraan käyttäjälle näkyvää. Tosin tämä vaatisi, että toiminnoissa olisi jotain varsinaista toteutettavaa.
Nykyisessä virheenkäsittelytavassasi ei ole paljonkaan järkeä. "Error 404" ei kerro käyttäjälle mitään vaan on pikemminkin harhaanjohtava, sillä 404 on tunnettu HTTP-virhe ”Not Found” mutta tarkoittaa sinun ohjelmassasi jotain aivan muuta. Nyt käsittelet kaikki virheet yhtä epämääräisesti. Yleensä kannattaa selvittää, mitä poikkeuksia voi tulla missäkin tilanteissa, ja havaita juuri ne poikkeukset ja tuottaa niistä käyttäjälle ymmärrettävät virheilmoitukset.
Ohjelma voisi näyttää paljon yksinkertaisemmalta esim. näin:
using System; using System.IO; public class LyhennettyEsimerkki { public static void Main(string[] args) { while (true) { // Pyydetään rivi. Console.Write(Directory.GetCurrentDirectory() + "> "); string line = Console.ReadLine(); if (line == null || line == "exit") { break; } // Katkaistaan syötetty rivi. string[] parts = line.Trim().Split(' ', 2); string command = parts[0]; string param = parts.Length <= 1 ? null : parts[1].Trim(); // Käsitellään komennot ja niiden virheet. try { if (command == "cd") { // Yksinkertainen komento suoraan tässä. Directory.SetCurrentDirectory(param); } else if (command == "mkdir") { Directory.CreateDirectory(param); } else if (command == "ls") { // Monimutkainen komento erillään. ls(param); } else { Console.WriteLine("Tuntematon komento."); } } catch (UnauthorizedAccessException) { Console.WriteLine("Ei käyttöoikeutta."); } catch (PathTooLongException) { Console.WriteLine("Liian pitkä polku."); // Ja niin edelleen... Viimeiseksi: } catch (Exception) { Console.WriteLine("Määrittämätön virhe."); } } } private static void ls(string param) { DirectoryInfo dirInfo = new DirectoryInfo(param == null ? "." : param); foreach (DirectoryInfo d in dirInfo.GetDirectories()) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("[" + d.Name + "]"); Console.ResetColor(); } foreach (FileInfo f in dirInfo.GetFiles()) { Console.WriteLine(f.Name); } } }
Tosin SetCurrentDirectory näyttää hukkaavan symboliset linkit polusta, mutta tämä ei varmasti Windows-käyttäjää haittaa.
string param = parts.Length <= 1 ? null : parts[1].Trim();
Voisiko joku selventää mitä tässä tapahtuu?
Ehkä pitäisi kysyä, että mikä siinä tarkalleen ottaen on epäselvää jos kuitenkin olet C#:llä koodannut (tai millä tahansa C-sukuisella kielellä)...
Mutta eipä siinä nyt niin montaa osaa ole etteikö niitä voisi selittää kaikkiakin:
string param = parts.Length <= 1 ? null : parts[1].Trim(); /*^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^^^^^^^^ 1 2 3 4 5 1: Määritellään merkkijonomuuttuja param 2: Määritelty muuttuja alustetaan seuraavalla (kolmiosaisen operaattorin) tuloksella 3: Totuusarvo, joka määrää onko tulos 4 vai 5: - onko taulukon parts pituus enintään 1 4: Tosi-osuus: null 5: Epätosi-osuus: Taulukon parts toinen (indeksi 1) arvo, josta on poistettu alun ja lopun tyhjämerkit (.Trim()). */
Siis tuo kysymysmerkki ja sen jälkeinen oli epäselvää (en siis ole sitä käyttänyt), eli meneekö se seuraavanlaisesti:
Int num = 1 < 2 ? (arvo jos tosi) : (arvo jos epätosi)
Muistelisin nähneeni jossain myös tuplakysymysmerkkiä (??)
AtskaFin kirjoitti:
eli meneekö se seuraavanlaisesti:
Int num = 1 < 2 ? (arvo jos tosi) : (arvo jos epätosi)
No siis juu, joskin koska esimerkissäsi 1 < 2 on aina epätosi, niin voisi sanoa:
int num = 1 < 2 ? (ei koskaan palauta tätä) : (palauttaa aina tämän);
?: on siis operaattori, joka ottaa kolme parametria (aivan kuten vaikka + ottaa kaksi parametria) ja palauttaa 2. parametrin jos 1. on tosi ja 3. parametrin jos 1. on epätosi.
Tässä kannattaa myös huomioida että noista toista ei myöskään koskaan suoriteta.
Eli: r = x ? y : z
on sama kuin: if (x) { r=y; } else { r=z; }
https://en.wikipedia.org/wiki/?:
Hyvin yleinen samassa muodossa C# lisäksi esim. C, C++, Javascript, Java, PHP...
AtskaFin kirjoitti:
Muistelisin nähneeni jossain myös tuplakysymysmerkkiä (??)
C#:ssa on lisäksi tosiaan muitakin ? sisältäviä operaattoreita, kuten ??, joka ottaa kaksi parametria ja palauttaa ensimmäisen, jos ensimmäinen ei ole null ja jälkimmäisen jos ensimmäinen on null.
Eli siis: x ?? y
on vastaava kuin: x==null ? y : x
Sitten on (uusimmissa versioissa, olisiko C# 6 alkaen) ?. ja ?[] jotka kutsuu olion jäsentä tai indeksiä jos olio ei ole null tai muuten palauttaa null.
Eli siis: x?.y
on vastaava kuin: x==null ? null : x.y
Ja: x?[y]
on vastaava kuin: x==null ? null : x[y]
Huomenna lomilta kotia nii pääsee testaamaan. Sillähän nuo oppii
Onko tämä parempi, kuin alkuperäinen:
using System; using System.IO; using System.Text; using System.Collections.Generic; namespace ConsoleUI { class Program { static void Main(string[] args) { string command; string levy = askDrive(); while (true) { Console.Write(Directory.GetCurrentDirectory() + "> "); command = Console.ReadLine(); if (command.ToLower() == "stop") break; string[] parts = command.Trim().Split(' '); command = parts[0]; StringBuilder sb = new StringBuilder(); for (int i = 1; i < parts.Length; i++) sb.Append(parts[i] + " "); string param = sb.ToString().Trim(); try { if (command == "cd") { Directory.SetCurrentDirectory(param); } else if (command == "mkdir") { Directory.CreateDirectory(param); } else if (command == "ls") { ls(); } else if (command == "help") { // Kertoo käytettävissä olevat komennot Console.WriteLine("\ncd [kansio n] - siirry kansioon n\nmkdir [s] - luo kansion s\nls - lista kansion sisällöstä\nclear - siivoaa konsolin\n"); wait(); Console.WriteLine(); } else if (command == "clear") { // Siivoaa konsolin Console.Clear(); } else if (command == "mkfile") { // Luo tiedoston makefile(param); } else { Console.WriteLine("Tuntematon komento."); } } catch (UnauthorizedAccessException) { Console.WriteLine("Ei käyttöoikeutta."); } catch (PathTooLongException) { Console.WriteLine("Liian pitkä polku."); } catch (Exception) { Console.WriteLine("Määrittämätön virhe."); } } } // Luo tiedoston static void makefile(string param) { try { File.Create(param); Console.Write("Tiedosto on luotu onnistuneesti..."); wait(); } catch (UnauthorizedAccessException) { Console.WriteLine("Ei käyttöoikeutta."); } catch (Exception) { Console.WriteLine("Määrittämätön virhe"); } } // Tulostaa kansion sisällön private static void ls() { DirectoryInfo dirInfo = new DirectoryInfo(Directory.GetCurrentDirectory()); foreach (DirectoryInfo d in dirInfo.GetDirectories()) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("[" + d.Name + "]"); Console.ResetColor(); } foreach (FileInfo f in dirInfo.GetFiles()) { Console.WriteLine(f.Name); } } // Kysyy käyttäjältä kiintolevyn, mitä hän haluaa hallita static string askDrive() { string[] drives = Directory.GetLogicalDrives(); string drive = null; // Jatketaan, kunnes käyttäjä on valinnut levyn while (true) { try { Console.WriteLine("Levyt:\n----------"); foreach (string item in drives) Console.WriteLine(item); Console.WriteLine("----------\nValitse levy:"); drive = readInput().Replace(':', ' ').Replace('\\', ' '); Directory.SetCurrentDirectory(drive.Trim() + @":\"); Console.Clear(); break; } catch (DirectoryNotFoundException) { Console.WriteLine("\nEtsimääsi levyä ei löytynyt"); wait(); Console.Clear(); } catch (IOException) { Console.WriteLine("\nEtsimääsi levyä ei löytynyt"); wait(); Console.Clear(); } } return drive; } // Metodi joka odottaa, että käyttäjä painaa jotain static void wait() { Console.Write("Press any key to continue..."); Console.ReadKey(true); } // Metodi joka lukee käyttäjältä syötteen ja palauttaa sen static string readInput() { Console.ForegroundColor = ConsoleColor.Red; string input = Console.ReadLine().ToUpper(); Console.ResetColor(); return input; } } }
Käytin apuna tuota metabolixin koodia, mutta siinä ei ollut mahdollista siirtyä kansioon, jonka nimessä on välilyönti (esim. cd program files)
AtskaFin kirjoitti:
Käytin apuna tuota metabolixin koodia, mutta siinä ei ollut mahdollista siirtyä kansioon, jonka nimessä on välilyönti (esim. cd program files)
Miten niin ei? Siinä nimenomaan jaetaan komento enintään kahteen osaan ja saadaan siis koko loppuosa ilman tuollaista purkkaviritelmää, jossa ensin katkaistaan ja sitten uudestaan kootaan teksti.
No ainakin kun testasin, niin ei toiminut, mutta saattoihan olla että kirjoitin vaikka väärin
Jospa vaikka tarkastaisit, mitä olet kirjoittanut.
teksti.Split(' ', 2); // Katkaisu 2 osaan välilyönnin kohdalta. teksti.Split(new char[] {' '}, 2); // Versio vanhemmille kääntäjille.
Mulla on vs2017, mutta miks mulla ei toimi toi uudempi vaihtoehto?
Onko tämä huonoa ohjelmointityyliä?
saldo = maara > 0 && saldo + maara <= tilavuus ? saldo + maara : maara > 0 ? 100 : 0;
Tyylissä ei ole vikaa, koska sen toiminnan näkee helposti. Kuitenkin siistisin siitä pois kahteen kertaan tehdyn vertailun "maara > 0"
saldo = maara <= 0 ? 0 : saldo + maara <= tilavuus ? saldo + maara : 100
Käyttäisin vielä selkeyden vuoksi sulkeita:
saldo = (maara <= 0 ? 0 : saldo) + (maara <= tilavuus ? saldo + maara : 100)
(Menivätkö oikein?)
HTML5, tuo on kyllä väärin sulutettu. Operaattori ?: on yksi viimeisistä laskettavista, joten lausekkeen laskujärjestys on tämä:
saldo = ( (maara <= 0) ? 0 : ( ((saldo + maara) <= tilavuus) ? (saldo + maara) : 100 ) );
Kovin pitkiä lausekkeita ei kannata merkata ?:-operaattorilla, koska lauseketta on usein vaikea lukea.
Edellisessä esimerkissä hieman omituinen yksityiskohta on myös se, että jos saldo + määrä > tilavuus, palautetaan 100. Miksi juuri 100? Miksi ei esimerkiksi tilavuus, jos kerran se on asetettu rajaksi?
Jos lausekkeen tarkoitus on asettaa rajoja, usein on selvempää käyttää Min- ja Max- ja Clamp-metodeita tai laskea jokin järkevä välitulos.
// Epäselvää... x = x < 0 ? 0 : x > 100 ? 100 : x; maksu = velka > varat ? varat : velka; // Selvää! x = Math.Clamp(x, 0, 100); maksu = Math.Min(varat, velka);
AtskaFin kirjoitti:
Mulla on vs2017, mutta miks mulla ei toimi toi uudempi vaihtoehto?
Enpä tuota tiedä. Dokumentaatiossakaan ei ole pelkällä yhdellä merkillä toimivaa versiota metodista, mutta silti se toimii minulla ja esimerkiksi palvelussa https://try.dot.net/.
AtskaFin kirjoitti:
Mulla on vs2017, mutta miks mulla ei toimi toi uudempi vaihtoehto?
Olisitko valinnut käyttöön muun .Net frameworkin kuin .Net Core 2.0 tai uudempi. (Esim .Net 4.8 Preview tai vanhempi)
Metabolix kirjoitti:
Enpä tuota tiedä. Dokumentaatiossakaan ei ole pelkällä yhdellä merkillä toimivaa versiota metodista
Kyllä minulla näkyy, kun valitsee .Net Core 2.0 tai uudemman. https://docs.microsoft.com/en-us/dotnet/api/system.string.split?view=netcore-2.0
Tosin tuolla ei sanota, että useimmissa noista StringSplitOptionsille on annettu oletuarvo .None, jolloin sitä ei ole pakko määritellä.
Minulla on käytössä .net framework 4.7.2, mutta kumpaa kannattaisi käyttää projektia luodessa: Console App(.net core), vai Console App(.net framework). Johtuisiko tuo toiminta ero siitä, että olen käyttänyt tuota frameworkia?
Edit.
teksti.Split(' ', 2); // Toimii .net coressa teksti.Split(new char[] {' '}, 2); // Toimii .net frameworkissa
Kyllä, tuo ensimmäinen ei toimi .Net frameworkissa. Tosin muuttaisin kommentit muotoon
teksti.Split(' ', 2); // Toimii vain .net Coressa teksti.Split(new char[] {' '}, 2); // Toimii sekä .net Coressa että .net Frameworkissa
Jos haluat että tuotoksesi toimii muuallakin kuin Windowsissa, niin sanoisin että kannattaa ehdottomasti käyttää .Net Corea. Jos taas haluat että tuotoksesi toimii nimenomaan Windowissa ja sinulla on tarve käyttää sellaisia asioita joita .Net Coresta ei löydy, niin sitten luonnollisesti valinta kallistuu .Net Frameworkin suuntaan.
Sanoisin, että konsolisovelluksessa luultavasti kumpikin toimii ihan yhtä hyvin.
Tuossa vielä yksi artikkeli niiden eroista:
https://stackify.com/net-core-vs-net-framework/
Kyllä tässä tapauksessa alan käyttämään .net corea.
Pitää varmaan käydä läpi kaikki Math -luokan tarpeelliset metodit. En tiennyt aiemmin tuosta Math.Clamp -metodista ja siitä on kyllä hyötyä.
Aihe on jo aika vanha, joten et voi enää vastata siihen.