ubuntuusers.de

Vergleich zweier char-Arrays

Status: Gelöst | Ubuntu-Version: Ubuntu 20.04 (Focal Fossa)
Antworten |

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11254

Wohnort: München

Data2006 schrieb:

Das Beispiel von Dir (seahawk1986) verstehe ich nicht. Das ist mir zu komplex.

Woran genau scheitert es?

Das mit umfangreichem Code ist ein grundlegendes Problem von C - einerseits hat man sehr viel Kontrolle darüber, was passiert, aber im Gegenzug muss man sich um lästigen Kleinkram kümmern. Wenn man das nicht will, tut man sich mit anderen Sprachen deutlich leichter - z.B. wäre das Gleiche das als Python3-Skript dank der umfangreichen Abstraktionen nur ein paar Zeilen lang:

1
2
3
4
#!/usr/bin/env python3
text = input("Bitte einen Text eingeben: ")
suche = input("Bitte einen Suchbegriff eingeben: ")
print(f"Der Suchbegriff kommt im Text {text.count(suche)} mal vor.")

(s)size_t kenne ich nicht.

size_t ist ein vorzeichenloser (unsigned) Datentyp, der Werte annehmen kann, die so groß sein dürfen wie die maximale Array-Länge, die man auf dem System nutzen kann. Der Maximalwert eines int ist dafür auf den meisten heutigen Architekturen zu klein. ssize_t bietet auch eine entsprechenden Maximalwert, ist aber signed - d.h. der Datentyp kann auch negative Werte repräsentieren - was man für Funktionen wie getline braucht, die im Fehlerfall ein -1 zurückgeben.

Ich versuche es noch bischen, wenn es nicht klappen sollte, höre ich damit auf und gucke weiter YouTube. Kennt jemand die C-Reihe von Franneck? Was haltet Ihr davon?

Ich kann damit nichts anfangen - aber das hängt nicht nur am Kanal, für mich ist das generell nicht wirklich praktikabel eine Sprache anhand von Tutorialvideos zu erlernen - fürs Verständnis von Konzepten und Algorithmen oder auch mal Leuten beim Coden von komplexeren Dingen über die Schultern schauen zu können, kann ein gut gemachtes Video aber durchaus praktisch sein.

Ich schaue i.d.R., dass ich mich mit der grundlegenden Syntax vertraut mache, dann geht es einmal über die merkwürdigen Eigenheiten der Sprache (z.B. im Stil von WTFJS), damit man die häufigsten Stolpersteine kennen lernt und dann probiere ich mit einem Buch oder der Dokumentation zur Sprache und hilfreichen Erklärungen aus dem Netz (Stack Overflow, Blogposts usw.) bewaffnet so lange an einem selbst gewählten Problem herum, bis ich ein Gefühl für die Grundlagen habe. Danach kann man sich dann darum kümmern Dinge eleganter zu lösen.

Humbi

Anmeldungsdatum:
14. Juni 2014

Beiträge: 74

Data2006 schrieb:

Ich wollte eigentlich das Array text Zeichen für Zeichen durchgehen ob das gerade erwählte Zeichen = dem ersten Zeichen von wort übereinstimmt. Und dann gucken ob die restlichen Zeichen mit text auch stimmen.

Das ist genau die Funktion meiner ft_strstr 😀 .

Wenn du hauptsächlich am lernen interessiert bist solltest du vlt 10min mehr Zeit in die Phase 0 des Projektes stecken. Phase 0 ist das Gedanken machen um das Projekt. Das wäre für eine Text(needle) in Text2(haystack) Suche, für das erste Vorkommen von Text, folgender Ansatz:

  • haystack ist länger als needle.

  • Gehe haystack bis zum Ende(\0) durch. [Optimiert auf "Ende - needle length" weil needle dann nicht mehr rein passt]

  • Wenn der Char übereinstimmt gehe needle bis zum Ende durch und gehe auch auf haystack weiter und vergleiche die Chars.

  • Du brauchst für Punkt 3 einen relativen Zähler, damit du bei einem Missmatch an der vorherigen Position weiter machen kannst.

