04. september 2005 - 16:34Der er
224 kommentarer og 1 løsning
Hjælp til programmering af programmer
Hej Eksperten Jeg har nogle programidéer, som jeg håber, at der en nogen som vil hjælpe mig med. Jeg kender ikke det store til C++, men vil meget gerne lære det.
Mine programidéer håber jeg kan blive til "rigtige" programmer med en pæn GUI og progressbar og det hele. Jeg regner med det er et projekt som vi kan arbejde på i de næste halve til hele år.
I første omgang er det "bare" at lave selve programmet, hvor du måske kan lave en del og give mig en lille programdel for som skal programmeres. Jeg vil meget gerne står for testningen af programmet. Mine programidéer kan selvfølgelig skrives mere detaljeret. Jeg regner med, at vi kan udforme en sådan liste i fællesskab. Projektet er ulønnet!
Er du interesseret i projektet kan du skrive til mig på nph12@hotmail.com
Første program
Programmet tjekker om mapper og filer på ens USB-nøgle (eller et bibliotek) allerede findes på computeren.
Der skal laves en status-side, hvor der bliver vist: 1) Findes mapper (med indhold) eller filer allerede 2) Hvis de findes på computeren, hvor ligger de 3) Hvilken version er nyest 4) Mulighed for at kopiere eller synkronisere mapper og filer
Andet program
Programmet kopierer billeder fra ens medie f.eks. memory stick (eller et bibliotek) over til biblioteket med ens billeder.
Programmet skal kunne følgende:
1) Undersøge som billederne allerede lægger på computeren (med samme indhold, men kan godt hedde noget forskelligt) 2) Kopiere kun nye billeder over på computeren 3) Lægge billeder i mapper efter forskellige kriterier som f.eks. dato. Alle billeder med en bestemt dato bliver lagt i samme mappe f.eks. 22-08-2005 (eller hvilket andet format man ønsker at mappe skal hedde) 4) Gennemløbe alle ens billeder og navngive dem efter dato f.eks. DSC00001, DSC00002 osv
Bemærkning Det var måske en idé at samme de to programmer til et, hvis man kan….
Hvis vi kan dele programmerne op i små bidder, så det er til at overskue vil det være en meget stor hjælp. Hvis jeg laver en detaljeret liste over hvad programmer skal kunne, at vi nok mere planlægge hvad der skal laves. Lad os tage en programbid af gangen og så bygge videre på det.
Kan vi tilføje en GUI med tilhørende progressbar til programmerne, når vi er færdige med programmerne?
Jeg synes at du skal starte med at lave et program der kan vise hvilke filer der ligger et bestem sted, også i undermapper. Der er nogle eksempler som du kan starte med her: http://home20.inet.tele.dk/midgaard/sample.html#listfiles
Du skal også lave en funktion til checke om to filer en ens. Her bliver du nok nød til at åbne filerne på vanlig vis og læse dem byte for byte. Du kan evt bruge stat() til at finde ud af hvor store de er.
Til at kopiere filer kan du bruge CopyFileEx(), den kan kobles sammen med en progress bar.
Har du arbejdet med GUI før? Hvilken kompiler bruger du?
Jeg foretrækker at besvare spørgsmål her. Bemærk at jeg ikke ønsker at være direkte involveret i at lave programmerne, kun besvare spørgsmål.
Jeg har kigget på nogle af dine eksempler og kigget på de programmer som du lavede til mig på et tidspunkt (checkdup og filedate). http://www.eksperten.dk/spm/401308
Jeg kender lidt til GUI (fra java), men har ikke arbejdet med GUI i C++ (vil gerne lære det, når jeg er kommet så langt).
Hvad skal jeg bruge af teksteditor og kompiler (bruger textpad til html og til tider java)?
Jeg overvejer at lave de to programmer som et program. Jeg har tænkt mig, at slette punkt tre for det "andet program" og punkt fire for det "andet program" skal enten laves som et selvstædigt program eller være en funktion i det nye program.
Jeg har tænkt mig, at det nye program skal gøre det samme som det "første program", men både for billeder (tjekker som indholdet er det samme) og for andre type filer (tjekker filnavn og hvilke et som er det nyeste).
Man skal som det første vælge hvilke drev/biblioteker som skal holdes op imod hinanden (lave en GUI, hvor man kan vælge drev/biblitek, men til at starte med kan man bare skrive selve stien).
Så kommer selve status-siden, hvor indholdet på ens USB-nøgle bliver hold op imod indholdet på ens computer. Alle mapper og filer bliver listede op med størrelse, filnavn, filtype (evt. med icon af filtypen), antal filer i mappen og om mapper/filer findes på computeren i nyere version. Hvis de findes i nyere version, skal man synronisere både USB-nøgle og computer, så den ligger begge steder. Jeg prøver lige at arbejde lidt videre med idéen, hvordan programmet skal se ud!
Her til sidst vil jeg bare lige sige, at ALLE ER MERE END VELKOMMEN TIL AT KOMME MED FORSLAG OG BIDRAG SOM KAN HJÆLPE MED AT GØRE PROGRAMMERNE SÅ GODE SOM MULIGT!
C er C++ uden class'er, templates, namespace, og nogle få andre ting. Ren C bliver mest brugt på små indlejrede systemer, hardware drivere og til at lave kerner (f.ex. Linux) i. C er delvist en forløber for C++, men der bliver stadig udviklet kode i C. Til dit formål bør du konsentrere dig om C++
GUI er ikke en del af standard biblioteket. Du kan slå op på MSDN, eller du kan downloade Win32Api dokumentationen som en .hlp fil her (scroll lidt ned og find "Windows API documentation"): http://www.cs.virginia.edu/~lcc-win32/
Jeg har kigget lidt på det, men jeg bliver nok nød til at få noget mere hjælp. Jeg forstår det meste af koden i eksemplerne, men det er noget andet selv at skrive koden (ved ikke rigtigt, hvor jeg skal starte).
Start med at lave logikken, så kikker vi på GUI senere.
Start med at lave en funktion der kan gemme navnet og størrelse på alle filer i en given mappe og undermapper i en std::vector.
Lav et program der beder brugeren om navnet på to mapper/drev og derpå laver listen fra før for hver af de to mapper.
Derpå sammenligner du størrelser for alle filer på de to placeringer. Så skriver programmet alle de filer der: 1: Kun ligger på A 2: Kun ligger på B 3: Ligger på både A og B
Derpå udvider du testen af størrelser så den åbner filerne og sammenligner byte/char for byte/char.
Det lyder måske som en stor mundfuld. Men med de links jeg har givet burde du kunne starte.
Jeg har fået min C++ som er til en del hjælp. Jeg har også kigget på de links som du har henvist til. Jeg har printet ListFiles, ADir, checkdup og filedate ud, så jeg kan kigge koden lidt igennem.
Jeg har taget udgangspunkt i checkdup, som langt hen af vejen, kan det som det nye program skulle kunne.
Valg af input: enten skal program kun finde dubletter et sted på computer eller skal der gennemsøges to steder fx to biblioteker eller en USB-nøgle og et bibliotek. Der ud over skal man kunne vælge parametre som man kan i checkdup.
[sti1] [parametre] [sti1] [sti2] [parametre]
Programmet skal lede efter dubletter og nyere version er filen. Ligesom checkdup finder dubletter ved at sammenligne indholdet af to filer skal det nye program også. Derudover kunne det være en god idé, at finde nyere version af en fil ved at bruge dele af ADir. Hvis to filer hedder det samme kigger programmet som indholdet er det samme eller som en af filerne er nyere end den anden. Dette gøres ved, at se hvilken en af filerne som sidst er rettet i.
Nu skal man enten slette dubletterne, så man har en tilbage eller skal man synkronisere begge stier ved, at overskrive den gamle version er filen.
Da checkdup er skrevet i C får jeg ikke lov til at køre det som C++ (der er fejl i linie 68 og 196). Filedate er også skrevet i C. Kan man ikke få det til at køre i C++?
Det er måske lidt af en stor mundfuld jeg har kastet mig ud i og da jeg ikke har programmeret i C++ før var det måske en idé, at jeg lavede en ”oversættelse” af checkdup og ADir? Det vil sikkert også hjælpe mig til at forstår, hvordan jeg skal lave det nye program.
Med hensyn til de to fejl. De skyldes at man i C++ ikke automatisk konverterer fra void pointere til vilkårlige andre pointere. De kan løses ved at de to linier til: FileSize = (int *)malloc(NumFile * sizeof(int)); FileName = (void **)realloc(FileName, sizeof(char *) * NumAlloc );
Men det kunne være en god idé at "oversætte" programmerne til C++, dvs: Bruge new og delete i stedet for malloc og free. Bruge std::vector i stedet for char **FileName; og int *FileSize; Bruge cout og cerr i stedet for printf Bruge ifstream og ofstrem i stedet for FILE * Bruge std::string i stedet for char xxx[...] Måske bruge en class eller to.
Nu kører checkdup i C++:-) Jeg har lavet en lille oversættelse. Jeg kan godt gå mere i dybden, hvis det er. Kan jeg ud fra checkdup få de nye program til at tage imod to input (et eller to drev/stier)?
Det jeg mener er at du skal lave en class: class DirInfoClass { public: char **FileName; int NumAlloc; int NumFile; int *FileSize; void AddFile(const char *aName, int aFileSize); };
Fjerne de globale variable FileName, NumAlloc, ... og så lave en/to varable af typen DirInfoClass:
Det forstår det du har skrevet, men det met pointere har jeg ikke helt styr på.
Jeg bliver nød til at få noget mere hjælp, da jeg ellers ikke kommer videre. Kan vi ikke i fællesskab (gerne linie for linie) lave den delt af programmet som indlæser filerne i array´et (som er en forudsætning for resten af programmet)?
Jeg har kigget i tutorialen og min C++ bog, men er bare blevet mere forvirret.
Jeg har udvidet DirInfoClass: class DirInfoClass { char **FileName; int NumAlloc; int NumFile; int *FileSize; int GetFileSize(const char *fn); public: const char *GetFileName(int n) { return FileName[n]; } int GetFileSize(int n) { return FileSize[n]; } int GetNumFile() { return NumFile; } void AddFile(const char *aName); };
På denne måde kan man ikke få fat i class'ens members direkte, men skal tilgå dem gennem funktionerne. Det har den store fordel at man kan laver om på strukturen unden at andre kan se det, f.ex. lave om så man gemmer i en std::vector og/eller i std::string.
Jeg vil godt kende hvornår en given fil sidst er modificeret, da man kan se hvilken fil som er nyest. Et eksempel kunne være, at man er ved at lave en opgave som man gemmer på sin computer med navnet "opgave.doc". Man kopiere opgaven over på sin USB-nøgle for at arbejde videre på den. Nu hedder begge filer "opgave.doc", men hvilken en af dem er nu den man arbejder med? Det er selvfølgelig den som sidst er rettet!
Jeg har godt se, at jeg stadigvæk er på begynderstadiet (er jo også lige startet med at programmere i C++).
Jeg har læst en del i "Grundlæggende programmering i C++", men mangler at få lavet øvelserne og hermed få erfaringen og forståelsen for, hvordan man programmere i C++.
Kan vi ikke gøre det sådan, at du en bid af gangen, så jeg kan følge med i hvordan koden skal skrives. Altså hvordan man i først omgang læser fra de to stier og gemmer det i et array. Gerne med en forklaring, hvorfor man gør det på den måde.
Jeg er nok bedre til at få idéer til programmer end at selv at programmere dem på nuværende tidspunkt. Håber vi kan lave programmet, da det er et som jeg vil have stor gavn af (der jeg ikke er specielt god til, at holde styr på mine filer). Måske kan du hjælpe mig med at bygge videre på de øvelser som står i bogen (når jeg er kommet så langt)?
Er Visual C++ 2005 Express Edition Beta 2 er god C++ editor? Kan jeg bruge den i stedet for CodeBlocks eller skal jeg vente lidt til jeg har fået noget mere erfaring?
OK. Jeg beholder Code::Blocks og laver øvelserne i bogen.
Jeg vil godt kigge på programmet her i efterårsferien, men skal nok have noget hjælp. Jeg skal vide helt præcis hvad jeg skal lave. Her tænker jeg på, hvor jeg skal rette og hvor jeg skal tilføje noget (gerne hvilke linier eller afsnit).
"Du kan prøve at lave det så det læser fra to sti'er og gemmer den anden sti i DirInfo[1]." Hvad er det helt præcis gøre? Jeg ved godt, at du har beskrevet det 27/09-2005 23:31:37, men skal lige have lidt hjælp i den rette retning. Jeg skal oprette et array, hvordan gør jeg det?
Jeg skal bruge NumAlloc og NumFile til at vide, hvor stort array´et skal være. Hvordan får jeg filerne gemt i array´et? Jeg skal vel køre en løkke?
Du skal putte dem ind i: int i; for(i = 0; i < 2; i++) { Kode her } Og bruge i og ikke idx som index til DirInfo, meningen er jo at udskrive informationen for begge lister, dvs DirInfo[0] og DirInfo[1]
Hvis du prøver at ændre den sidste del (fra "int idx;") af main til: int idx; for(i = 1, idx = 0; i < argc; i++) { if(argv[i][0]) { Scan(argv[i], "*", DoSub, idx); idx++; }
Jeg synes at du skal lave et par små ting om først, også som en øvelse.
Prøv at lave Scan om til at være en funktion i DirInfoClass, så det kommer til at se sådan ud: class DirInfoClass { ... public: void Scan(char *dir, const char *pattern, int sub); ... };
Og du vil så kalde den med: DirInfo[idx].Scan(...);
sub parametren burde egentlig være bool (true/false)
Du kan også prøve at lave disse: char **FileName; int *FileSize; Om til: std::vector<std::string >FileName; std::vector<int >FileSize; Så behøver du ikke: int NumAlloc; int NumFile; Du kunne også lave det om til: class FileEntryClass { public: std::string FileName; int FileSize; }; std::vector<FileEntryClass> FileEntry;
I main, der hvor du kalder Scan laver du det om til: DirInfo[idx].Scan(argv[i], "*", DoSub); Der hvod du kalder Scan fra Scan, fjerner du blot sidste argument i kaldet.
Alle steder i Scan hvor du nu har noget i stil med: DirInfo[idx].AddFile(sub); laver du det om til: AddFile(sub); Da AddFile er en memberfunktion.
Du skal inkludere vector og string for at kunne bruge std::vector og std::string: #include <vector> #include <string>
GetFileName skal rettes, det letteste er at lade den returnere en reference til en std::string, da FileName er en std::strig: std::string &GetFileName(int n) { return FileName[n]; }
Scan er nu en member af DirInfoClass, så du skal skrive DirInfoClass foran selve funktionen: void DirInfoClass::Scan(char *dir, const char *pattern, int sub)
Da du ikke har nogen NumFile variabel i class'en kan du finde ud af hvor mange filnavne der er i listen vha. FileName.size() (i funktionen GetNumFile()).
Derudover skal du rette AddFile så den tilføjer til en vector, det er ret simpelt, så prøv at læse lidt på vectorer (du skal bruge funktionen push_back).
Er der ellers rigtigt det jeg har lavet indtil videre? #include <dir.h> #include <stdlib.h> #include <stdio.h> #include <io.h> #include <string.h> #include <sys\stat.h> #include <errno.h> #include <iostream> #include <vector> #include <string>
/*Erklære variabler*/ class DirInfoClass { std::vector<std::string >FileName; std::vector<int >FileSize; int GetFileSize(const char *fn); public: std::string &GetFileName(int n) { return FileName[n]; } int GetFileSize(int n) { return FileSize[n]; } int GetNumFile() { return NumFile; } void AddFile(const char *aName); void Scan(char *dir, const char *pattern, int sub); };
std::vector har en push_back funktion der tilføjer et element i slutningen af vectoren.
I AddFile har du netop brug for at tilføje et element til FileName og FileSize, som begge er en vector. Det fil-navn du skal tilføje til FileName er argumentet til AddFile. Fil-størrelsen der skal tilføjes til FileSize får du med GetFileSize()
Alt i alt gør det at du kan lave AddFile som to gange push_back, en for hver vector.
Jeg tror, at jeg er ved at være med på den. Jeg tror, at jeg har forstået hvad det skal gøres, men hvordan jeg gør det helt præcis er jeg stadigvæk lidt usikker på.
Er det disse linier som skal rettes? FileName = (char **)realloc(FileName, sizeof(char *) * NumAlloc); FileSize = (int *)realloc(FileSize, sizeof(int) * NumAlloc);
Vi skal have afskafftet NumFile. push_back putter til enden på vectoren, så der skal ikke være nogen [] på push_back.
FileName indeholder std::string, så det der skal push's skal være en std::string. Compileren kan selv finde ud af at lave en "const char *" om til en std::string, så for FileName bliver det til: FileName.push_back(aName); Eller vi kan lave konverteringen for compileren: std::string T(aName); FileName.push_back(T); Eller (det samme skrevet på en anden måde): FileName.push_back(std::string(aName));
Denne del var rigtigt? void DirInfoClass::AddFile(const char *aName) { FileName.push_back(std::string(aName)); FileSize.push_back(GetFileSize(aName)); }
Så skal jeg fjerne FileName og FileSize vectorene class FileEntryClass { public: std::string FileName; int FileSize; }; std::vector<FileEntryClass> FileEntry;
altså:
class FileEntryClass {
}; std::vector<FileEntryClass> FileEntry;
og lave en push_back på FileEntry (lige som jeg gjorde med vectorerne i addFile)?
FileEntry skal være member i FileInfoClass, mener du ikke DirInfoClass?
Kan vi ikke tage hvert af punkterne fra 23/10-2005 23:25:40 og forklar mig præcis hvad jeg skal? Hvad skal jeg gøre for, at FileEntry er member af DirInfoClass og næste punkt osv.
Start med FileEntryClass: class FileEntryClass { public: std::string FileName; int FileSize; };
DirInfoClass ændres til: class DirInfoClass { std::vector<FileEntryClass> FileEntry; int GetFileSize(const char *fn); public: std::string &GetFileName(int n); int GetFileSize(int n); int GetNumFile(); void AddFile(const char *aName); void Scan(char *dir, const char *pattern, int sub); };
I AddFile skal vi oprette et FileEntryClass object, sætte dens member-variable, og push'e den på vectoren. Du skal selv kode det der svarer til de to kommentarer:
void DirInfoClass::AddFile(const char *aName) { FileEntryClass Entry; Entry.FileName = aName; // Entry.FileSize skal også sættes // Og Entry skal push'es på FileEntry med push_back }
Nu skal vi lave GetFileSize(int n), for at få det n'te element i FileEntry bruges[], det vi får er så en FileEntryClass object, vi får FileSize med . operatoren;
int FileInfoClass GetFileSize(int n) { return FileEntry[n].FileSize; }
GetFileName laves på samme måde.
GetNumFile skal ændres så den returnerer størrelsen på FileEntry vectoren (size())
Du kan få det til at udskrive æøå (og andre specielle tegn) ved at oversætte med OemToChar (eller er det CharToOem?). Men hvis det er meningen at det skal ende med at være et windows program (hvor problemet ikke opstår) er det måske ikke umagen værd.
Du kan starte med at tilføje en feature der også gemmer tidspunkt for hver fil til FileEntryClass, og lave en GetFileTime funktion til DirInfoClass. Du får fat i tidspunkt med stat: http://www.hmug.org/man/2/stat.php
Næste opgave kunne være at få programmet til at undersøge forskellen på filer i de to lister.
Så tilføjer du en: time_t FileTime; Til FileEntryClass.
I AddFile kalder du GetFileTime, og sætter FileTime i FileEntry.
På siden jeg linker til kan du se hvordan man kan bruge ctime(...) til at udskrive fil-tiden med. Det kan måske være en ide at lave en: std::string GetFileTime(int n); i DirInfoClass, der bruger ctime til at lave en std::string, som så kan udskrives.
Når du kommer til at lave sammenligningen bør du starte med at lave en pseudo-kode beskrivlse af hvordan du vil gøre det.
Du skal lave en: time_t DirInfoClass::GetFileTime(const char *aName) { ... } I denne skal du kalde stat(...) I koden fra "16/09-2005 11:06:04" kan du se hvordan det gøres. I kaldet til stat(...) bruges "struct stat s;" s har st_mtime det er den der skal returneres fra GetFileTime.
I din AddFile(...) har du "FileEntry(FileTime);", det giver ikke rigtigt mening, du skal sætte FileEntry.FileTime på samme måde som du sætter FileEntry.FileSize. Og det skal gøres inden du kalder push_back, da den laver et kopi.
Når du har det på plads skal du lave std::string DirInfo::GetFileTime(int n) Du skal bruge ctime(...) til at lave en "char Data[50]", og så lave en std::string fra dette, som returneres.
Til slut skal du bruge GetFileTime(j) i loopen i enden af main til at udskrive tiden på filen sammen med navnet og størrelsen.
Når du har det på plads skal du lave std::string DirInfo::GetFileTime(int n) Du skal bruge ctime(...) til at lave en "char Data[50]", og så lave en std::string fra dette, som returneres.
Der er lige et par ting som jeg vil foreslå at vi laver om først.
Først flytte GetFileSize(const char *xx) og GetFileTime(const char *xx) til FileEntryClass, og lave en constructor til FileEntryClass som tager en "const char *aName) som argument.
Derved kommer vores class'er til at se sådan ud: class FileEntryClass { int GetFileSize(const char *fn); time_t GetFileTime(const char *aName); public: FileEntryClass(const char *aName); std::string FileName; int FileSize; time_t FileTime; };
class DirInfoClass { std::vector<FileEntryClass> FileEntry; public: std::string GetFileTime(int n); std::string &GetFileName(int n); int GetFileSize(int n); int GetNumFile() { return FileEntry.size(); } void AddFile(const char *aName); void Scan(char *dir, const char *pattern, int sub); };
AddFiles kommer så til at se såden ud: void DirInfoClass::AddFile(const char *aName) { FileEntryClass Entry(aName); FileEntry.push_back(Entry); }
Du skal så selv lave: FileEntryClass::FileEntryClass(const char *aName)
Den skal sætte FileName, FileSize ved at kalde GetFileSize og FileTime ved at kalde GetFileTime.
Du må også gerne fjerne argumenterne til GetFileSize(const char *) og GetFileTime(const char *), da navnet er en member i class'en.
Dernæst vil jeg gerne have flyttet disse: int GetFileSize(int n); int GetNumFile() { return FileEntry.size(); } Til FileEntryClass også.
Din version af GetFileTime virker ikke. Du skal kalde stat(...) og returnere s.st_mtime
Jeg har lige hurtig kigget på det, men er gået lidt i stå. Hvis du lige kan sige, hvad der er rigtigt og hvad som skal rettes, så kigger jeg på det i aften.
class DirInfoClass { std::vector<FileEntryClass> FileEntry; public: std::string GetFileTime(int n); std::string &GetFileName(int n); int GetFileSize(int n); int GetNumFile() { return FileEntry.size(); } void AddFile(const char *aName); void Scan(char *dir, const char *pattern, int sub); };
Du skal så ændre FileEntryClass::GetFileSize() så den bruger FileName og ikke fn. fopen, access og printf skal bruge en "const char *", du laver en fra en std::string med: FileName.c_str()
Du skal så ændre FileEntryClass::GetFileSize() så den bruger FileName og ikke fn. fopen, access og printf skal bruge en "const char *", du laver en fra en std::string med: FileName.c_str()
Jeg kan ikke finde FileEntryClass::GetFileSize()...
Jeg ved godt, at det langt fra er rigtigt, men jeg kører bare rundt i det nu. Jeg mangler lige at få styr på det grundlæggende, så jeg læser lige i min C++ bog her i weekend.
Skal vi fortsætte fra 04/11-2005 16:02:01? Hvad skal jeg rette?
Jeg tror at "problemet" er, at jeg mister overblikket og derfor ikke ved rigtigt, hvad jeg skal gøre. Alt nyt er svært, men jeg er indstillet på at lære at programmere i C++.
// Skal være FileEntryClass::GetFileTime() // Skal kalde stat(...) time_t DirInfoClass::GetFileTime(const char *aName) { struct stat s; return s.st_mtime; }
//Skal bruge FileName.c_str() i stedet for fn int FileEntryClass::GetFileSize() { int len; FILE *f = fopen(fn, "rb"); if(!f) { fprintf(stderr, "Unable to open: %s\n", fn); if(!access(fn, 0)) fprintf(stderr, "The file seems to be there\n"); exit(EXIT_FAILURE); } fseek(f, 0, SEEK_END); len = ftell(f); fclose(f);
// Skal være FileEntryClass::GetFileTime() // Skal kalde stat(...) time_t FileEntryClass::GetFileTime() { struct stat s; return s.st_mtime; }
/*Henter filstørrelse*/ //Skal bruge FileName.c_str() i stedet for fn int FileEntryClass::GetFileSize() { int len; FILE *f = fopen(FileName.c_str(), "rb"); if(!f) { fcout <<(stderr, "Unable to open: %s\n", FileName.c_str()); if(!access(FileName.c_str(), 0)) fcout <<(stderr, "The file seems to be there\n"); exit(EXIT_FAILURE); } fseek(f, 0, SEEK_END); len = ftell(f); fclose(f);
return len; }
DirInfoClass DirInfo[2];
static void Scan(char *dir, const char *pattern, int sub); static void Usage(const char *ProgramName); static int DelReadOnly = 0;
/*main-metode*/ int main(int argc, char *argv[]) { char Dir[1024]; int i, DoSub = 0;
getcwd(Dir, sizeof(Dir));
for(i = 1; i < argc; i++) { if(argv[i][0] == '-') { switch(argv[i][1]) { case 's': DoSub = 1; argv[i][0] = 0; break; case 'r': DelReadOnly = 1; argv[i][0] = 0; break; default: fprintf(stderr, "Unknown switch: %s\n", argv[i]); /* No break; */ case 'h': Usage(argv[0]); break; } } } int idx; for(i = 1, idx = 0; i < argc; i++) { if(argv[i][0]) { DirInfo[idx].Scan(argv[i], "*", DoSub); idx++; }
Denne linie: FileEntryClass::FileEntryClass::GetFileSize() Skal være: FileEntryClass::FileEntryClass(const char *aName)
For at skrive til stderr bruger man std::cerr: int FileEntryClass::GetFileSize() { int len; FILE *f = fopen(FileName.c_str(), "rb"); if(!f) { std::cerr << "Unable to open: " << FileName << std::endl; if(!access(FileName.c_str(), 0)) std::cerr << "The file seems to be there" << std::endl; exit(EXIT_FAILURE); } fseek(f, 0, SEEK_END); len = ftell(f); fclose(f);
return len; }
Når man bruger ctime() til at lave en time_t om til en streng putter den et linieskift på i enden af strengen. Det er ikke så smart, prøv om du kan fjerne den, læs om std::string her: http://www.sgi.com/tech/stl/basic_string.html
Det ekstra linieskift som skal fjernes og kaldet til stat(...) i: time_t FileEntryClass::GetFileTime()driller mig.
Kan vi ikke tage trin for trin, hvad der sker i de to klasser? Det vil være en rigtigt god måde, hvis jeg forstod hvad de forskellige ting gør (så vil jeg måske også kunne se, hvad jeg mangler før det virker). Det vil være en godt hjælp, hvis jeg fik at vide, at der mangler det og det og hvor det mangler.
Jeg har ændret det så det hele virker. Prøv at sammenligne dette med din seneste udgave af koden.
Jeg tror at du får mest ud af at beskrive koden selv. Prøv at skrive nogle liniers kommentar for hver funktion og blok i funktion. Hvis der er dele du ikke forstår så spørg.
Der er to funktioner der hedder GetFileSize og der er også to funktioner der hedder GetFileTime, det forvirrer måske. Prøv at give et andet navn til dem i FileEntryClass.
// Skal være FileEntryClass::GetFileTime() // Skal kalde stat(...) time_t FileEntryClass::GetFileTime() { struct stat s; stat(FileName.c_str(), &s); return s.st_mtime; }
/*Henter filstørrelse*/ //Skal bruge FileName.c_str() i stedet for fn int FileEntryClass::GetFileSize() { int len; FILE *f = fopen(FileName.c_str(), "rb"); if(!f) { std::cerr << "Unable to open: " << FileName << std::endl; if(!access(FileName.c_str(), 0)) std::cerr << "The file seems to be there" << std::endl; exit(EXIT_FAILURE); } fseek(f, 0, SEEK_END); len = ftell(f); fclose(f);
return len; }
DirInfoClass DirInfo[2];
static void Scan(char *dir, const char *pattern, int sub); static void Usage(const char *ProgramName); static int DelReadOnly = 0;
/*main-metode*/ int main(int argc, char *argv[]) { char Dir[1024]; int i, DoSub = 0;
getcwd(Dir, sizeof(Dir));
for(i = 1; i < argc; i++) { if(argv[i][0] == '-') { switch(argv[i][1]) { case 's': DoSub = 1; argv[i][0] = 0; break; case 'r': DelReadOnly = 1; argv[i][0] = 0; break; default: fprintf(stderr, "Unknown switch: %s\n", argv[i]); /* No break; */ case 'h': Usage(argv[0]); break; } } } int idx; for(i = 1, idx = 0; i < argc; i++) { if(argv[i][0]) { DirInfo[idx].Scan(argv[i], "*", DoSub); idx++; }
"Skal jeg ændre printf og fprintf til std::cout?" >Ja
"Jeg skriver #include <main.h> i starten af min main.cpp og flytter FileEntryClass og DirInfoClass til en .h fil som jeg passende kunne kalde main.h" > Foretrækker #include "main.h" til filer der er ens egne og som ligger i samme mappe. main.h er måske ikke det bedste navn.
Du kan derpå lave en funktion der sammenligner to FileEntryClass objecter/filer. Start med at checke om de har samme størrelse, så kan vi lave resten senere.
Og så laver du en loop der sammenligner alle filer i to lister (DirInfoClass) og udskriver resultatet af hver sammenligning.
Nu er jeg ændret printf og fprintf til std::cout<<.
Jeg har flyttet FileEntryClass og DirInfoClass til header.h filen. Jeg kan godt compile, men jeg får to warnings:
Project : Console application Compiler : GNU GCC Compiler (called directly) Directory : D:\CPP\checkdup2\ -------------------------------------------------------------------------------- Switching to target: default Compiling: main.cpp In file included from main.cpp:12: header.h:22:3: warning: no newline at end of file main.cpp:192:2: warning: no newline at end of file Linking console executable: D:\CPP\checkdup2\console.exe Process terminated with status 0 (0 minutes, 1 seconds) 0 errors, 2 warnings
Jeg prøver, at kigge på resten her en af dagene. God weekend:-)
Jeg mangler det sidste fra 16/11-2005 01:26:58. Kan vi ikke tage det sidste linie for linie? Hvordan laver jeg en funktion som sammeligner to FileEntryClass objecter/filer?
Man kunne starte med at tilføje en funktion til FileEntryClass: bool Compare(const FileEntryClass& aOther);
Den kunne se sådan ud: bool FileEntryClass::Compare(const FileEntryClass& aOther) { if(FileSize != aOther.FileSize) return false; if(FileTime != aOther.FileTime) return false; return true; } Hvis den returnerer true er filerne ens. Men det checker kun om filerne har samme størrelse (hvilket er ok) og om de er fra samme tid (hvilket ikke er nogen god idé). I stedet bør man åbne filerne og læse dem byte for byte for at se om de er ens, CheckDup() fra den oprindelige kode gør netop det.
Du har nogle af disse: chat temp[1023]; std::cout<<(temp, "%s\\%s", dir, pattern);
I dette tilfælde var det en sprintf, den skal du lave om til: std::string temp: temp = dir: temp += "\\"; temp += pattern; og så bruge temp.c_str() efterfølgende
Der hvor du udskriver til cout of cerr står der f.ex: std::cout<<(stderr, "Usage: %s [-s] [-a] [-q] [pattern1 [pattern2 [...]]]\n", ProgramName); Det skal være: std::cerr << "Usage: " << ProgramName << "[-s] [-a] [-q] [pattern1 [pattern2 [...]]]" << std::endl;
Du skriver, at jeg har nogle af disse, men er der ikke kun en? char sub[1024]; std::cout<<(sub, "%s\\%s", dir, fileinfo.name);
Jeg har prøvet at ændre denne ene, men får det ikke rigtigt til at køre. Hvis jeg sletter de to linier og indsætter disse: std::string temp: temp = dir: temp += "\\"; temp += pattern;
Hvad skal jeg så gøre med temp.c_str()? Er der ander jeg skal?
Skal jeg tilbage til 29/11-2005 19:18:44 og så rette temp[1024] og sub[1024] med det du beskriver 30/11-2005 19:44:51? Skal jeg rette parametrene tilbage?
Hvad skal jeg f.eks. rette disse linier til? char sub[1024]; std::cout<<(sub, "%s\\%s", dir, fileinfo.name); Scan(sub, pattern, 1);
Jeg har ikke lavet andet kode end det som jeg har lavet 12/12-2005 23:02:49 (udover at fjerne OLD linierne).
Der er tilføjet det som også er beskrevet 22/11-2005 23:14:57. Jeg kan godt se, at jeg skal have fat i noget kode fra det oprindelige checkdup program, men ved ikke helt, hvor jeg skal starte.
Vi har allerede en compare function i FileEntryClass, den er ikke færdig, men kan bruges om en begyndelse.
Vi starter med at lave den const (den modificerer ikke objektet). I header.h tilføjer vi const efter funktionen: bool Compare(const FileEntryClass& aOther) const; også i .cpp filen: bool FileEntryClass::Compare(const FileEntryClass& aOther) const
Derpå laver vi en [] operator i DirInfoClass: const FileEntryClass& operator [] (size_t aIdx) const {return FileEntry[aIdx]; }
Så kan vi lave en loop i enden af main(): if(idx == 2) { int i, j; for(i = 0; i < DirInfo[0].GetNumFile(); i++) { for(j = 0; j < DirInfo[1].GetNumFile(); j++) { if(DirInfo[0][i].Compare(DirInfo[1][j])) { std::cout << "These files seems to be the same: " << DirInfo[0].GetFileName(i) << ", " << DirInfo[1].GetFileName(j) << std::endl; } } } }
Den vil så udskrive navnene på de filer der har samme størrelse og er fra samme tidspunkt.
Når du har det til at spille kan du forbedre compare funktionen: 1: At to filer har forskellige tidsstempler gør dem ikke forskellige. 2: Hvis to filer har samme størrelse bør man åbne dem, og sammenligne dem byte for byte. Du kan se hvordan i den oprindelige kode.
Ja, jeg har prøvet at køre programmet. Jeg har to mapper som er næsten 100% undtagen to filer som jeg har tilføjet backup2. Det er var jeg får, når jeg kører programmet:
Scan funktionen skal se sådan ud: void DirInfoClass::Scan(char *dir, const char *pattern, int sub) { struct _finddata_t fileinfo; long handle; char temp[1024];
/* First parse any files in this directory */ sprintf(temp, "%s\\%s", dir, pattern); handle = _findfirst(temp, &fileinfo); if(handle != -1) { do { if(!(fileinfo.attrib & _A_SUBDIR)) { char sub[256]; sprintf(sub, "%s\\%s", dir, fileinfo.name); AddFile(sub); } } while(_findnext(handle, &fileinfo) == 0); _findclose(handle); } if(sub) { /* Then parse all sub directories */ std::cout<<temp << dir <<std::endl; handle = _findfirst(temp, &fileinfo); if(handle != -1) { do { if((fileinfo.attrib & _A_SUBDIR) && strcmp(fileinfo.name, ".") && strcmp(fileinfo.name, "..")) { char sub[1024]; std::cout<<sub <<dir <<fileinfo.name << std::endl; Scan(sub, pattern, 1); } } while(_findnext(handle, &fileinfo) == 0); _findclose(handle); } } }
Det er de to cout << der er lavet om til sprintf. Så bør du kunne ændre det så den udskriver filer der ikke er ens.
Det ser stadigvæk ikke helt rigitgt ud. Burde programmet ikke undersøge alle undermapper? Jeg har kopieret mappen backup over til backup2 (samt tilføjet en ekstra fil)
C:\>console c:\backup c:\backup2 -s c:\backup\*c:\backup ¼c:\backupDocuments and Settings ¼\*¼ c:\backup2\*c:\backup2 ¼c:\backup2Documents and Settings ¼\*¼ Found: 1file c:\backup\Backup.txt 106287 Tue Dec 27 11:44:13 2005 Found: 2files c:\backup2\Backup.txt 106287 Tue Dec 27 11:44:13 2005 c:\backup2\Ny Bitmapbillede.bmp 0 Wed Dec 28 12:40:50 2005 These files seems to be the same: c:\backup\Backup.txt, c:\backup2\Backup.txt
Jeg har rettet det sidste. Nu ser det ud som om det virker:-)
Hvad sortere vi filerne efter? Jeg vil gerne finde filer som er 100% identiske og filer som optræder i flere versioner fx et Word-dokument.
1) 100 % identiske filer skal bare tjekkes ligesom i checkdup byte for byte.
2) Hvad gør vi her? Er det noget med filnavn, hvornår filen er oprettet og hvornår filen sidst er ændret? Man kan ikke bruge byte for byte metoden her? Altså udregne hvor identiske dokumenterne er fx. 85% identisk
At checke for 100% er ikke noget problem, det synes jeg du skal lave først.
Med stat() kan man få tiden for hvornår en fil er oprettet, jeg ved ikke om den kan bruges. Det er ret svært at finde ud af om to filer en næsten ens, hvis man putter nogle få ekstra bytes i den ene vil det umiddelbart se ud somom de er meget forskellige, det samme hvis man flytter rundt på to afsnit. Du kan prøve at sammenligne to næsten ens word dokumenter byte for byte og se hvor meget det er forskellige.
Du kan putte funktionen CheckDup ind i FileEntryClass og kalde den fra Compare funktionen. Du skal bruge c_str() til at lave om fra en std::string til en const char *
Jeg tror, at jeg skal have lidt mere hjælp. Kan du ikke beskrive hvad jeg skal, ligesom du gjorde 02/01-2006 19:58:43? Jeg forstår det bedre på den måde:-)
Jeg lavede en kommentar for et par dage siden, men den er fursvundet :-(
Jeg foreslår at vi laver en == operator der sammenligner to FileEntryClass objekter.
Så du skal: 1: Fjerne Compare funktionen fra header.h og .cpp filen.
2: Indsætte en prototype for operatoren i header.h, den skal se sådan ud: bool operator == (const FileEntryClass& lhs, const FileEntryClass& rhs); Put den ind i til slut i header.h
3: Så sætter du CheckDup funktionen ind, og ændrer linien: int CheckDup(const char *fn1, const char *fn2) til: bool operator == (const FileEntryClass& lhs, const FileEntryClass& rhs) I operatoren skal du så bruge lhs.FileName.c_str() i stedet for fn1 og rhs.FileName.c_str() i stedet for fn2
4: Så sætter du disse to linier ind, de checker om de to filer har samme størrelse: if(lhs.FileSize != rhs.FileSize) return false;
5: For den første fil (f1) skal du ændre koden til: FILE *f1, *f2;
f1 = fopen(lhs.FileName.c_str(), "rb"); if(!f1) { fprintf(stderr, "Unable to open: %s\n", lhs.FileName.c_str()); if(!access(lhs.FileName.c_str(), 0)) fprintf(stderr, "The file seems to be there\n"); exit(EXIT_FAILURE); } Det samme for f2.
6: Nu kan du bruge operatoren til at sammenligne to filer, i main hvor du før kaldte Compare skal du ændre koden til: if(DirInfo[0][i] == DirInfo[1][j]) { std::cout << "These files seems to be the same: " << DirInfo[0].GetFileName(i) << ", " << DirInfo[1].GetFileName(j) << std::endl; }
7: Når du har det til at spille kan du lave nogle små forbedringer: a: operatoren skal returnere false eller true, ikke 0 eller 1 b: Det er nok ikke nogen god ide at afslutte programmet (exit()) hvis der er en fil den ikke kan åbne. c: Bruge cout << og cerr << i stedet for printf og fprintf d: Bruge std::ifstream istedet for FILE, husk at åbne som binær fil.
f1 = fopen(lhs.FileName.c_str(), "rb"); if(!f1) { fprintf(stderr, "Unable to open: %s\n", lhs.FileName.c_str()); if(!access(lhs.FileName.c_str(), false)) fprintf(stderr, "The file seems to be there\n"); exit(EXIT_FAILURE); }
Der er et problem med den sprintf der er lige efter kommentaren /* Then parse all sub directories */ Der er to gange %s, men kun et argument. Den skal laves om til: sprintf(temp, "%s\\*.*", dir);
Jeg skal nok have lidt mere hjælp til 7b og 7d. Jeg har lavet 7c. Jeg finder lige ud af, hvad der helt præcis skal ske, når vi har fundet to filer som er ens.
At udskrive en warning betyder blot at udskrive en advarsel til brugeren, i dette tilfælde at filen whatever.ext ikke kunne åbnes.
Med hensyn til 7d. Det er ikke nødvendigt at lave det nu, men det kunne være en god øvelse. Jeg vil foreslå at du som en øvelse laver et lille program der bruger std::ifstream til at sammenligne to filer, i programmet læser du blot filerne én byte af gangen med read.
Ja, der bliver udskrevet en advarsel, det er ok. I stedet for exit(...) skal du i == operatoren returnere false, for at indikere at filerne ikke er ens. I "int FileEntryClass::GetFileSize()" er problemet lidt større. Den pæne måde kunne være at indikere en fejl, så filen bliver ignoreret, ellers er man nok nødt til at sætte en "tilfældig" filstørrelse, problemet med den løsning er ikke så stor - det bør ikke ske.
Jeg har lidt svært ved, at få taget hul på iostream delen. Jeg læst om iostreams på: http://www.cppreference.com/cppio/ og læst i min C++ bog, men jeg forstår det ikke helt. Jeg forstår godt, at vi skal åbne, læse og lukke filen, men resten driller!
Jeg har nok brug for lidt hjælp her. Hvad skal jeg helt præcis gøre for at åbne filen?
Der var en fejl i min kode, den har du gentaget i en lidt anden form: char x1, x2; while(File.read(&x1, 1) && File.read(&x2, 1)) { if(x1 != x2) return false; }
Du læser alle tegn fra samme fil.
Det skal være: while(File.read(&x1, 1) && File2.read(&x2, 1))
Vil du selv forsøge at lave en sådan "status side"?
Du kan begynde med at skrive til std::cout, så kan vi altid lave det om til at blive skrevet i en fil, på en windows app, eller hvor du nu vil have det hen.
Vi checker kun om filer er 100% ens, man kunne godt skrive navnet på filer der har samme navn. Men det er ret svært at finde dokumenter der er "næsten" ens.
Der er iøvrigt en fejl i denne linie: std::cerr << "Unable to open: %s\n" << rhs.FileName.c_str() << std::endl;
Nu tjekker vi om filerne er 100% ens, ligesom checkdup gør det. Hvis vi kan lave nogle flere søgekriterier vil det være super:-)
Det med at søge på navnet på filen er oplagt.
Hvis man opretter et word-dokument og gemmer dette på harddisken og derefter kopiere det til fx. ens usb-nøgle, så har begge dokumenter samme "Ændret" dato (se egenskaber for dokumentet). Dokumentet hedder også det samme, hvis dokumentet gemmes et andet sted end originalen ellers kommer dokumentet til at hedde "kopi af....".
Jeg ser om der er behov for andre søgekriterier og vender tilbage, hvis der er.
Jeg kan ikke lige se fejlen i denne linje: std::cerr << "Unable to open: %s\n" << rhs.FileName.c_str() << std::endl;
Hvis du starter med at tilføje en variabel til FileEntryClass der indeholder filnavnet uden stinavn, du kunne kalde den BaseName kunne du udskrive navnet på filer der er ens med: if(idx == 2) { int i, j; for(i = 0; i < DirInfo[0].GetNumFile(); i++) { for(j = 0; j < DirInfo[1].GetNumFile(); j++) { if(DirInfo[0][i].BaseName == DirInfo[1][j].BaseName) { std::cout << "These files seems to be the same: " << DirInfo[0].GetFileName(i) << ", " << DirInfo[1].GetFileName(j) << std::endl; }
} } }
Du kan bruge funktionen splitpath fra FileEntryClass's constructor (FileEntryClass::FileEntryClass) til at finde BaseName eller overføre det som parameter, da det er kendt i Scan(...) (fileinfo.name).
Jeg tror det kører nu, men sortere programmet ikke nu både på indhold og filnavn samtidigt?
Kan vi ikke starte med at lave en simpel oversigt? Oversigten kunne evt. vise; hvor mange filer der er på de to steder der bliver læst fra, hvilke filer som har samme indhold, og hvilke filer som har samme filnavne.
Jeg er stadigvæk lidt i tvivl om, hvordan vi fanger f.eks. Word-dokumenter som findes i flere version (se evt. 02/03-2006 21:37:08. Det er typisk, når man laver gruppearbejde, at man lige pludselig har flere versioner af et Word-dokument.
Indeholder et Word-dokument et "fingeraftryk" som man kan bruge til at samle de forskellige dokumenter, og på en eller anden måde finde det nyeste?
Jeg synes at den sidste del ser ok ud, virker det ikke? Du skal måske ændre teksten til: std::cout << "These files has the same name: " << DirInfo[0].GetFileName(i) << ", " << DirInfo[1].GetFileName(j) << std::endl;
Det letteste er at skrive den information ud i scan funktionen. Hvis ikke det kommer til at stå det rigtige sted, kan du lade scan gemme det i et array af en struct med dir-navn og antal, og så skrive det ud i main funktionen.
Problemet er at DirInfo kan indholde informationer fra mange undermapper, jeg ud fra at du vil have vist informationen for alle mappe? (hvis ikke kan du bruge argv[] og DirInfo.FileEntry.size())
Det er måske en idé, at inddele filerne i undermapper. I første omgang vil jeg bare havde vist, hvilke stier der bliver indlæst fra (de stier som bliver brugt som parametre til programmet).
Start med at tilføje en std::string til DirInfo class: std::string RootDir;
Der hvor du kalder Scan fra main tilføjer du: DirInfo[idx].RootDir = argv[i];
Og så kan du udskrive: std::cout << "Found " << DirInfo[i].GetNumFile() << " files in " << DirInfo[i].RootDir << std::endl;
Alt i alt bliver det til disse linier i main: int idx; for(i = 1, idx = 0; i < argc; i++) { if(argv[i][0]) { DirInfo[idx].RootDir = argv[i]; DirInfo[idx].Scan(argv[i], "*", DoSub); idx++; }
Nu kører det. Nu har vi en metode som finder filer med samme indhold og en metode som finder filer med samme navn. Kan man ikke samle det i en metode? Man kan jo godt have filer som både har samme indhold og hedder det samme.
Hvis man finder dette match kunne man skrive "These files seems to be the same" og "These files has the same name".
Hvis du laver en class i stil med følgende i header.h: class EqualFilesClass { public: std::string FirstFile; std::string SecondFile; bool SameName; bool SameContent; }; Kan du lave en liste af disse (i .cpp filen): std::vector<EqualFilesClass> EqualFilesList;
Hvis du så laver loopen så det starter med at finde filer filer med samme navn, kan du hver gang du finder sådanne, lave et nyt EqualFilesClass objekt og putte ditte i listen (EqualFilesList.push_back). Inden du putter det i listen sætter du navnene på de to filer (fuld sti) og sætter SameName flaget Derpå checker du om de to filer har samme indhold (skal gøres uanset om de har samme navn), hvis de har samme indhold og de havde samme navn skal du sætte SameContent flaget i det object du lige tilføjede til listen, ellers skal du lave et nyt object, sætte SameContent flaget og putte objektet i listen.
Når du så er færdig med at sammenligne, kan du udskrive al information fra listen.
Det er måske en idé at lave en constructor til EqualFilesClass.
Det er en lidt stor mundfuld, men prøv at starte, så tager vi problemerne hen ad vejen.
Du kan godt lave en AddFile funktion der tilføjer til EqualFilesList, du skal i så fald kalde den fra loopen i main hvor du sammenligner. Dvs Scan skal køre som nu, og tilføje til DirInfo listerne.
Med hensyn til tre eller flere ens filer; Nej det tager ikke højde for det, men det synes jeg at vi skal lave senere, for ikke at gøre opgaven for stor.
Dette kunne være delen der tilføjer til EqualFilesList:
for(i = 0; i < DirInfo[0].GetNumFile(); i++) { for(j = 0; j < DirInfo[1].GetNumFile(); j++) { bool SameName = false; if(DirInfo[0][i].FileName == DirInfo[1][j].FileName) { EqualFilesClass EqualFiles; EqualFiles.FirstFile = DirInfo[0].GetFileName(i); EqualFiles.SecondFile = DirInfo[1].GetFileName(j); EqualFiles.SameName = true; EqualFiles.SameContent = false; EqualFilesList.push_back(EqualFiles); SameName = true; } if(DirInfo[0][i] == DirInfo[1][j]) { if(SameName) { // The files has the same name, just set the SameContent flag: EqualFilesList.back().SameContent = true; } else { // The content of the files are the same but the names are not, // we have to add an entry to EqualFilesList ... } } } }
Denne loop sammenligner hele fil navn, dvs. inklusive path, den skal kun sammenligne selve filnavnet, dvs BaseName. Du skal selv udfylde ..., husk at sætte de rigtige flag.
Når du har det på plads kan du udskrive fra EqualFilesList
Hvordan udskriver jeg fra EqualFilesList? Jeg vil godt sidde og lege lidt med EqualFilesList metoden. Det vil hjælpe mig en del, hvis jeg kan se, hvad som ligger i listen.
Du kan f.ex: if(EqualFilesList[Idx].SameContent) std::cout << "Same Content"; else std::cout << "Not Same Content"; if(EqualFilesList[Idx].SameName) std::cout << "Same Name"; else std::cout << "Not Same Name";
Du kan gemme en pointer til det FileEntryClass object som filen peger på, i stedet for FirstFile og SecondFile. Så har du adgang til alle de informationer der er gemt for filen. Du laver så EqualFilesClass om til: class EqualFilesClass { public: FileEntryClass* FirstFile; FileEntryClass* SecondFile; bool SameName; bool SameContent; }; Der hvor du opretter EqualFilesClass Objekterne og putter dem i listen laver du det om til: EqualFiles.FirstFile = &DirInfo[0][i]; EqualFiles.SecondFile = &DirInfo[1][j];
Det kræver at du tilføjer en [] operator til DirInfoClass: FileEntryClass& operator [] (size_t Idx) { return FileEntry[Idx]; }
Når du så skal udskrive filnavn: std::cout << EqualFilesList[Idx].FirstFile->FileName;
Når jeg kører programmet, så bliver tingene udskrevet forkert. Programmet udskriver kun en sti (uden drevangivelse) og nogle pointer adresser. Hvad gør jeg forkert?
int j; for(i = 0; i < DirInfo[0].GetNumFile(); i++) { for(j = 0; j < DirInfo[1].GetNumFile(); j++) { bool SameName = false; if(DirInfo[0][i].BaseName == DirInfo[1][j].BaseName) { EqualFilesClass EqualFiles; EqualFiles.FirstFile = &DirInfo[0][i]; EqualFiles.SecondFile = &DirInfo[1][j]; EqualFiles.SameName = true; EqualFiles.SameContent = false; EqualFilesList.push_back(EqualFiles); SameName = true; } if(DirInfo[0][i] == DirInfo[1][j]) { if(SameName) { // The files has the same name, just set the SameContent flag: EqualFilesList.back().SameContent = true; } else { // The content of the files are the same but the names are not, // we have to add an entry to EqualFilesList EqualFilesList.back().SameName = false; } } } }
Du gemmer tid for filen som en time_t (som er en form for heltal). For at få det udskrevet som et normalt datoformat kan du bruge ctime, se f.ex: http://www.cplusplus.com/ref/ctime/ctime.html
Eksemplet er i C, i C++ ville man bruge: std::cout << "Current date and time are: " << ctime(&EqualFilesList[Idx].FirstFile->FileTime);
Ja, du kan lave en funktion i stil med FormTime, f.ex: std::string FormSize(int aFileSize)
Med hensyn til flere ens filer; det er en anelse kompliceret. Du kan f.ex. have tre filer der hedder det samme, men kun to af dem er ens. Der kan så være en fjerde fil der indeholder det samme som de to ens filer, men som hedder noget andet, og hvad gør man så?
Du kan starte med at lave EqualFilesClass om til: class EqualFilesClass { public: std::vector<FileEntryClass*> FileList; bool SameName; bool SameContent; };
Så kan den indeholde mere end to ens filer. Start med at ændre programmet så det bruge denne EqualFilesClass, så fortsætter vi derfra. Du bruger push_back til at tilføje til FileList. De to (første) filer kan du så tilgå med FileList[0] og FileList[1]
Jeg skal nok have lidt hjælp med at lave FormSize. Hvad er det præcis jeg skal lave?
Jeg retter lige det andet til imens. Jeg kan godt se, at det nok bliver lidt kompliceret, men vi skal nok finde en løsning (tænker lige over forskellige løsninger).
Jeg kan godt kompile programmet, men programmet lukker ned, når jeg kører det.
int j; for(i = 0; i < DirInfo[0].GetNumFile(); i++) { for(j = 0; j < DirInfo[1].GetNumFile(); j++) { bool SameName = false; if(DirInfo[0][i].BaseName == DirInfo[1][j].BaseName) { EqualFilesClass EqualFiles; EqualFiles.FileList[0] = &DirInfo[0][i]; EqualFiles.FileList[1] = &DirInfo[1][j]; EqualFiles.SameName = true; EqualFiles.SameContent = false; EqualFilesList.push_back(EqualFiles); SameName = true; } if(DirInfo[0][i] == DirInfo[1][j]) { if(SameName) { // The files has the same name, just set the SameContent flag: EqualFilesList.back().SameContent = true; } else { // The content of the files are the same but the names are not, // we have to add an entry to EqualFilesList EqualFilesList.back().SameName = false; } } } }
Du skal lave plads til elementerne i FileList, så disse: EqualFiles.FileList[0] = &DirInfo[0][i]; EqualFiles.FileList[1] = &DirInfo[1][j]; Skal laves om til: EqualFiles.FileList.push_back(&DirInfo[0][i]); EqualFiles.FileList.push_back(&DirInfo[1][j]);
På dette sted i koden skal du også tilføje et EqualFilesClass object: else { // The content of the files are the same but the names are not, // we have to add an entry to EqualFilesList EqualFilesList.back().SameName = false;
Dvs noget i stil med: EqualFilesClass EqualFiles; ... EqualFilesList.push_back(EqualFiles);
Jeg synes, at vi skal udvide FormSize lidt. Vi kan udskrive filesize i KB, MB eller GB (hvor mange cifre skal vi have med efter kommaet?) Eller en anden måde vi kunne gøre det på var, at udskrive det hele i KB og så indsætte et punktum efter hvert tusindende.
Hvad er den bedste måde at lave det på? Hvis man skal sammeligne filer, så er det nok bedst at køre med samme enhed.
Som vil udskrive med ét punktum, f.ex: 1.234 bytes
På et tidspunkt skal vi nok lave det om så vi gemmer størrelse i en 64 bits type, så vi kan arbejde med filer der er større en 4G. Til det brug kunne det være gavnligt at kikke på GetFileAttributesEx
Lad os kigge på FormSize senere, det er OK for nu.
Kan vi ikke i første omgang finde flere ens filer? Så må vi kigge på, hvordan vi få skrevet det ud, så man kan sammeligne filerne. Evt. give de fundne filer numre
Du kan starte med at finde alle filer der hedder det samme, og lave en liste af disse. Og derpå lave en liste af ens filer, og lave en liste af disse. Og til slut finde lighedspunkter mellem de to lister.
Hvis du laver loopen så et entry i EqualFilesList er enten med ens navne ELLER med ens indhold, kan du hver gang du finder to ens filer, checke om en af filerne allerede er i listen, og er ens på samme måde, kan du tilføje den anden fil til samme EqualFilesClass objekt.
Derved har du info om alle filer der er ens, og du kan skrive dem ud.
Når du har det på plads kan du checke om to entries i EqualFilesList indeholder de samme filer, men er ens på de to forskellige måder. Hvis du finder to ens entry kan du sætte begge flag i det ene og slette det andet entry.
Jeg tror, at jeg har styr på at lave de to lister (filer med samme navn og filer med samme indhold).
Nu når vi kører med to lister og kan sammenligne disse lister, så kunne vi måske også gøre det muligt, at vælge i mellem disse lister (filer med samme navn, filer med samme indhold, filer med sammen navn og/eller samme indhold).
Jeg skal nok have lidt hjælp til at finde lighedspunkter mellem de to lister.
Jeg forstår ikke helt det sidste, hvor jeg skal sammenligne de to lister. Jeg er ikke helt med på den operator fejl jeg får, når jeg kompiler programmet.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.