Forum: PC-Programmierung Texte kombinieren


von nowhere (Gast)


Lesenswert?

Hab folgende Anforderung und stehe noch auf dem Schlauch...

Anforderung:

mehrere Textdateien zusammenfassen wobei die Texte so in die Zieldatei 
übernommen werden daß zu jedem Zeilenanfang jeweils der ursprüngliche 
Dateiname aufgeführt wird.


Beispiel:3 (bzw. x Files) einzelne Textdateien

Dateiname: ABC.TXT
Dateiinhalt:
24.05.1988 Tne.
Keine Info
27.08.1988 zweifach Frnt

Dateiname: 33TVB.TXT
Deateiinhalt:
30.01.2016 Follow up
1.05.2017 Tne.

Dateiname: X2789TLM.TXT
Dateiinhalt:
nicht verfügbar
Silverdot 246+!
27.6.1988 zweifach stint


Ergebnis Zusammenfassung in Neue Datei Output.TXT

Dateiname: Output.TXT
Dateiinhalt:
ABC.TXT 4.05.1988 Tne.
ABC.TXT Keine Info
ABC.TXT 27.08.1988 zweifach Frnt
33TVB.TXT 30.01.2016 Follow up
33TVB.TXT 1.05.2017 Tne.
X2789TLM.TXT nicht verfügbar
X2789TLM.TXT Silverdot 246+!
X2789TLM.TXT 27.6.1988 zweifach stint


Programmauswahl Parameter Abfrage

- Verzeichnis der Source Files
- Verzeichnis Target File
- Target File Name

Übernahme Parameter
- mit oder ohne Dateiname
- Falls ja Dateiname übernehmen Abfrage
- Startzeichen des Dateiname z.B. ab 2. Zeichen ect.
- Übernahme File extension J/N
Wenn ja mit dot Übernahme

Dank im voraus
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <sys/types.h>
4
#include <sys/stat.h>
5
#include <fcntl.h>
6
#include <unistd.h>
7
#include <dirent.h>
8
#include <string.h>
9
char* in_path = "./inp/";
10
char* out_path = "./out/output.txt";
11
int fd;
12
int fd2;
13
ssize_t gelesen;
14
ssize_t schreiben;
15
char buf[1];
16
int main() {
17
    char* tmp;
18
    DIR *dir;
19
    struct dirent *infolder;
20
    fd2 = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
21
    if (fd2 == -1) {
22
        perror("Fehler bei open der output Datei\n");
23
        exit(EXIT_FAILURE);
24
    }
25
    dir = opendir(in_path);
26
    if (dir != NULL) {
27
        while (1) {
28
            infolder = readdir(dir);
29
            if (infolder == NULL) break;
30
            while (strcmp(infolder->d_name, ".") == 0
31
                    || strcmp(infolder->d_name, "..") == 0) infolder = readdir(dir);
32
            if ((tmp = malloc(strlen(in_path) + strlen(infolder->d_name) + 1)) != NULL) {
33
                tmp[0] = '\0';
34
                strcat(tmp, in_path);
35
                strcat(tmp, infolder->d_name);
36
                printf("infolder->d_name = %s\n", infolder->d_name);
37
                printf("tmp = %s\n", tmp);
38
            } else {
39
                perror("ERROR: buildPath");
40
                exit(EXIT_FAILURE);
41
            }
42
            fd = open(tmp, O_RDONLY); /* input */
43
            if (fd == -1) {
44
                perror("ERROR: bei inputfile open\n");
45
                exit(EXIT_FAILURE);
46
            }
47
            free(tmp);
48
            do {
49
                gelesen = read(fd, buf, 1);
50
                printf("buf = %s\n", buf);
51
                if (gelesen == -1) {
52
                    perror("Fehler bei read");
53
                    exit(EXIT_FAILURE);
54
                }
55
                printf("gelesen %zd und das Zeichen ist %c\n", gelesen, buf[0]);
56
                // jetzt write() anwenden
57
                schreiben = write(fd2, buf, 1);
58
                if (schreiben == -1) {
59
                    perror("Fehler bei write");
60
                    exit(EXIT_FAILURE);
61
                }
62
            } while (gelesen > 0);
63
            close(fd);
64
        }
65
        closedir(dir);
66
        close(fd2);
67
    } else {
68
        perror("ERROR: directory kan nicht geoeffnet werden\n");
69
        exit(EXIT_FAILURE);
70
    }
71
    return 0;
72
}
--