Edgecases und Datenreinheit:

  • Was bekommst du für Daten. In deinem Fall ist eine Vorbehandlung erforderlich die die '\n' durch [space] ersetzt, weil dein needle die '\n' nicht beinhaltet.

  • Was passiert wenn needle länger ist als haystack, also nicht rein passt.

  • Was passiert wenn haystack und/oder needle leer sind.

size_t ist ein 16bit Unsigned Integer. Sollte also für alle Texte reichen. Hab ich hier verwendet, weil Indizes von Char Pointer ja idR nicht negativ sind. Inkonsequenter Weise hab ich das in ft_strlen nicht, sehe ich gerade.

Den return Value könnte man ändern, damit er 1 oder true zurück gibt anstatt einen Pointer.

Und ich glaube du lernst mehr wenn du anfangs vlt ein paar existierende Funktionen nachbaust. Dafür hast du eine Manpage die dir die Aufgabe vorgibt und im Zweifel eine fertige Lösung im Sourcecode, die man auch erstmal verstehen muss. Die Fragen zum recherchieren kommen dabei automatisch. Nach Video oder Buch zu programmieren ist IMHO teilweise lückenhaft und schwierig nachzuvollziehen wenn es nicht gut gemacht ist.

PS: Wer sich über die Formatierung der Funktionen wundert. Das liegt an Norminette 😉 .

Data2006

(Themenstarter)

Anmeldungsdatum:
7. November 2021

Beiträge: 899

So, vielen Dank für die Antworten.

Einzelne Anweisungen verstehe ich ja, aber nicht wie das Programm insgesamt läuft.

Habe noch mal was geändert:

#include <stdio.h>
#include <string.h>
int main()
{
    char text[100];
    char wort[15];
    int i, x, gefunden;
    int anzahl_suchwort=0;

    printf("Geben Sie einen Text ein: ");
    fgets(text,100,stdin);
    printf("Geben Sie nun das zu suchende Wort ein: ");
    scanf("%s", wort);
    
    /*äußere Schleife für die Indexe von 'text'*/
    for(i=0;i<strlen(text)-1;i++)
    {
        gefunden=0;

        /*Wenn das Zeichen am 'text' an der Indexposition i == Zeichen des 'wort' an
          Indexposition 0*/
        if(text[i]==wort[0] && (strlen(text)-1-i) >= strlen(wort))
        {
            gefunden=1;

            for(x=1;x<strlen(wort);x++)
            {
                /*Wenn die Zeichen an 'text[i+x]' ungleich 'wort[x]'
                  ist, gefunden wieder auf 0*/
                if(text[i+x]!=wort[x])
                {
                    gefunden=0;
                    break;
                }
            }
            if(gefunden==1)
                anzahl_suchwort++;
        }
    }

    if(gefunden==1)
            printf("gefunden.\n");
        else
            printf("nicht gefunden.\n");

    return 0;
}

Aber läuft auch nicht. Ist denn wenigstens meine

if(text[i]==wort[0] && (strlen(text)-1-i) >= strlen(wort))

richtig? Oder muß ich den Teil nach && anders klammern?

Ich glaube wir brechen hier besser ab. Vielleicht noch mal die letzten beiden ? noch beantworten.

Lieben Dank, Heiko

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11254

Wohnort: München

Data2006 schrieb:

Einzelne Anweisungen verstehe ich ja, aber nicht wie das Programm insgesamt läuft.

Habe noch mal was geändert:

#include <stdio.h>
#include <string.h>
int main()
{
    char text[100];
    char wort[15];
    int i, x, gefunden;
    int anzahl_suchwort=0;

    printf("Geben Sie einen Text ein: ");
    fgets(text,100,stdin);
    printf("Geben Sie nun das zu suchende Wort ein: ");
    scanf("%s", wort);
    
    /*äußere Schleife für die Indexe von 'text'*/
    for(i=0;i<strlen(text)-1;i++)
    {
        gefunden=0;

        /*Wenn das Zeichen am 'text' an der Indexposition i == Zeichen des 'wort' an
          Indexposition 0*/
        if(text[i]==wort[0] && (strlen(text)-1-i) >= strlen(wort))
        {
            gefunden=1;

            for(x=1;x<strlen(wort);x++)
            {
                /*Wenn die Zeichen an 'text[i+x]' ungleich 'wort[x]'
                  ist, gefunden wieder auf 0*/
                if(text[i+x]!=wort[x])
                {
                    gefunden=0;
                    break;
                }
            }
            if(gefunden==1)
                anzahl_suchwort++;
        }
    }

    if(gefunden==1)
            printf("gefunden.\n");
        else
            printf("nicht gefunden.\n");

    return 0;
}

Aber läuft auch nicht.

Du prüfst für die Ausgabe am Ende immer noch auf den Wert von gefunden, aber dessen Wert ist ja normalerweise am Ende der äußeren Schleife 0, wenn es nicht gerade im letzten Durchlauf der inneren for-Schleife einen erfolgreichen Vergleich über die gesamte Länge des Suchstrings gab. Wenn musst du auf anzahl_suchwort schauen - also z.B. so:

1
2
    if (anzahl_suchwort >= 1) printf("gefunden.\n");
    else printf("nicht gefunden.\n");

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11254

Wohnort: München

Humbi schrieb:

size_t ist ein 16bit Unsigned Integer

Was das für ein Typ ist, hängt von der Architektur ab - nimm z.B. mal diesen C++ Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <cstddef>
#include <iostream>
#include <limits>

int main() {
std::cout << "size_t max:   " << std::dec << std::numeric_limits<size_t>::max()   << '\n'
          << "size_t min:   " << std::dec << std::numeric_limits<size_t>::min()   << '\n'
          << "ssize_t max:  " << std::dec << std::numeric_limits<ssize_t>::max()  << '\n'
          << "ssize_t min:  " << std::dec << std::numeric_limits<ssize_t>::min()  << '\n'
          << "uint16_t max: " << std::dec << std::numeric_limits<uint16_t>::max() << '\n'
          << "uint16_t min: " << std::dec << std::numeric_limits<uint16_t>::min() << '\n'
          << "uint32_t max: " << std::dec << std::numeric_limits<uint32_t>::max() << '\n'
          << "uint32_t min: " << std::dec << std::numeric_limits<uint32_t>::min() << '\n'
          << "uint64_t max: " << std::dec << std::numeric_limits<uint64_t>::max() << '\n'
          << "uint64_t min: " << std::dec << std::numeric_limits<uint64_t>::min() << '\n'
          << std::endl;
}

Auf einem 64-Bit System ist es ein uint64_t:

$ uname -a
Linux my64bitmachine 5.15.0-47-generic #51-Ubuntu SMP Thu Aug 11 07:51:15 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
$ ./show_size_t_limits
size_t max:   18446744073709551615
size_t min:   0
ssize_t max:  9223372036854775807
ssize_t min:  -9223372036854775808
uint16_t max: 65535
uint16_t min: 0
uint32_t max: 4294967295
uint32_t min: 0
uint64_t max: 18446744073709551615
uint64_t min: 0 

Und auf einem armv7l 32-Bit System ist es ein uint32_t:

$ uname -a
Linux myarmmachine 5.19.6-1-ARCH #1 SMP PREEMPT Thu Sep 1 20:26:33 MDT 2022 armv7l GNU/Linux
$ ./show_size_t_limits
size_t max:   4294967295
size_t min:   0
ssize_t max:  2147483647
ssize_t min:  -2147483648
uint16_t max: 65535
uint16_t min: 0
uint32_t max: 4294967295
uint32_t min: 0
uint64_t max: 18446744073709551615
uint64_t min: 0 

Data2006

(Themenstarter)

Anmeldungsdatum:
7. November 2021

