Datei "durchscannen" und säubern
-
Hallo, ich bins mal wieder

Ich habe mehr oder weniger 2 Probleme bzw. Fragen. Erstmal die Geschichte:
Es handelt sich nicht um normale Textdateien sondern um Dateien mit der Endung ".pt". Es geht darum, dass ganze File "durchzuscannen" und alle "0x63" Werte rauszulöschen weil sie da drin unnötig sind und die Dateien nur zwecklos grösser machen (sogar ziemlich grösser). Ich habe mir dabei gedacht, dass das Programm erstmal alle Dateien aus dem Ordner und den Unterordnern ausliest den ich angegeben habe (habe ich bereits fertig). Danach erstellt es eine exakte "temporäre" Kopie aller File-Namen mit einer zusätzlichen ".pr" Endung (steht für PRocess). Also wenn jetzt z.B. ein File "12345.pt" heisst, wird ein leeres neues File erstellt welches "12345.pt.pr" heisst. Selber Name, zusätzliche Endung, kein Inhalt. Soweit sollte ich es noch schaffen. Jetzt zum eigentlichen Probem: Dem Scannen. Ich dachte mir, dass ich das File durchscannen lasse und alle Datentypen die NICHT "0x63" sind in dieses erstellte ".pr"-File speichere. Dies muss fast so sein weil man ja das File während dem scannen schlecht direkt verändern kann und es auch einfacher geht gerade alles was brauchbar ist in ein neues zu speichern. Am Schluss des Prozesses wird die originale ".pt" Datei gelöscht und die neue ".pr" Datei ohne diese "0x63" umbenannt resp. das ".pr" am Schluss weggenommen.

Jetzt zum eigentlichen Problem:
- Wie kann ich ein File mit der Endung .pt öffnen um es weiterzuverarbeiten?
- Wie lässt sich ein File so durchscannen nach Werten? Ich mein, es ist logisch dass ich eine if-Schleife machen muss die abfragt ob es gerade "0x63" ist aber das Ganze muss in einer do-while Schleife sein und ich peil deren Ablauf nicht.
- Wenn ich das File mit einem Textbearbeitungsprogramm öffne sieht es ungefähr so aus (ist nur ein klitzekleiner Abschnitt): HIER KLICKEN . Wie kann ich nicht nach DEM Inhalt suchen sondern nach den Teilen da oben wie "0x63" etc?
Sind wahrscheinlich etwas schwerere Fragen aber hier gibt es auch jede Menge guter Coder die vielleicht einige Ansätze, Anregungen etc. geben können

