Teilstring aus Zeile extrahieren zwischen zwei :



  • Hallo,

    Ich muss unter Linux ein Programms schreiben, welches aus der /etc/group den Gruppennamen und die ID extrahiert und in iener Liste Speiichert. Das ist auch soweit kein Problem, nur ist es hat recht unschön.
    Die Datei hat folgendes Format: gruppenname:passwort:id:mitglied1,mitglied2

    Meine Vorgehensweise:

    while( (c=fgetc(file))!=EOF)
    {
    	j=0;
    	buf[i] = c;
    	i++;
    	if(c==':')
    		pos++;
    	else if(c=='\n')
    	{
    		printf("\n");
    		i=0;
    		pos=0;
    		id = 0;
    	}
    	switch(pos)
    	{
    	case 1: //grupenname Ende
    		for(j=0; j<40; j++)
    			gname[j] = '\0';		
    		for(j=0; j<i-1; j++)
    			gname[j] = buf[j];
    		if(j < 40)
    			gname[j] = '\0';
    		length = j;
    		pos++;
    	break;
    	case 2://füller
    	break;
    	case 3: //vorherige Position speichern
    		posPre = i;
    		pos++;
    	break;
    	case 5: //GruppenID
    		for(j=posPre; j<i-1; j++)
    		{
    			ID[j-posPre+1] = buf[j];
    			id += (buf[j]-48);
    			id *= 10;
    		}
    		id /= 10;
    		//printf("Neues Objekt wird erstellt\n");
    		Group *tmp = (Group*) malloc(sizeof(Group));
    		for(j=0; j<length; j++)
    			tmp->gname[j] = gname[j];
    		tmp->gname[length] = '\0';
    		tmp->gid = id;
    		//printf("neues Objekt wird hinzugefuegt\n");
    		insertElement(&head,tmp);
    		//printf("Neues Element hinzugefuegt\n");		
    		pos++;
    	break;
    	default:
    		printf("");
    	}
    }
    

    Group ist hier mein Listen-Struct.

    Ich lese Quasi jede Zeile Zeichenweise ein, schaue bei welchem : ich grade bin, speichere mir ggf die Position des : und extrahiere dann daraus den Gruppennamen und die ID.

    Gibt es dafür schöneere/Kürzere Lösungen, oder muss ich deisen Umständlcihen weg gehen?

    lg



  • Der Code sieht tatsächlich sehr umständlich aus. Wenn du willst, dass dir jemand hilft, dann solltest du aber ein kompilierbares Beispielprogramm bereitstellen und am besten eine entsprechende Beispieldatei.

    Der von dir gepostete Code ist mitten aus einem Programm und tut mehr als es deinem Problem entspricht. Es ist schwierig das so nachzuvollziehen und zu verbessern.



  • Ansonsten fällt mir spontan fscanf ein.



  • Das einzige was er mehr macht, ist eine Neues structelement zu erstellen und es an eine Funktion zu übergeben. Das hat doch mit der Funktionsweise an sich nichts zu tun? Aber ok, hier mal der Ganze Code:

    #include <stdlib.h>
    #include <stdio.h>
    #include <math.h>
    
    typedef struct Group {
    	char gname[40];
    	int gid;
    	struct Group *next;
    } Group;
    void readFile();
    
    void insertElement(Group **head, Group *elem);
    void printList(Group **head);
    
    static Group *head;
    
    int main()
    {
    	printf("Programm gestartet\n");
    	//*head = NULL;
    	readFile();
    	printList(&head);
    	printf("Programm weird beendet\n");
    	return 0;
    }
    
    void readFile()
    {
    	printf("Datei wird gelesen\n");
    	FILE *file;
    	char buf[256];
    	char gname[40];
    	int groupID;
    	file = fopen("/etc/group","r");
    	if(file == NULL)
    	{
    		printf("fehler beim öffnen der Grouplist");
    		return;
    	}
    	int c = 0;
    	int i = 0;//zeichen in der aktuellen Zeile
    	int j = 0;
    	int pos = 0;
    	int posPre = 0;
    	char ID[8];
    	int id = 0;
    	int length = 0;
    	printf("Daten werden gelesen\n");
    
    	while( (c=fgetc(file))!=EOF)
    	{
    		j=0;
    		buf[i] = c;
    		i++;
    		//printf("Zeichen: %c, i: %d\n",c,i-1);
    		if(c==':')
    			pos++;
    		else if(c=='\n')
    		{
    			printf("\n");
    			i=0;
    			pos=0;
    			id = 0;
    		}
    		switch(pos)
    		{
    		case 1: //grupenname Ende
    			for(j=0; j<40; j++)
    				gname[j] = '\0';		
    			for(j=0; j<i-1; j++)
    			{
    				gname[j] = buf[j];
    				//putchar(gname[j]);
    			}
    			if(j < 40)
    				gname[j] = '\0';
    			length = j;
    			/*for(j=0; j < i-1; j++)
    				printf("%c",gname[j]);
    			printf("_");*/
    			pos++;
    		break;
    		case 2://füller
    		break;
    		case 3: //vorherige Position speichern
    			posPre = i;
    			pos++;
    		break;
    		case 5: //GruppenID
    			//printf(" posPre: %d, i: %d", posPre,i);
    			for(j=posPre; j<i-1; j++)
    			{
    				ID[j-posPre+1] = buf[j];
    				id += (buf[j]-48);
    				id *= 10;
    			}
    			id /= 10;
    			//printf("%d",id);
    			printf("Neues Objekt wird erstellt\n");
    			Group *tmp = (Group*) malloc(sizeof(Group));
    			for(j=0; j<length; j++)
    				tmp->gname[j] = gname[j];
    			tmp->gname[length] = '\0';
    
    			tmp->gid = id;
    			printf("neues Objekt wird hinzugefögt\n");
    			insertElement(&head,tmp);
    			printf("Neues Element hinzugefügt\n");
    			//printf("\"");			
    			pos++;
    		break;
    		default:
    			printf("");
    		}
    	}
    	fclose(file);
    }
    
    void insertElement(Group **head, Group *elem)
    {
    	if(elem == NULL)
    	{
    		printf("Fehlerhafter PArameter\n");
    		return;
    	}
    	if((*head) == NULL)//erstes Element einfügen
    	{
    		printf("Liste ist Leer, neues Elmente wird eingefügt\n");
    		*head = elem;
    		printf("Element\n");
    		(*head)->next = NULL;
    		printf("Element wird eingefügt\n");
    	}
    	else
    	{
    		if((*head)->next == NULL)
    		{
    			(*head)->next = elem;
    			printf("Liste nicht leer, element wird eingefügt\n");
    		}
    		else
    		{
    			if(((*head)->next)->gid > (*elem).gid)//Element wird zwischengeschoben
    			{
    				Group *tmp = (*head)->next;
    				(*head)->next = elem;
    				elem->next = tmp;
    			}
    			else
    			{
    				insertElement(&((*head)->next), elem);
    				printf("Liste nicht leer, element wird weitergereicht\n");
    			}
    		}
    	}
    }
    
    void printList(Group **head)
    {
    	if(head != NULL)
    	{
    		printf("%s:%d\n",(*head)->gname, (*head)->gid);
    		if((*head)->next != NULL)
    		{
    			printList(&(*head)->next);
    		}
    	}
    }
    

    Beispieldatei (für alle die nicht unter Linux arbeiten): (ersten 20 Zelen meiner Group-Datei)
    root❌0:
    daemon❌1:
    bin❌2:
    sys❌3:
    adm❌4:
    tty❌5:
    disk❌6:
    lp❌7:
    mail❌8:
    news❌9:
    uucp❌10:
    man❌12:
    proxy❌13:
    kmem❌15:
    dialout❌20:
    fax❌21:
    voice❌22:
    cdrom❌24:
    floppy❌25:
    tape❌26:

    (einfach in Zeile 34 den Richtigen Pfad angeben)

    lg



  • Was du bei case 1 machst, sieht schon sehr umständlich aus.

    Du darfst auch Funktionen wie strchr, strncpy, strncat, strtol und weiter benutzen.

    Mit fscanf wird das Einlesen ein Einzeiler.



  • Nicht jeder nutzt Linux. Nach deiner Beispieldatei kann es also sein, dass es mitglied1 und mitglied2 gar nicht gibt? Sind mitglied1 und mitglied2 eigentlich Zahlen oder auch Text?

    Die gepostete Datei könntest du z.B. so auslesen:

    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
    	char name[256];
    	char password[256];
    	int id;
    
    	FILE *fp = fopen("test.txt", "r");
    
    	if (!fp)
    		return 1;
    
    	;
    
    	while (fscanf(fp, "%[^:]:%[^:]:%d:\n", name, password, &id) == 3)
    	{
    		printf("Name: '%s'\nPassword: '%s'\nID: '%d'\n\n", name, password, id);
    	}
    
    	fclose(fp);
    }
    


  • jhkhjkhjk schrieb:

    while (fscanf(fp, "%[^:]:%[^:]:%d:\n", name, password, &id) == 3)
    

    Das mit dem '\n' wird so nicht funktionieren, da '\n' an der Stelle auch "nur" ein Whitespace ist wie ein Leerzeichen.

    fscanf(fp, "%39[^:\n]:%*[^:\n]:%d%*[^\n]", name, &id)
    

    Die '\n' in den ersten Formatspecifieren sind nur zur Sicherheit, falls kein : vorhanden ist.



  • DirkB schrieb:

    jhkhjkhjk schrieb:

    while (fscanf(fp, "%[^:]:%[^:]:%d:\n", name, password, &id) == 3)
    

    Das mit dem '\n' wird so nicht funktionieren, da '\n' an der Stelle auch "nur" ein Whitespace ist wie ein Leerzeichen.

    fscanf(fp, "%39[^:\n]:%*[^:\n]:%d%*[^\n]", name, &id)
    

    Die '\n' in den ersten Formatspecifieren sind nur zur Sicherheit, falls kein : vorhanden ist.

    Unter Windows klappt es perfekt. Allerdings gibt es natürlich zwischen Linux und Windows ohnehin auch einen Unterschied zwischen \n und \r\n.

    Aufjedenfall sollte der TE verstanden haben, dass (f)scanf sehr mächtig ist und er es gut für seinen Fall nutzen kann.



  • Hey, danke das mit scanf Funktioniert (mehr oder Weniger) einwandfrei

    Benutzen werde ich nun :

    while((fscanf(file, "\n%[^:]:%*[^:]:%d:%*[^\n]",gname,&id))!=EOF)
    

    Das "\n" am Anfang brauche ich, da er es ansonsten mit in gname speichert.
    Dass gibt mir den Gruppennamen und die ID richtig zurück. Allerdings scheint die Schleife nach der Letzten Zeile Weiterhin auf die Datei zugreifen zu wollen, ich bekomme nämlich den Fehler: Speicherzugriffsfehler (Speicherabzug geschrieben)
    Ich habe schon alle möglichen Formate durchprobiert, aber der Fehler bleibt. Woran kann da liegen?

    Kurze Verständnisfrage: "[^:] liest alle Zeichen bis zum nächsten : ein und übergibt sie; das * vor der Klammer bedeutet dann, dass das gelesene NICHT in einer Variable gespeichert wird?

    @DirkB: Laut konvention hat der Gruppenname keine Leerzeichen, deshalb benötige ich die Überprüfung mittels ""%39[^:\n]" nicht.



  • Vergesst den Fehler "Speicherzugriffsfehler (Speicherabzug geschrieben)", war mein eigener.

    Danke für die schnelle und ausführliche Hilfe, habt mir wirklcihs ehr geholfen! ICh denke, cih werde fscanf nun öfter mal benutzen, war ein Guter Tipp!

    Für alle die es interessiert, hier ncoh mein Kompletter Code (Benutzergruppen auslesen und nach ID sortiert ein einer Lsite speichern)

    #include <stdlib.h>
    #include <stdio.h>
    
    typedef struct Group {
    	char gname[40];
    	int gid;
    	struct Group *next;
    } Group;
    void readFile();
    
    void insertElement(Group **head, Group *elem);
    void printList(Group **head);
    void deleteList(Group **head,Group **prev);
    
    static Group *head;
    
    int main()
    {
    	printf("Programm gestartet\n");
    	//*head = NULL;
    	readFile();
    	printList(&head);
    	deleteList(&head, NULL);
    	printf("Programm wird beendet\n");
    	return 0;
    }
    
    void readFile()
    {
    	printf("Datei wird gelesen\n");
    	FILE *file;
    	char gname[40];
    	char password[40];
    	char member[40];
    	int id = 0;
    	file = fopen("/etc/group","r");
    	if(file == NULL)
    	{
    		printf("fehler beim öffnen der Grouplist");
    		return;
    	}
    
    	printf("Daten werden gelesen\n");
    
    	while((fscanf(file, "\n%[^:]:%*[^:]:%d:%*[^\n]",gname,&id))!=EOF)
    	{
    		printf("Neues Objekt wird erstellt\n");
    		Group *tmp = (Group*) malloc(sizeof(Group));
    		int j;
    		for(j=0; gname[j]!='\0'; j++)
    			tmp->gname[j] = gname[j];
    		tmp->gname[j] = '\0';
    		tmp->gid = id;
    		printf("neues Objekt wird hinzugefögt\n");
    		insertElement(&head,tmp);
    		printf("Neues Element hinzugefügt\n");
    	}
    	fclose(file);
    }
    
    void insertElement(Group **head, Group *elem)
    {
    	if(elem == NULL)
    	{
    		printf("Fehlerhafter PArameter\n");
    		return;
    	}
    	if((*head) == NULL)//erstes Element einfügen
    	{
    		*head = elem;
    		(*head)->next = NULL;
    	}
    	else
    	{
    		if((*head)->next == NULL)//aktuell letzets Element der Liste
    			(*head)->next = elem;
    		else
    		{
    			if(((*head)->next)->gid > (*elem).gid)//Element wird zwischengeschoben
    			{
    				Group *tmp = (*head)->next;
    				(*head)->next = elem;
    				elem->next = tmp;
    			}
    			else
    				insertElement(&((*head)->next), elem);
    		}
    	}
    }
    
    void printList(Group **head)
    {
    	if(head != NULL)
    	{
    		printf("%s:%d\n",(*head)->gname, (*head)->gid);
    		if((*head)->next != NULL)
    			printList(&(*head)->next);
    	}
    }
    
    void deleteList(Group **head, Group **prev)
    {
    	if(head != NULL)
    	{
    		if(prev != NULL)
    			free(*prev);
    		if((*head)->next != NULL)
    			deleteList(&(*head)->next, &(*head));
    		else
    			free(*head);
    	}
    }
    


  • Duck93 schrieb:

    Das "\n" am Anfang brauche ich, da er es ansonsten mit in gname speichert.

    Ein Leerzeichen macht dasselbe. Kannst du auch noch ans Ende hängen.

    Duck93 schrieb:

    Dass gibt mir den Gruppennamen und die ID richtig zurück. Allerdings scheint die Schleife nach der Letzten Zeile Weiterhin auf die Datei zugreifen zu wollen, ich bekomme nämlich den Fehler: Speicherzugriffsfehler (Speicherabzug geschrieben)
    Ich habe schon alle möglichen Formate durchprobiert, aber der Fehler bleibt. Woran kann da liegen?

    Das mag daran liegen, das EOF die falsche Abbruchbedingung ist; bzw nicht das alleinige Erfolgsmerkmal.
    Nach der letzten Zeile steht von dieser noch das '\n' im Eingabestrom. Dies wird dann eingelesen, die Gruppe allerdings nicht mehr. fscanf gibt 0 zurück.
    Danach wird das Problem auttauchen.
    OK, hat sich erledigt.

    Duck93 schrieb:

    Kurze Verständnisfrage: "[^:] liest alle Zeichen bis zum nächsten : ein und übergibt sie; das * vor der Klammer bedeutet dann, dass das gelesene NICHT in einer Variable gespeichert wird?

    Ja. Der : wird nicht mehr eingelesen.

    Duck93 schrieb:

    @DirkB: Laut konvention hat der Gruppenname keine Leerzeichen, deshalb benötige ich die Überprüfung mittels ""%39[^:\n]" nicht.

    Das hat mit Leerzeichen nichts zu tun, sondern mit der Größe von deinem Array (char gname[40];)

    @jhkhjkhjk
    Das hat mit Windows oder Linux nichts zu tun.
    Bei meiner Version werden vorhanden Mitglieder auch überlesen.
    Und im Textmodus (kein "b" im open-Mode) wird das CR und/oder LF schon richtig übersetzt.



  • Für Zeile 49-52 nimmst du doch bitte strcpy .
    Oder du schreibst gleich tmp->gname bei fscanf hin.

    An readFile kannst du auch head als Parameter übergeben.
    Dann brauchst du die globale Variable nicht mehr.

    main kann nicht feststellen, ob die Datei erfolgreich gelesen wurde.

    printList braucht keinen Doppelzeiger, oder du musst die Bedingung vom if nochmal überprüfen.

    Weiter habe ich erstmal nicht durchgeschaut.


Log in to reply