C++ Exel Tabellen bearbeiten

Zkor

Lt. Junior Grade
Registriert
Juli 2007
Beiträge
294
Hallo Leute!

Ich habe folgende Aufgabe zu bewältigen: Ein Programm unter Windows zu implementieren, welches beim Start von Windows die Systemzeit in eine Exel-Tabelle schreibt und beim Abmelden die Abmeldezeit + Differenz dokumentiert.

Soweit bin ich gekommen:

Ich bin mir wohl gewusst das es in vielen Fällen wohl einfacher gehen könnte und bin auch gerne bereit etwas von euch zu lernen :). Bei dieser Implementierung ging es rein um die Funktionalität des Programmes und ich habe es soweit noch ohne eure Hilfe geschafft :p

Code:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <Lmcons.h>
#include <string>
#include <ctime>
#include <sstream>
#include <fstream>


using namespace std;

int main ( )
{
    cout << "ANFANG" << endl << endl;

    //Username feststellen
    string benutzer = getenv("USERNAME");
    string pfadordner = getenv("USERPROFILE");
    string logordner;

    //Variablendeklarationen
    const bool MANUELL = true;                  //mit Abfragen
    int jahr, monat, tag, stunden, minuten;
    char in;
    string zeit, datum;
    ostringstream s1, s2, b1;
    time_t rawtime;
    struct tm * timeinfo;

    time ( &rawtime );
    timeinfo = localtime ( &rawtime );

    jahr    = 2012;                             //(timeinfo->tm_year); funktioniert iwie nicht
    monat   = (timeinfo->tm_mon)+1;
    tag     = (timeinfo->tm_mday);
    stunden = (timeinfo->tm_hour);
    minuten = (timeinfo->tm_min);

    s1 << stunden << ":" << minuten;
    zeit = s1.str();


    s2 << tag << "." << monat << "." << jahr;
    datum = s2.str();


//    cout << "tag: "     << tag  << endl;
//    cout << "monat: "   << monat<< endl;
//    cout << "year: "    << jahr << endl;

    if(MANUELL)
    {
        while(in != 'y')
        {
            system("cls");
            cout << "Bitte folgende Daten ueberpruefen: " << endl
                 << "Bei Fehler Installation abbrechen und sich an Programmierer wenden" << endl << endl;

            cout << "ZEIT: "      << zeit       << endl;
            cout << "DATUM: "     << datum      << endl;
            cout << "BENUTZER: "  << benutzer   << endl;
            cout << "PFAD: "      << pfadordner << endl;
            cout << endl << "Alles richtig? (y/n)"      << endl;
            cin >> in;
            if(in == 'n')
            {
                cerr << "Das Programm wird beendet ... " <<endl;
                exit(2);
            }
        }
    }

    //Neuen Ordner im Pfad "pfadordner\log" erstellen
    logordner = pfadordner + "\\Documents\\log";
    string befehl = (string)"if not exist " + logordner + " md " + logordner;
    cout << befehl;

    //Öffnen der Hilfsdatei (Hier musste ich kreativ sein weil ich nicht wusste wie ich den string befehl an system("") übergeben kann)
    ofstream datei("help.cmd", ios::app);
    if(!datei.fail())
    {
        //datei << "@echo off" << endl;
        datei << befehl;          //In die Datei schreiben
        datei.close();            // Datei schließen
    }

    //Ausfuehren der Datei erfolgreich?
    if (system("@echo off; help.cmd") != 0)
    {
        cerr << "Beim Anlegen des Ordners ist ein Fehler unterlaufen." << endl
             << "Das Programm wird beendet ... " << endl;
        exit(3);
    }

    //Loeschen der Datei erfolgreich?
    if( system("del help.cmd") != 0 )
        cerr << "Fehler beim Loeschen der Datei" << endl;
        else
        cout << "Loeschen erfolgreich"; << endl;


    //Batch Datei in Autostart einfuegen (kommt noch)

    cout << "ENDE" << endl << endl;
    return 0;
}

wie kann ich nun die Exel-Datei öffnen und in die einzelnen Felder die Systemzeiten schreiben?

Kleine Nebenfrage noch: kann man das mit der Hilfsdatei help.cmd irgendwie anders lösen?
Das Problem bestand darin, dass ich den Befehl in Zeile 77 nicht als char casten konnte weil der Befehl definitiv zu groß ist. Und mit system("string"); kommt man auch nicht weit...

vielen Dank für eure Hilfe schonmal :)
 
Als alternative schlage ich Java + Apache POI vor
 
Microsoft Office benutzt doch jetzt das offene Format OOXML. Einfach die 6000 Seiten Spezifikation lesen und die Richtige der 200 verschiedenen sich widersprechenden Timestamprepräsentationen aussuchen.
Fertig!
 
