2012-03-31 5 views
0

Ich möchte eine Datei Zeile für Zeile in c verarbeiten, alle Zeilen in der Datei müssen Länge 100 Zeichen haben, wenn die Zeile überschritten oder die Zeile leer ist, möchte ich drucken die Nummer der fehlerhaften Zeile und fortlaufend zur nächsten Zeile.Lesen von Zeilen fester Größe aus einer Datei in c

Ich verwende dieses, aber es funktioniert nicht:

int maxLineLen = 101; // 100 + 1 for the '\n' end of line 
char myBuffer[101]; 
FILE *myFile; 

myFile = fopen("dataFile.txt", "r"); 

while (fgets(myBuffer, maxLineLen, myFile) != NULL) { 
    // I can't figure out how to detect and print empty or error lines 
} 

der Dank fro Hilfe.

Edit: Ich habe diese Probe meiner Datei:

              // Empty line : Wrong line 
FirstName-Paolo-LastName-Roberto-Age-23-Address-45,abcdefghijklmnopqrst-CustomerId-xxxxxxxxxxxxxxxx // Correct line 
FirstName-Juliana-LastName-Mutti-Age-35-Address-28,abcdefghijklmnopqrst-CustomerId-xxxxxxxxxxxxxxxABCDEFGHIJx // Exeed the length : Wrong line 
FirstName-David-LastName-Lazardi-Age-59-Address-101,abcdefghijklmnopqrst-CustomerId // Short length : Wrong line 

Wenn ich mein Programm laufen i bekommen sollte:

Line 1 : ERROR 
Line 3 : ERROR 
Line 4 : ERROR 

Antwort

1

Da Sie beide underlength und overlength Linien zuverlässig erfassen müssen, und neu synchronisieren Sie Eingabe nach entweder, es ist wahrscheinlich am einfachsten, eine Funktion zu schreiben, die getc() die Daten lesen verwendet.

Ihre Standard-Funktionsoptionen:

  • fgets() — wird nicht zu viel lesen, aber man müßte, um zu bestimmen, ob es eine neue Zeile bekam (die in der Eingabe enthalten sein würden) und befaßt sich mit Resynchronisation beim Lesen einer überlangen Linie (nicht sehr schwierig).
  • fread() — wird genau die richtige Länge lesen, und wäre eine gute Wahl, wenn Sie denken, dass Überlängen und Unterlängen werden verschwindend selten vorkommen. Resynchronisation nach einem Fehler ist alles andere als trivial, besonders wenn Sie angrenzende fehlerhafte Zeilen erhalten.
  • getline() — von POSIX 2008. Erteilt genügend Speicher für die Länge der Zeile, die es liest, was eine kleine Verschwendung ist, wenn Sie einfach überlange Zeilen verwerfen.

Da sie nicht geeignet sind, schreiben Sie am Ende Ihre eigenen.

Jetzt getesteter Code. (Das Update wurde in der ersten if wie von Dave diagnostiziert benötigt. Das Problem war, dass ich ursprünglich den inversen Zustand schrieb (if ((c = getc(fp)) != EOF && c != '\n')), dann abgelenkt wurde, nachdem ich die Logik invertiert, was zu einer 'unvollständigen Inversion' der Bedingung.)

Die wichtigsten Teile davon sind die zwei while-Schleifen.

Die erste While-Schleife liest bis zum Ende der Zeile, speichert die Daten und zählt die Zeichen — den normalen Betrieb. Wenn die Linie die richtige Länge hat, wird die Schleife unterbrochen, wenn die neue Zeile gelesen wird. Beachten Sie den <= Zustand; Wenn Sie die Schleife betrachten, wenn linelen == 1, werden Sie sehen, dass <= hier korrekt ist, obwohl < ist üblich. Wenn die Leitung kurz ist, zeigt count dies an.