Mit den richtigen Tags [ c ] [ /c ] wird so ein Quelltext doch gleich 
etwas lesbarer, oder?
-rufus

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Warum in C? Ist C Teil der Anforderung? Für solche Aufgaben eignet sich 
jede Scriptsprache (Python, Perl, Matlab, Basic, Tcl) besser als C. 
Schon allein wegen der Zeichensätze, die Textdateien haben können.

Wenn's in C sein muß: Versuch erst einmal ein Programm zu schreiben. 
Funktionalitäten in Funktionen zusammenfassen. Nicht wie ein langes 
Skript.

: Bearbeitet durch User
von Jack (Gast)


Lesenswert?

Hast du eine Linux-Umgebung zur Verfügung? Da ist das ein Einzeiler
1
awk '{print FILENAME, $0}" ABC.TXT 33TVB.TXT X2789TLM.TXT

von Wilfried G. (nowhere)


Lesenswert?

Es soll in C sein, wenn es mit den Zeichensätzen Probleme gibt dann muß 
ich diese halt lösen bzw in der Parameterabfrage den Zeichensatz 
vorgeben.

Der abgebildete Code funktioniert bereits nach Compilierung wenn die txt 
Files und der outputfile im Programmverzeichnis liegen. Mit dem Code 
lassen sich die Texte bereits aus verschiedenen Dateien in der 
output.txt zusammenfassen.
Es fehlt
1. noch der Source Dateiname in den einzelnen Zeilen der output.txt
2. die Parameter Abfrage
3. Auswahl der Übernahme Parameter

von Wilfried G. (nowhere)


Lesenswert?

Ja ist unter Linux aktuell Fedora 27

von Wilfried G. (nowhere)


Lesenswert?

Walter T. schrieb:
> Warum in C? Ist C Teil der Anforderung? Für solche Aufgaben eignet sich
> jede Scriptsprache (Python, Perl, Matlab, Basic, Tcl) besser als C.
> Schon allein wegen der Zeichensätze, die Textdateien haben können.
>
> Wenn's in C sein muß: Versuch erst einmal ein Programm zu schreiben.
> Funktionalitäten in Funktionen zusammenfassen. Nicht wie ein langes
> Skript.

Es soll in C sein, wenn es mit den Zeichensätzen Probleme gibt dann muß
ich diese halt lösen bzw in der Parameterabfrage den Zeichensatz
vorgeben.

Der abgebildete Code funktioniert bereits nach Compilierung wenn die txt
Files und der outputfile im Programmverzeichnis liegen. Mit dem Code
lassen sich die Texte bereits aus verschiedenen Dateien in der
output.txt zusammenfassen.
Es fehlt
1. noch der Source Dateiname in den einzelnen Zeilen der output.txt
2. die Parameter Abfrage
3. Auswahl der Übernahme Parameter

von Wilfried G. (nowhere)


Lesenswert?

Jack schrieb:
> Hast du eine Linux-Umgebung zur Verfügung? Da ist das ein Einzeiler
>
1
> awk '{print FILENAME, $0}" ABC.TXT 33TVB.TXT X2789TLM.TXT
2
>

Ja ist unter Linux aktuell Fedora 27

die einzelnen Dateinamen aufzulisten ist nicht das Ziel,
Ziel ist es die Source Dateinamen am Zeilenanfang der output.txt mit dem 
Text auszugeben wie im Beispiel angegeben.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Wilfried G. schrieb:
> Es soll in C sein

Warum? Ist es eine Hausaufgabe?

Wenn es ein ernsthaftes Programm ist, würde ich, bevor ich irgendeine 
Funktionalität hinzufüge, erst einmal refaktorieren und das Ganze in 
einzelne Funktionen aufteilen. Funktionieren hin oder her: Ich könnte 
auf Anhieb noch nicht einmal sagen, ob die main() wirklich nie ein 
Speicherleck produziert.

von Wilfried G. (nowhere)