Danke im Voraus
-
Hm, also du kannst die Datei mit ofstream öffnen, und mit ifstream die
kopie schreiben. (steht in der C++ FAQ).
Dann könntest du dir ne funktion basteln, die den Pfad der Datei übergeben
bekommt, diese öffnet und durchscannt, und dann als .pr abspeichert.
Zum Durchsuchen kannst du unter MFC CFileFind nehmen. Ich hab mal ne
Klasse geschrieben, die das Scannen erleichtert, und CFileFind kapselt DirScanner
Der Klasse kannst du auch Funktionspointer übergeben, auf deine Funktion...Devil
-
Danke für deine Antwort. Das mit der Funktion dachte ich mir auch und das übergeben selber ist auch nicht das Problem. Das einzige Problem was ich jetzt habe und einfach nicht lösen kann ist das "scannen" des Files. Folgendes Datenblatt habe ich hier:
Scanner (NV6)
Datentyp: 0x63Header:
1 Byte (BYTE) | 2 Byte (WORD) | 1 Byte (BYTE) | 1 Byte (CHAR)Ich muss also immer den Header auslesen und nur falls es dieses 0x63 ist soll er nichts unternehmen und einfach weiterleen, ansonsten alles ins neue File speichern. Vielleicht könnte man was mit .read machen aber ich hab schon in der MSDN Library geschaut aber die hilft mir keinen Millimeter weiter.
-
Hy ,
du willst so wie ich das verstehe eine nicht textdatei öffen , byteweise abchecken ob es sich um 0x63 handelt oder nicht und alle nicht 0x63 info abspeichern ?//binär einlesen CFile cfb( filename , CFile::modeRead|CFile::typeBinary) ; DWORD lenB = cfb.GetLength() ; //grösse der einzulesenden datei unsigned int bmfArrLen; //array länge BYTE *bmfArr; //unsigned byte //falls es sich um short oder integers handelt einfach tyen ersetzen bmfArrLen = (lenB/sizeof(WORD)) ; bmfArr = new BYTE[bmfArrLen]; //unsigned short cfb.Read(bmfArr , lenB); // jetzt liegt erstmal die ganze datei im BYTE Array bmfArr for(int i= 0 ; i < bmfArrLen ; i++) { if(bmfArr[i] != 0x63) { //in neues ByteArray schreiben und diese sichern } } cfb.Close();
-
Hy ,
du willst so wie ich das verstehe eine nicht textdatei öffen , byteweise abchecken ob es sich um 0x63 handelt oder nicht und alle nicht 0x63 info abspeichern ?Genau.
Danke dir für den Aufwand! In der Abfrage selber sehe ich keinen Fehler aber er gibt mir an "C:datei.pt2 wurde nicht gefunden". Zuerst dachte ich er hat ein Problem mit dem übergeben des Pfades und habe kurz eine neue Variable erstellt und dort einen statischen Pfad deklariert. Doch komischerweise geht das auch nicht. Das File existiert 100%, den Pfade habe ich auch 100% richtig angegeben aber wie man sieht schneidet er das "\" nach "C:" ab, obwohl ich das geschrieben habe. Gibts ein spezielles Zeichen dafür oder wie seh ich das? So einen Fehler hab ich noch gar nie gesehen, hab schon öfter mit Pfaden gearbeitet.
-
hy ,
das problem liegt auf der hand .. jedes darzustellende \ muss durch \\ angebenen werden. ist klar dass er die datei nicht findet ..
ebenfalls ein " muss durch \" und ein ' durch \' dargestellt werden ..
-
Ja, es war mir nach 5 Minuten auch offensichtlich und ich habs geändert. Trotzdem danke.
Nun doch noch eine kurze Frage: Du speicherst ja in deinem Codeteil das ganze File in einem Array, das funzt ja auch wunderbar aber ich konnte dem Datenblatt gerade entnehmen, dass nicht alle 63-er gelöscht werden sollen sondern nur ganze sog. ScannerMeldungen. Das sind die mit dem Header den ich ein paar Posts vorher geschrieben habe. Sie haben z.B. im Header zuerst die Länge, danach der timestamp, dann der typ und schlussendlich die eigentlichen daten. Der Typ ist hierbei wichtig. Wenn der 63 ist sollte es übersprungen werden. Ich verlange kein komplett funktionstüchtigen Codeteil, ich würd nur gerne wissen wie ich das auslesen kann und wie danach weiterverarbeiten und ne Abfrage draus machen. Dass es mit .read gehen sollte weiss ich. Sorry wenn ich so viel Fragen stelle aber mir ist das ganze binärverarbetungszeugs irgendwie noch neu.
-
Hy ,
naja du suchst , wenn ich es richtig verstehe nach einem Teil einer kl. struktur, die aus(Länge , Timestamp, typ , daten) bestehen ..nämlich nach typ=0x63.
jetzt muss du dir überlegen wieviel bytes jeweils deine elemente der struktur haben (zb länge = 4 bytes ), zb.timestamps = 4 bytes , zb.typ = 1 byte , zb. daten = variable 20 bytes) //nur bsp.grössen
dann musst du dynamisch abfragen , sich (hier in diesem bsp ) an bytestelle
9 die 0x63 befindet. wenn ja kannst du die anzahl der bytes (hier 4+4+1+20 = 29) getrost wegschmeissen ..Achtung: Du musst dir überlegen wo (für bsp länge , also 4 bytes) das erste byte liegt und wo das letzte ..
-
Danke für die Erklärung. Mir ist der Ablauf was ich machen müsste eigentlich fast immer ziemlich klar da ich vorher schon nicht wenig Programmiererfahrungen hatte. Mein grösstes und fast einziges Problem ist die Sprache C++ an sich, also der Code. Ich kann mir genau vorstellen was eine Applikation machen muss um dazu zu kommen nur finde ich selten (auch anhand des MSDN) 100%ig raus wie es nun mit dem Code ausschaut. Es gibt irgendwie immer 10 Möglichkeiten z.B. ein File zu öffnen und anfangs verwirrt das denke ich viele. Deshalb frage ich hier so viel bis ich die Materie etwas vertieft habe, hoffe das stört nicht.
Zum Programm zurück: Wie gesagt verstehe ich WAS GENAU er machen müsste damit ich zum gewünschten Ergebnis komme, ich peile aber nicht wie ich das realisieren soll. Hier erstmal die gesamte Funktion (filename ist nur vorübergehend statisch aus Testzwecken):
CString filename = "C:\\datei.pt2"; CString prFilename = filename + ".pr"; int arrayLenght; WORD *array; //WORD = 16bit unsigned int CFile file(filename, CFile::modeRead); //Open file (binary reading is standard) DWORD fileLenght = file.GetLength(); //Lenght of the current file (DWORD = 32bit unsigned int) arrayLenght = (fileLenght / sizeof(WORD)); //WORD = 16bit unsigned int array = (WORD*) calloc(arrayLenght, sizeof(WORD)); file.Read(array, arrayLenght*sizeof(WORD)); //The whole file is in the BYTE Array 'array' WORD lenght; //WORD = 16bit unsigned int DWORD timestamp; //DWORD = 32bit unsigned int BYTE type; //BYTE = 8bit unsigned int BeginWaitCursor(); CFile prFile(prFilename, CFile::modeCreate | CFile::modeWrite); //Open the .pr File (ProcessFile) for(int i = 0; i < fileLenght; i++) { WORD varLenght = file.Read(&lenght, 2); //Read the lenght (2 Byte) DWORD varTimestamp = file.Read(×tamp, 4); //Read the timestamp (4 Byte) BYTE varType = file.Read(&type, 1); //Read the type (1 Byte) DWORD data; DWORD varData = file.Read(&data, lenght); if(type != 63) { prFile.Seek(0,CFile::end); prFile.Write(lenght, 2); prFile.Write(timestamp, 4); prFile.Write(type, 1); prFile.Write(data, lenght); } } prFile.Close(); //Close the ProcessFile EndWaitCursor(); file.Close(); //Close the main-file free(array); //Clear memory (IMPORTANT)EDIT: Ich hatte nur n paar logische Fehler, jetzt sollte im oberen Part eigentlich soweit alles nicht schlceht sein. Hab nur Probleme mit dem write. Er sagt immer
"cannot convert parameter 1 from 'unsigned short' to 'const void *'". Wie kann ich das lösen? Danke im Voraus.
-
Hy ,
also zu deinem Parameterübergabeproblem :
ich nehme an du benutzt CFileDialog ?!
dann füge einfach den cast ein :CString path = cfd.GetPathName(); //CString nach char* casten //LPCTSRT = long pointer const string char * szMyString = (char *) (LPCTSTR) path;dann sollte es wieder funzen ...
PS. : deine for-schleife kannste optimieren .. mach ne while draus und überprüfe dann erst bei byte(zb 9)weiter .. nicht jedes byte durchgehen und checken.
Wieso eigentlich free? kannst doch delete [] arrayname machen ..
-
Ich habs immernoch nicht geschafft. Nun ist das ganze Programm rundum fertig und ich hab eigentlich alle Klassen, Abfragen, rekursive Scans etc. gemacht. Es hapert nur an dieser einen Funktion. Der Quelltext ist eigentlich gleich geblieben also brauche ich den nicht nochmal zu posten, man kann ihn oben einsehen. Die normale .pt Datei ist meistens so von 3-5MB. Wenn ich es ausführe wird die .pt.pr Datei locker mal schnell 700MB gross. Er schreibt wahnsinnig viel Schrott rein und ich weiss nicht wie ich das ganze irgendwie "intelligenter" scannen und schreiben lassen kann. Was mache ich falsch? Beispiele? Wäre dankbar um jede Hilfe.
-
hy ,
1.) weil du nirgends deine werte aus dem eingelesen array benutzt?
(willst du es überhaupt benutzen ansonsten muss du in der schleife
sowieso dafür sorgen das dein filepoiter vorwärtsgerücktwird )
2.) du immer die selben bytes interpretierst und für jedes byte!!
(so tun würdest als ob es ein header bate ist und somit immer 4+2+1 bytes
in die datei schreibst)// dieser vorschlag gilt nur wenn du statt unsignde short byteweise einliest // habe ich nicht getestet nur so provisorisch aufgestllt.. int i=0; WORD varLenght=0; DWORD varTimestamp=0; BYTE varType=0; while( i < fileLenght ) { //Read the lenght (2 Byte) varLenght = array[i]<<8 ; varLenght = varLenght | array[i+1] ; //Read the timestamp (4 Byte) varTimestamp = array[i+2]<<24 ; varTimestamp = varTimestamp | array[i+3]<<16; varTimestamp = varTimestamp | array[i+4]<<8; varTimestamp = varTimestamp | array[i+5]; //Read the type (1 Byte) varType = array[i+6]; // falls die struktur dynmisch in der grösse ist .. hier anpassen // ist dein data 2 bytes ? oder ist es danamisch // also sowas wie sizeof(length) = entspricht 2 //oder in length ist der wert zb 20 , die anzahl der bytes if(type != 63) { prFile.Seek(0,CFile::end); prFile.Write(lenght, 2); prFile.Write(timestamp, 4); prFile.Write(type, 1); prFile.Write(data, lenght); } i+=7; //anzahl bytes der struktur // falls die struktur dynmisch in der grösse ist .. hier anpassen //also die 7 ggf um 2 erhöhen oder den dynamischen wert. // i+=(7+lenght) ; }
-
Wenn ich ehrlich bin versteh ich davon keinen Meter. Dass du Byte für Byte ausliest und in ein Array speicherst ist mir klar aber du liest ja nicht direkt das File aus weil du kein CFile.read() benutzt oder seh ich das falsch? Sobald ich irgend einen Scheiss in das prFile schreiben will mit dem Write gibt er mir einen Fehler aus. Zum "i += 7" fällt mir auch absolut nichts ein. Langsam hab ich das Gefühl ich gebs auf...
-
Herzlich willkommen in der welt von c++
, gib bloss schnell auf und du hast deutlich mehr freizeit ..*g*Also jetzt nochmal für die steinmetze zum mitmeisseln..
Ich bin davon ausgegangen ,dass du den oberen teil von dir/mir übernimmst und es mit byte inliest..deshalb habe ich kein read() usw geschrieben ..(mitdenken..)Ausdrucksbeschreibung:
varTimestamp = varTimestamp | array[i+3]<<16;
//shifte das i+3 byte um 2 byte nach links //4 3 2 1
//weil varTimestamp (4 byte type) ist, steht jetzt das i+3 byte 0x00**0000 an
//der ** position//i+=7; ist das gleiche wie i= i+7 nur kürzer .. sollte aber bekannt sein
das mit dem schreiben könntest du auch anders machen ..
in einem zusätzleichen array speichern ..und am ende alles schreiben..
So muss jetzt leider nach hause..
und kann erst morgen wieder reinschauen (mein arbeitgeber sei dank*g*)..habe so das gefühl du schriebst wieder*g*
cu Andreas
-
So, endlich habe ich es geschafft. Das Ganze funktioniert einwandfrei und die Dateien verlieren 2/3 ihrer ursprünglichen Grösse weil diese unnötigen ScannerMeldungen (die ich ja jetzt rausfiltere) ziemlich viel Platz brauchen. Vielen Dank für deine Hilfe wuTangl, warst ja der Einzige der mir geholfen hat
. Anfangs bin ich immer etwas schwerfällig was das angeht aber man sollte auch beachten dass ich erst seit 2 Wochen C++ mache. Ich hab heute morgen einfach mal meine letzte Logik hervorgeholt und versucht das Ganze so anzuschauen wie es der PC macht, hat eigentlich gut geklappt.Für die die es noch interessiert noch die fertige Scanfunktion:
BOOL CScanFilterDlg::ProcessFile(CString currentFile) { UpdateData(TRUE); int arrayLenght; int i = 0; BOOL ok; CString file; CString prFilename = currentFile + ".pr"; WORD varLenght = 0; DWORD varTimestamp = 0; BYTE varType = 0; BYTE *array; CFile cfile(currentFile, CFile::modeRead); //Open file (binary reading is standard) DWORD fileLenght = cfile.GetLength(); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////SAVE THE WHOLE FILE IN AN ARRAY////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// arrayLenght = (fileLenght / sizeof(BYTE)); array = (BYTE*) calloc(arrayLenght, sizeof(BYTE)); cfile.Read(array, arrayLenght*sizeof(BYTE)); CFile prFile(prFilename, CFile::modeCreate | CFile::modeWrite); //Create ProcessFile ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////MOST IMPORTANT SCANNING LOOP///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// while(i < fileLenght) { //Read the Lenght (2 Byte) varLenght = array[i]; varLenght = varLenght | array[i + 1] << 8; //Read the Timestamp (4 Byte) varTimestamp = array[i + 2] << 24; varTimestamp = varTimestamp | array[i + 3] << 16; varTimestamp = varTimestamp | array[i + 4] << 8; varTimestamp = varTimestamp | array[i + 5]; //Read the Type (1 Byte) varType = array[i + 6]; if(varType != 0x63) { prFile.Seek(0,CFile::end); //Jump to End of File prFile.Write(&array[i], (7 + varLenght)); //Write whole Message } i = (i + 7 + varLenght); //Set Cursor on next MessageStart } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////COMPLETE PROCESS BY CLOSING, DELETING AND RENAMING THE NECESSARY FILES/////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// cfile.Close(); //Close OldFile prFile.Close(); //Close ProcessFile cfile.Remove(currentFile); //Remove OldFile prFile.Rename(prFilename, currentFile); //Rename ProcessFile free(array); //Clear Memory (IMPORTANT) return ok; //Return ok UpdateData(FALSE); }