Die zweite while-Schleife behandelt überlange Zeilen, liest bis zum Ende der Zeile und verwirft die Ergebnisse. Es verwendet x anstelle von c, weil in der Return-Anweisung c benötigt wird.

/* 
@(#)File:   $RCSfile: rdfixlen.c,v $ 
@(#)Version:  $Revision: 1.2 $ 
@(#)Last changed: $Date: 2012/04/01 00:15:43 $ 
@(#)Purpose:  Read fixed-length line 
@(#)Author:   J Leffler 
*/ 

/* Inspired by https://stackoverflow.com/questions/9957006 */ 

#include <stdio.h> 
#include <assert.h> 

extern int read_fixed_length_line(FILE *fp, char *buffer, int linelen); 

/* Read line of fixed length linelen characters followed by newline. */ 
/* Buffer must have room for trailing NUL (newline is not included). */ 
/* Returns length of line that was read (excluding newline), or EOF. */ 
int read_fixed_length_line(FILE *fp, char *buffer, int linelen) 
{ 
    int count = 0; 
    int c; 
    assert(fp != 0 && buffer != 0 && linelen > 0); 
    while (count < linelen) 
    { 
     if ((c = getc(fp)) == EOF || c == '\n') 
      break; 
     buffer[count++] = c; 
    } 
    buffer[count] = '\0'; 
    if (c != EOF && c != '\n') 
    { 
     /* Gobble overlength characters on line */ 
     int x; 
     while ((x = getc(fp)) != EOF && x != '\n') 
      count++; 
    } 
    return((c == EOF) ? EOF : count); 
} 

#ifdef TEST 

#include "posixver.h" 
#include <stdarg.h> 
#include <unistd.h> 
#include <string.h> 

int main(void) 
{ 
    enum { MAXLINELEN = 10 }; 
    int actlen; 
    char line[16]; 
    int lineno = 0; 
    memset(line, sizeof(line), '\0'); 

    while ((actlen = read_fixed_length_line(stdin, line, MAXLINELEN)) != EOF) 
    { 
     lineno++; 
     if (actlen != MAXLINELEN) 
     { 
      if (actlen > MAXLINELEN) 
       printf("%2d:L: length %2d <<%s>>\n", lineno, actlen, line); 
      else 
       printf("%2d:S: length %2d <<%s>>\n", lineno, actlen, line); 
     } 
     else 
      printf("%2d:R: length %2d <<%s>>\n", lineno, actlen, line); 
     assert(line[MAXLINELEN-0] == '\0'); 
     assert(line[MAXLINELEN+1] == '\0'); 
    } 
    return 0; 
} 

#endif /* TEST */ 

Testdaten und Ausgangs

$ cat xxx 

abcdefghij 
a 
Abcdefghij 
ab 
aBcdefghij 
abc 
abCdefghij 
abcd 
abcDefghij 
abcde 
abcdEfghij 
abcdef 
abcdeFghij 
abcdefg 
abcdefGhij 
abcdefgh 
abcdefgHij 
abcdefghi 
abcdefghIj 
abcdefghiJ 
abcdefghiJ1 
AbcdefghiJ 
abcdefghiJ12 
aBcdefghiJ 
abcdefghiJ123 
$ ./rdfixlen < xxx 
1:S: length 0 <<>> 
2:R: length 10 <<abcdefghij>> 
3:S: length 1 <<a>> 
4:R: length 10 <<Abcdefghij>> 
5:S: length 2 <<ab>> 
6:R: length 10 <<aBcdefghij>> 
7:S: length 3 <<abc>> 
8:R: length 10 <<abCdefghij>> 
9:S: length 4 <<abcd>> 
10:R: length 10 <<abcDefghij>> 
11:S: length 5 <<abcde>> 
12:R: length 10 <<abcdEfghij>> 
13:S: length 6 <<abcdef>> 
14:R: length 10 <<abcdeFghij>> 
15:S: length 7 <<abcdefg>> 
16:R: length 10 <<abcdefGhij>> 
17:S: length 8 <<abcdefgh>> 
18:R: length 10 <<abcdefgHij>> 
19:S: length 9 <<abcdefghi>> 
20:R: length 10 <<abcdefghIj>> 
21:R: length 10 <<abcdefghiJ>> 
22:L: length 11 <<abcdefghiJ>> 
23:R: length 10 <<AbcdefghiJ>> 
24:L: length 12 <<abcdefghiJ>> 
25:R: length 10 <<aBcdefghiJ>> 
26:L: length 13 <<abcdefghiJ>> 
$ 
+0