Lesenswert?

Walter T. schrieb:
> Wilfried G. schrieb:
>> Es soll in C sein
>
> Warum? Ist es eine Hausaufgabe?
>
> Wenn es ein ernsthaftes Programm ist, würde ich, bevor ich irgendeine
> Funktionalität hinzufüge, erst einmal refaktorieren und das Ganze in
> einzelne Funktionen aufteilen. Funktionieren hin oder her: Ich könnte
> auf Anhieb noch nicht einmal sagen, ob die main() wirklich nie ein
> Speicherleck produziert.


Hier mein Problem:

A) Einlesen erste Datei (1.Source)
    Nimm Dateiname und schreibe ihn in ein File Namens output.a in erste
     freie Zeile
    Nimm den Dateiinhalt und schreibe ihn nach dem letzten bereits
     vorhandenen Text mit vorangestellten Leerzeichen, bei 
Zeilenüberlauf
     beginne die Neue Zeile mit dem Dateinamen (1.Source) und ergänze 
die
     weitere Textübernahme

    Das ganze sooft wiederholen bis der kompl. Texte von 1.Source nach
    output.a übernommen wurde.

B) Einlesen der zweiten Datei (2.Source)
    analog zu A)

C) usw. bis alle txt Dateien eingelesen wurden...

und nun zur Kernfrage wie bekomme ich den Dateinamen in das Targetfile 
output.a?

: Bearbeitet durch User
von imonbln (Gast)


Lesenswert?

Auch ich würde dir raten das nicht unbedingt in C zu machen, IMHO ist C 
zwar immer noch eine wirkungsvolle Programmiersprache auch wenn es gerne 
zu einer C Vs C++ Grundsatz Diskussion hier verkommt. Aber für dein 
Anwendungsfall sind Script sprachen einfach das mittel der Wahl.

Wie auch immer wenn du auf C bestehst, solltest du Dein Programm in 
Funktionen zerlegen und refaktorieren, hier ein paar Tipps

1.) Globale Variablen sind immer ein Anzeichen eines schwachen Design, 
es gibt einige wenige ausnahmen, dein Programm tangiert keine davon, das 
solltest du also schon mal ändern.

2.) Deine Variablen, entweder du nutzt Deutsche oder Englische Namen, 
das Denglisch solltest du lassen, IMHO sollte man englische Bezeichner 
wählen wenn das Projekt keine abweichenden Vorgaben hat, das macht es 
den Internationalen Kollegen später leichter sich auf dein Programm zu 
Konzentrieren. Auch die Bezeichnung deiner Variablen dient vorallen "to 
confuse the Russian", hier mal meinen Senf dazu:
1
char* in_path = "./inp/";             // macht dein Code kaputt wenn es nicht mit Slash endet
2
char* out_path = "./out/output.txt";  // ist kein Path sondern eine Datei
3
int fd;                               // klingt nach lehrbuch aber da Du zwei davon hast würde ich die fdin und fdout nennen
4
int fd2;                              // Superidee den Input fd2 zu nennen das gibt ein richtigen Knoten im Kopf 
5
ssize_t gelesen;                      // in english please
6
ssize_t schreiben;
7
char buf[1];                          // XXX: Array von eins Why? das wäre ein char besser noch du machst es größter da zu nachher mehr.


3.) dein while (1) Konstrukt sieht wüst aus, zumal du mit den if dann 
doch sofort den Exit definiert hast. darf ich vorschlagen das so zu 
machen
1
    while ( (infolder = readdir(dir)) ){;
2
        if (strncmp(infolder->d_name, ".",  1) == 0 ||
3
            strncmp(infolder->d_name, "..", 2) == 0) {
4
            /* skipp current entry */
5
            continue;
6
        }
7
        printf(" entry %s\n", infolder->d_name);
8
        
9
        /* TODO append your code */
10
11
    }


4.) für das zusammenbauen des Path aus in_path und infolder->d_name 
solltest du eine Funktion schreiben, diese kann dann auch gleich noch 
ein paar Plausibilität Prüfungen machen. Im ideal Fall guckst du dir 
hierfür auch mal die Funktion snprintf und asprintf an, beide könnten 
die Helfen dein String eleganter zu erstellen als dein strcat. Generell 
gilt es bei C String Funktionen ohne Längen Prüfung zu vermeiden, also 
strncat statt strcat und so weiter. Eine der größten Schwachen von C und 
ein stetiger quell von Bufferoverflows  sind diese nicht Längen 
prüfenden Stringfunktionen.