Beiträge: 899

Ok. Ich setze gefunden auf 1 wenn ein Buchstabe aus text mit dem 1. Buchstaben von Wort übereinstimmt. Und dann in der inneren for-Schleife die restlichen Buchstaben. Findet die Schleife einen Buchstaben, der nicht paßt, setze ich gefunden wieder auf 0 und verlasse die Schleife wieder mit break.

Ich drucke mir mein Programm mal aus, da läßt es sich besser lesen.

Stimmt denn in meinem Programm die if-Bedingung (s. o.)? Oder ist die auch schon falsch?

Lieben Dank, Heiko

Data2006

(Themenstarter)

Anmeldungsdatum:
7. November 2021

Beiträge: 899

PS: Ich hätte gefunden auf bool setzen sollen. Und dann mit true oder false arbeiten.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17608

Wohnort: Berlin

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <string.h>

int main()
{
	const int FALSE = 0;
	const int TRUE = !FALSE;

	printf ("Geben Sie einen Text ein: ");
	// Deklaration so spät wie möglich vor Initialisierung/Verwendung
	char text[500];
	fgets (text, 500, stdin);
	printf ("\nAnzhahl Zeichen: %i\n", (int) strlen (text));

	int len = strlen (text);
	char wort[] = "Ubuntu";
	int wlen = strlen (wort);
	// "foo" kann nicht "foobar" enthalten
	if (wlen > len)
		return FALSE;

	int i = 0;
	// Xxx braucht man in AaaBbbCcc ab dem C nicht weiter zu suchen - es kommen zu wenige Zeichen
	while (i < (len - wlen))
	{
		if (text[i++] == wort[0])
		{
			int k = 1;
			while (k < (wlen) && text[i++] == wort[k++])
			{
				// intentionally empty
				// dbg:
				// printf ("\tmatch until %c\tat pos %i\n", text[i-1], i-1);
			}
			if (k == wlen)
			{
				printf ("Hit at pos: %i\n", i-wlen);
				return TRUE;
			}
			/* Debug, zum Feststellen ob Teilmatches gefunden werden und bis wohin
			else
			{
				printf ("\tmismatch at %c\tat pos %i\twort[%i]\n", text[i-1], i-1, k-1);
			}
			*/
			// Wenn man "Ubuntu" in "UUbuntu" sucht, und das 2 U wurde gefunden und als
			// Mismatch zu "b" motivierte es uns, aus der Schleife zu springen,
			// müssen wir einen Schritt zurück,
			// um es als potentiellen Start für ein neues Match zu berücksichtigen,
			// was bei UxUbuntu nicht nötig wäre, da das Suchwort nicht mit 'x' beginnt.
			--i;
		}
	}
	return FALSE;
}

Das ist Low-Level, und bestimmt gibt es eine kanonische C-Funktion für Strings, die eleganter und kürzer und besser getestet ist. Womöglich im Umfeld von regulären Ausdrücken/Strings (String.match (muster, text)) oder sowas.

Das char text[500]; ist ein dirty hack, aber für eine Übungsaufgabe passabel.

Disclaimer: Ich habe mir C autodidaktisch beigebracht aber nicht sehr lange und tief damit gearbeitet, seit Mitte der 90er (Java) nur noch sporadisch und bin nicht auf dem neuesten Stand. (Gibt es inzw. Bools; mir war so).

Data2006

(Themenstarter)

Anmeldungsdatum:
7. November 2021

Beiträge: 899

Wieso Dirty Hack! Sollte ich Speicher mit malloc reservieren?

Gute Nacht, Heiko

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17608

Wohnort: Berlin

Weil der eingegebene Text vielleicht länger als 500 Zeichen ist. Vielleicht ist das aber auch kein Problem.

Data2006

(Themenstarter)

Anmeldungsdatum:
7. November 2021

Beiträge: 899

Moin zusammen,

leider habe ich (noch) keine Antwort zu meiner if-Anweisung bekommen. Habe jetzt noch was probiert und die äußere i-Schleife was abgekürzt:

#include <stdio.h>
#include <string.h>
int main()
{
    char text[100];
    char wort[15];
    int i, x, gefunden;
    int anzahl_suchwort=0;

    printf("Geben Sie einen Text ein: ");
    fgets(text,100,stdin);
    printf("Geben Sie nun das zu suchende Wort ein: ");
    scanf("%s", wort);
    
    for(i=0;i<strlen(text)-1-strlen(wort);i++)
    {
        printf("%d = %c\n",i,text[i]);
    }
    return 0;
}

Wenn ich jetzt aber 'Dies ist' eingebe, werden die Buchstaben schon nicht richtig ausgegeben:

heiko@Worf:~/test33$ ./main
Geben Sie einen Text ein: Dies ist     
Geben Sie nun das zu suchende Wort ein: ist
0 = D
1 = i
2 = e
3 = s
4 =  
heiko@Worf:~/test33$

Ist die for-Schleife falsch? Ich glaube daran liegt es das meine ganzen Versuche scheitern.

Lieben Dank und schönen Sonntag, Heiko

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11254

Wohnort: München

Die Frage ist doch einfach, was du damit bezweckst - strlen(text)-1-strlen(wort) bei jedem Schleifendurchlauf zu berechnen bringt ja wenig, weil die Länge der Strings in den char-Arrays sich nicht ändert - also einmalig vor der Schleife berechnen und sich überlegen, ob da der gewünschte Wert rauspurzelt - IMHO tut er das nicht, weil du mindestens bis zum ersten Buchstaben des Suchbegriffs im text kommen musst (für dein Beispiel die Position mit dem Index 5 im Array), damit der Vergleich des ersten Buchstabens des Suchbegriff Erfolg haben kann - und mit dem -1 läuft die Schleife nicht weit genug über den Array mit dem Text.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11254

Wohnort: München

Data2006 schrieb:

Ich drucke mir mein Programm mal aus, da läßt es sich besser lesen.

Eventuell lohnt sich da auch ein hochauflösender, größerer Bildschirm - 50-60 Zeilen sollte man normalerweise schon unterbringen (und mindestens zwei DIN-A4 Seiten nebeneinander haben zu können ist immer gut) - wenn der Code zu unübersichtlich wird, kann es helfen Abschnitte davon in eigene Funktionen auszulagern, das verbessert oft auch die Testbarkeit.

Data2006

(Themenstarter)

Anmeldungsdatum:
7. November 2021

Beiträge: 899

Nun ja, ich bräuchte doch im text die letzte Möglichkeit wo ich das wort noch finden kann. Gebe ich z. B. "Dies ist ein Te" ein, dann brauche ich gar nicht erst nach dem wort "Test" zu suchen. Nur bei "Dies ist ein Test" kann ich nach dem wort "Test" suchen.

In meiner letzten for-Schleife frage ich mich warum er nicht ganz text, abzüglich der Länge von wort, ausgibt. Halt, ich schneide ja was ab und gehe nicht das ganze Array text durch.

Ich prüfe ja in jedem Element von text, ob der Buchstabe wort[0] gleich ist. Erst dann überprüfe ich die restlichen Buchstaben von wort[x] mit den weiteren Buchstaben von text[i+x] - das wäre dann die innere Schleife.

Wenn der 1. Buchstabe übreinstimmt, setze ich gefunden = 1, aber wenn dann die restlichen Buchstaben nicht mehr übereinstimmen, gefunden zurück auf 0. Ich kann ja in der inneren for-Schleife nicht alle Buchstaben überprüfen und dann 1x gefunden = 1 setzen. Ich müßte höchstens eine extra Funktion schreiben wo ich alle Buchstaben überprüfe.

Lieben Dank, Heiko

PS: Ich habe irgendwo im Kopf eine falsche Logik oder???

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13189

Nur mal so als Hinweis: es gibt reichlich Algorithmen zur Textsuche, wo man sich bedienen kann. ☺