@gbulmer 'fgets' tut ** NICHT ** den Zeilenumbruch verwerfen. – Dave

+0

@Dave - Danke, dass Sie das entdeckt haben. Sehr verwirrend :-(Ich denke, ich dachte, dass gets() (das ist noch schwieriger zu verwenden, weil es keine Pufferlänge dauert). Ich lösche, um Verwirrung zu vermeiden. – gbulmer

+0

@ JonathanLeffler 'c == EOF && c! = ' \ n''? Der zweite Teil versagt nie .. – Dave

1

Try this:

int maxLineLen = 101; // 100 + 1 for the '\n' end of line 
int i = 0; 
int len; 
char myBuffer[101]; 
FILE *myFile; 

myFile = fopen("dataFile.txt", "r"); 

while (fgets(myBuffer, maxLineLen, myFile) != NULL) { 
    i++; 
    len = strlen(myBuffer); 
    if(len != 100) { 
     printf("Error on line %u : expected 100 but got %u\n", i, len); 
    } 
} 

http://www.cplusplus.com/reference/clibrary/cstdio/fgets/

+0

Let me füge hinzu, dass anstelle von 'fgets (myBuffer, maxLineLen, myFile)' es etwas robuster ist, 'fgets (myBuffer, sizeof (myBuffer), myFile)' zu verwenden, wobei 'myBuffer' ein Array und kein Zeiger ist. Der Test könnte auch 'if (len! = Sizeof (myBuffer) -1)', Der Fehler könnte 'printf 'werden (" Fehler in Zeile% u: erwartet% d, aber% u \ n ", sizeof (myBuffer), ich, len); '. Das Ziel ist es, die Beziehung zu der _same_ Anzahl von Zeichen expliziter zu machen und das Auftreten von "magischen Zahlen" zu reduzieren. Für ein kleines Programm ist es ein kleines Problem. – gbulmer

+0

@rkosegi: Danke, aber das funktioniert nicht, denn wenn der Puffer die maximale Größe erreicht und die Zeile diese Größe überschreitet, wird sie wieder durch den Rest der Zeile laufen! In der Ausgabe haben wir eine zusätzliche Zeile, die nicht existiert! – iPadDevloperJr

+0

@gbulmer: das ist richtig, danke, dein Code ist der gleiche wie ich vor dem Posten machen :) – iPadDevloperJr

1

Versuchen Sie fgetc() (oder fgetwc() nach Bedarf).

+0

Ich stehe immer noch als beste Antwort hier. Es sollte @iPadDeveloperJr genügend Informationen geben, um sein Hausaufgabenproblem zu lösen. – DLS

-1

Versuchen Sie folgendes:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define maxLen 100 

int main() { 
int lineNumber = 0; 
char buffer[2048]; 
FILE *myFile = fopen("dataFile.txt", "r"); 

while ((fgets(buffer, 2048, myFile) != NULL)) { 
    buffer[strlen(buffer) - 1] = '\0'; 
    lineNumber++; 
    if (strlen(buffer) != maxLen) { 
     printf("Error in line: %d\n", lineNumber); 
    } 
} 
return 0; 
} 
+0

danke, aber Ihre Lösung funktioniert überhaupt nicht, versuchen Sie es. – iPadDevloperJr