5.) mit deinen do-while loop zum lesen und schreiben tust du deinen 
System echt keinen gefallen.
1
 
2
do {
3
    gelesen = read(fd, buf, 1);
4
    printf("buf = %s\n", buf);
5
    
6
    schreiben = write(fd2, buf, 1);
7
} while (gelesen > 0);


 Dein System ließt jedes mal einen kompletten Block a 4K oder so ein um 
dort ein Byte raus zu Operieren dir es zu übergeben und dann schreibst 
du wieder ein einzelnes Byte in eine andere Datei. das Heißt dein System 
vollzieht für jedes einzelne Byte,  2 mal den Kontext switch zwischen 
Userland und Kernel. Im worst case Flushed auch noch irgendwas den Read 
cache und du ließt die Bytes aus dem Sektor der Datei vielfach.

Ich würde vermuten das Hier dein Code so schwach ist das dich jedes 
script schneller ist. Gib deinen Buffer ( remember char *buf[1] ) etwas 
mehr Größe und ließ die Dateien in größeren Blocken (1024 oder so), das 
macht dein Code gleich viel Effizienter, allerdings heist das du musst 
denn Teil etwas umstellen, zum Beispiel muss dein code damit klarkommen 
das der letze readcall weniger als 1024 Bytes liefert.

von Walter T. (nicolas)


Lesenswert?

imonbln schrieb:
> [...]

Volle Zustimmung meinerseits! Das nenne ich mal eine zutreffende und 
gleichzeitig geduldige Erklärung - das kommt schon an einen Karlheinz 
Buchegger dran.


Bin ich eigentlich der einzige, der PNs vom TO mit dem gleichen Text wie 
im Forum bekommt?

von Wilfried G. (nowhere)


Lesenswert?

Walter T. schrieb:
> imonbln schrieb:
>> [...]
>
> Volle Zustimmung meinerseits! Das nenne ich mal eine zutreffende und
> gleichzeitig geduldige Erklärung - das kommt schon an einen Karlheinz
> Buchegger dran.
>
>
> Bin ich eigentlich der einzige, der PNs vom TO mit dem gleichen Text wie
> im Forum bekommt?



Ja, recht hat er, es hilft mir aber nicht das Problem zu lösen, PN#s 
gingen nur an WT.

Wer dichtet mir den Code so um das er wasserdicht wird und den Filenamen 
mit ausgibt? Ich stehe wie gesagt auf dem Schlauch bzw auf der 
Leitung...

von Walter T. (nicolas)


Lesenswert?

Wilfried G. schrieb:
> Wer dichtet mir den Code so um das er wasserdicht wird und den Filenamen
> mit ausgibt?

Hilfreiche Hände findest Du immer noch am besten am Ende Deiner eigenen 
Arme. Mustergültige Tipps hast Du ja schon bekommen (nicht von mir - ich 
meine natürlich imonbln (Gast) ).

von Wilfried G. (nowhere)


Lesenswert?

Walter T. schrieb:
> Wilfried G. schrieb:
>> Wer dichtet mir den Code so um das er wasserdicht wird und den Filenamen
>> mit ausgibt?
>
> Hilfreiche Hände findest Du immer noch am besten am Ende Deiner eigenen
> Arme. Mustergültige Tipps hast Du ja schon bekommen (nicht von mir - ich
> meine natürlich imonbln (Gast) ).

Meine Finger sind schon wund, vieleicht brauch ich mal ein paar Tage 
Pause..

von Walter T. (nicolas)


Lesenswert?

Kein Problem. Du kannst Deinen überarbeiteten Ansatz auch in ein paar 
Wochen vorstellen. Ich genehmige mir für meine Freizeitprojekte durchaus 
auch ab und an einige Wochen Pause.

von Wilfried G. (nowhere)


Lesenswert?

Problem gelöst!
Dank an Alle Beteiligten

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.