Zkor schrieb:
Kleine Nebenfrage noch: kann man das mit der Hilfsdatei help.cmd irgendwie anders lösen?
Das Problem bestand darin, dass ich den Befehl in Zeile 77 nicht als char casten konnte weil der Befehl definitiv zu groß ist.

Wie meinst du das? Ich wüßte nicht, daß es für den Kommando-String, den man std::system() übergeben kann, irgend welche Längeneinschränkungen gibt.
 
Die Java alternative ist in Bearbeitung ;) !
Bin ich eh fitter drin als in C++.

@antred: Sry ich hab mich misverständlich ausgedrückt:

Mein Problem liegt darin, dass ich Ausdrücke wie:

String pfad = C:\Users

system("md" + pfad + "\\benutzer");

nicht übergeben kann, da das system("") einen char-pointer erwartet.
Wie aber soll ich diesen Ausdruck (oben) in char umwandeln? Er ist einfach zu lang.
durch einfaches eingeben von:

system("md C:\Users\benutzer");

würde es zwar gehen, das ist wiederum aber nur statisch... das war eig meine Frage, wie ich längere Variablere befehle ans system übertragen kann.
 
Ist es ne Option einfach eine .csv zu nehmen? Lässt sich ja dann auch mit Excel öffnen.
Wenn du MSVC nutzt kann man auch die COM Schnittstelle von Excel nutzen...
 
Ist es ne Option einfach eine .csv zu nehmen? Lässt sich ja dann auch mit Excel öffnen.
@Mortal: Du Fuchs! Für mich hört es sich so an als ob es eine Hausaufgabe wäre. Mit Deiner raffinierten als auch zeitsparenden Lösung würde er mit Sicherheit eine eins bekommen! :D





................ NICHT :p
 
Zkor schrieb:
Die Java alternative ist in Bearbeitung ;) !
Bin ich eh fitter drin als in C++.

@antred: Sry ich hab mich misverständlich ausgedrückt:

Mein Problem liegt darin, dass ich Ausdrücke wie:

String pfad = C:\Users

system("md" + pfad + "\\benutzer");

nicht übergeben kann, da das system("") einen char-pointer erwartet.

std::string hat eine c_str()-Methode, die einen const-char-Pointer auf den Inhalt des String-Objekts liefert.
 
@antred: Vielen Dank!!! Das erleichtert so viele Probleme ... hab dadurch jetzt das Programm auf die Hälfte runterkürzen können mit:

Code:
const char* b2 = befehl.c_str();
system(b2);

@dammi: Hier meine in Java umgesetzte Lösung so far :p :

Code:
/**
 * 
 */
package oza.log;

import java.awt.geom.IllegalPathStateException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

/**
 * @author Zkor
 *
 */
public class log {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("hh:mm");
		SimpleDateFormat dmy = new SimpleDateFormat("dd.MM.yyyy");
		Scanner sc = new Scanner(System.in);
		
		
		String zeit = sdf.format(date);
		String datum = dmy.format(date);
		String benutzer;
		String pfad;
		String in = "x";

		
		benutzer = System.getenv("USERNAME");
		pfad = System.getenv("USERPROFILE");
		
		
		while (!in.equals("y") && !in.equals("Y") ) 
		{
			System.out.println("Bitte folgende Daten ueberpruefen: ");
			System.out.println("Bei Fehler Installation abbrechen und sich an Programmierer wenden");
			
			System.out.println("Benutzer: " + benutzer);
			System.out.println("Pfad: " + pfad);
			System.out.println("Zeit: " + zeit);
			System.out.println("Datum: " + datum);
			
			System.out.println("Alles richtig? (y/n)");
			
		     
		     in = sc.next();
		     if (in.equals("n"))
		     {
		    	throw new IllegalMonitorStateException("Das Programm wird beendet ... ");
		    	 
		     }
		}
		
		
		
		//Ordner anlegen
		
		pfad = pfad + "\\Documents\\log";
		String befehl = "if not exist " + pfad + " md " + pfad;
		
		System.out.println(befehl);
		try 
		{
			String cmd = "cmd /C " + befehl;
		    Process child = Runtime.getRuntime().exec(cmd);
		    
		    
	        InputStream ins = child.getInputStream();
	        int c;
	        while ((c = ins.read()) != -1) {
	            System.out.print((char)c);
	        }
	        ins.close();
		}
		catch (Exception ex)
		{
			throw new IllegalPathStateException(ex.getMessage());
		}
		
		
	}

		
		
		

	}

wie geht es jetzt weiter? Habe die apache.zip runtergeladen :p ...

Noch eine Nebenfrage: Wie kann ich das Endprogramm eig. generell in eine exe conv. oder ausführbar machen? Kann man auch irgendwie einen Icon includen für die Optik? :D Ok jetzt wird Spielerei...
 