+0

@iPadDevloperJr Ich denke, es funktioniert jetzt. –

+0

Nein, es tut mir leid, dass es wegen der Größe des Puffers nicht so funktionieren kann, alle Zeilen werden fehlerhaft sein! – iPadDevloperJr

0

Bevor Sie tatsächlich die Linie Lese Code schreiben, ist es ein großes Problem, das Sie beheben sollten:

  • Unzureichende Allocation Sie müssen zuweisen 100 für die Linie, 1 für die '\n'und 1 für die '\0'. Dies ergibt 102. Obwohl Sie aus Sicherheitsgründen fgets verwenden, verhindert dies, dass Sie eine zu lange Leitung erkennen.

Danach wird die richtige Verarbeitung ist einfach (kommentiert stark):

char *end = line + sizeof line - 2; 
int ch, nlines=0; 
while(fgets(line, sizeof line, file)){ 
    nlines++; 
    if(strchr(line, '\n')!=end){ // the line is too short or long 
     if(strlen(line) == sizeof line - 1)//line too long 
      do { ch=getc(file)); }while(ch!=EOF && ch!='\n'); //eat until newline 
     printf("Error on line %d", nlines); 
     continue; //skip line 
    } 
    //do whatever. 
} 
+0

thaks aber ich kann deinen Code nicht verstehen! bitte richtig einrücken. – iPadDevloperJr

+0

@iPadDevloperJr Ich habe mich perfekt eingerückt, ich habe eine Klammer verpasst, obwohl (was ich behoben habe). Was verwirrt dich? – Dave

+0

Danke, aber der Code funktioniert immer noch nicht wie erwartet, ich poste einige Daten, die Ihnen beim Testen helfen können. – iPadDevloperJr

0

Die Optionen für dateibasierten Zugriff wurden ausgiebig von anderen bedeckt. Wenn Sie jedoch den Systemaufruf mmap haben, gibt es eine andere Option. mmap bildet die Datei in den virtuellen Speicher ab und liest sie beim Zugriff ein. Es ist sehr praktisch und lässt Sie die Datei als eine einzelne Zeichenfolge behandeln.

Beachten Sie, dass die Datei unten mit MAP_PRIVATE zugeordnet wird, was bedeutet, dass Änderungen an der Zeichenfolge (Datei) nicht in die reale Datei zurückgeschrieben werden. Mit MAP_SHARED werden Änderungen zurück in die Datei geschrieben (nicht das, was hier gewünscht wird).

Hier ist ein Code, um loszulegen. Wir werden die Datei dann zur Karte zu verarbeiten:

char * file = map_file(filename); 

if (file) 
    read_equal_sized_lines(file, size); 

Zuerst haben wir die Datei Karte:

static char * map_file(const char *filename) 
{ 
    struct stat st; 
    char *file = NULL; 

    int fd = open(filename, O_RDONLY); 
    if (fd < 0) 
     perror(filename); 
    else if (fstat(fd, &st) < 0) 
     perror("fstat"); 
    else if ((file = mmap(0, st.st_size, 
          PROT_READ | PROT_WRITE, 
          MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) { 
     perror("mmap"); 
     file = NULL; 
    } 
    return file; 
} 

Jetzt haben wir einen String und kann sie manipulieren einfach:

static size_t get_line(char *s) 
{ 
    char *end = strchr(s, '\n'); 
    if (end) { 
     *end++ = '\0'; /* terminate the line */ 
     return (size_t) (end - s); 
    } 
    return strlen(s); 
} 


static void read_equal_sized_lines(char *file, size_t size) 
{ 
    int line_nr = 1; 

    while (*file != '\0') { 
     size_t len = get_line(file); 
     /* file points to nul-terminated line; do what you want with it */ 
     if (len != size) 
      printf("Line %d: ERROR\n", line_nr); 

     file += len; 
     ++line_nr; 
    } 
}