@Zkor:
Ich dachte in Java bist Du fitter? :D

Ich weiß zwar nicht was Du Dir mit der "apache.zip" heruntergeladen hast, was Du aber benötigst sind die Binaries für das POIs Projekt (Klick mich), welche Du dann in Dein Eclipse Projekt über "Rechtsklick auf Dein Projekt -> Build Path -> Configure Buildpath -> Add external Jar" einbinden musst (es geht um die "poi-3.7-20101029.jar").

Was Du benötigen wirst sind folgende Klassen:
- HSSFRow;
- HSSFSheet;
- HSSFWorkbook;

Die API Doku findest Du hier: Klick mich

Noch ein kleiner Tipp:
Um einer NullPointerException zu entgehen, bietet es sich an anstelle von "while (!in.equals("y") && !in.equals("Y") )" umgekehrt zu prüfen, also so: "while (!"y".equals(in) && !"Y".equals(in) )".
 
:D fitter ja, aber das heißst noch lange nicht das ich fit bin.

Verzeih mir bitte meine Unwissenheit, aber nach stundenlangem rumlesen der Dokus komm ich doch nicht ganz weiter :(.

Ich bräuchte noch einen kleinen Anhaltspunkt, ... die jar habe ich eingebunden und bin soweit, das ich die von dir genannten Klassen benutzten kann. Nur kann ich noch nicht ganz nachvollziehen in welcher Relation sie zueinander stehen.

Wie kann ich nun mit Hand dieser Klassen eine bereits vorgefertigte Exel-Tabelle aufrufen und auf die eizelnen Zeilen zugreifen? (kleiner Beispielcode?) Die Dokus gehen sehr spärlich mit den Beschreibungen zum Einsatz der Klassen um :D.


EDIT:
Ich ergänze mal ein wenig: Das was ich soweit in Erfahrung bringen konnte ist folgendes:

Code:
		//Exel-Datei Oeffnen
		HSSFWorkbook wb = new HSSFWorkbook();
		
		Sheet sheet = wb.createSheet("log");
		
		sheet.createRow(1);
		
		int is = wb.getNumberOfSheets();
		
		System.out.println(is);

Also ist das Workbook die eigentliche Exel-Datei, der Sheet die Tabelle und die Row's die einzelnen Zeilen?

Ich muss dem Workbook ja irgendwie den Pfad der Exel-Datei zukommen lassen, oder?
Ich hatte da an folgenden Konstruktor gedacht:

public HSSFWorkbook(DirectoryNode directory,
POIFSFileSystem fs,
boolean preserveNodes)
throws java.io.IOException

nur weiß ich nicht wie ich den DirectoryNode initialiseren kann, bzw. wie ich ihm den Pfad mitteilen kann. Wofür genau stehen die anderen beiden Parameter?

Fragen über Fragen ich weiß ... sry :D
 
Zuletzt bearbeitet: (Ergänzung)
jSmooth damit kannste aus deiner .jar ne Exe kompilieren. Findest bei SourceForge
 
Das funktioniert wunderbar danke!

Nur noch eine Frage dazu: Die sysouts bekomme ich leider jetzt nicht mehr mit. Wie kann ich die Ausgabe auf eine Konsole umwandeln?

Klar ich könnte jedes mal einen echo Befehl machen. Aber das muss doch auch einfacher gehen oder?
 
Einen anderen Wrapper nehmen.
Bzw. den Console Wrapper wenn mich nicht alles täuscht.
 
mit Launch4j hab ich es jetzt endlich geschafft. Er hatte ein Problem mit dem Pfad zur Java Datei. JSmooth ist bei mir dauernd abgeschmiert.... ES GEHT VORAN :) VIELEN DANK SCHONMAL...

Aber das Problem mit der Implementierung in Exel bleibt weiter bestehen...
Wie kriege ich meine xls Datei ins Programm?
 
Nicht gleich beim ersten Ergebnis aufgeben! :D

Das Einlesen/Ausgeben ist da echt einfach. Einlesen geht über den FileInputStream und Schreiben über den FileOutputStream.
Hab leider grad kein Eclipse zur Hand, hier aber mal zwei Beispiele (kanns aber nicht überprüfen)

Beispiel Lesen:
File file = new File("C:\\blub.xls");
FileInputStream fileIn = new FileInputStream(file);
HSSFWorkbook workbook = new HSSFWorkbook(fileIn);
...

Beispiel Schreiben:
HSSFWorkbook workbook = new HSSFWorkbook();
...
FileOutputStream fileOut = new FileOutputStream("C:\\blub.xls");

workbook.write(fileOut);
fileOut.flush();
fileOut.close();

Den Rest suchste Dir aber selbst zusammen, oder? :D
 
Zurück
Oben