funcția strstr(), care ignoră majusculele sau minusculele (Programare, C, String)

eOf a intrebat.

Am două șiruri de caractere. Să spunem `

str1="One Two Three";

și

str2="two";

Aș dori să știu dacă există vreo funcție care să verifice dacă al doilea șir de caractere se potrivește cu primul și să îmi returneze un pointer la prima apariție, ceva de genul strstr(), , dar care să nu trateze aceeași literă, majusculă sau minusculă, ca două caractere diferite.

Pentru exemplul meu, funcția ar trebui să găsească o potrivire pentru str2 în primul șir, în ciuda literelor majuscule "T", , din "Two".

Comentarii

  • De ce nu le convertiți pe amândouă în minuscule/majuscule și apoi le comparați? –  > Por SierraOscar.
  • Ce cod ai până acum? Ce limbaj de programare folosești? –  > Por danfuzz.
  • Nu există un stristr() funcție de bibliotecă C, dar ai putea să faci una pentru tine… –  > Por Veleta meteo.
  • M-am gândit la asta,dar nu am voie să modific str1 cu excepția eliminării șirului dat care este găsit.trebuie să păstrez din str1 doar „Unu Trei” așa cum sunt aici și dacă aș face toate literele în minuscule sau majuscule s-ar modifica rezultatul. –  > Por eOf.
  • Faceți copii ale șirurilor. –  > Por Weather Vane.
8 răspunsuri
Nathan Fellman

Din pagina de manual pentru strstr:

STRSTR(3)           Linux Programmer's Manual           STRSTR(3)

NAME
       strstr, strcasestr - locate a substring

SYNOPSIS
       #include 

       char *strstr(const char *haystack, const char *needle);

       #define _GNU_SOURCE

       #include 

       char *<b>strcasestr</b>(const char *haystack, const char *needle);

DESCRIPTION
       The  strstr()  function  finds the first occurrence of the substring needle in
       the string haystack.  The terminating '' characters are not compared.

       <b>The strcasestr() function is like strstr(3), but  ignores  the  case  of  both
       arguments.</b>

RETURN VALUE
       These functions return a pointer to the beginning of the substring, or NULL if
       the substring is not found.


Deci, ceea ce căutați este strcasestr.

Comentarii

  • Pagina mea de manual spune: „Funcția strstr() este conformă cu C89 și C99. Funcția strcasestr() este o extensie nestandardizată.” –  > Por Fred Larson.
  • Nu se află în biblioteca mea Visual C. –  > Por Weather Vane.
  • Mulțumesc, asta este exact ceea ce căutam. Nu m-am gândit să caut în man…Data viitoare voi începe cu asta 😀 -.  > Por eOf.
  • @FredLarson : Nu este o funcție a bibliotecii ISO C, ci face parte din biblioteca GNU C. Funcțiile Microsoft care nu țin cont de majuscule și minuscule folosesc i în loc de case (de ex. stricmp()), dar nu definește o funcție stristr(). –  > Por Clifford.
  • @Clifford: Da, aceasta este o extensie non-standard pentru tine. –  > Por Fred Larson.
Clifford

În timp ce bibliotecile C ale unor compilatoare includ extensii cu versiuni care nu țin cont de majuscule și minuscule ale funcțiilor standard de șir de caractere, cum ar fi GNU’s strcasestr(), , denumirea acestor funcții nu este standardizată nici măcar atunci când sunt incluse.

O modalitate de a depăși lipsa unei implementări standard este, bineînțeles, să vă implementați propria implementare:

char* stristr( const char* str1, const char* str2 )
{
    const char* p1 = str1 ;
    const char* p2 = str2 ;
    const char* r = *p2 == 0 ? str1 : 0 ;

    while( *p1 != 0 && *p2 != 0 )
    {
        if( tolower( (unsigned char)*p1 ) == tolower( (unsigned char)*p2 ) )
        {
            if( r == 0 )
            {
                r = p1 ;
            }

            p2++ ;
        }
        else
        {
            p2 = str2 ;
            if( r != 0 )
            {
                p1 = r + 1 ;
            }

            if( tolower( (unsigned char)*p1 ) == tolower( (unsigned char)*p2 ) )
            {
                r = p1 ;
                p2++ ;
            }
            else
            {
                r = 0 ;
            }
        }

        p1++ ;
    }

    return *p2 == 0 ? (char*)r : 0 ;
}

Codul de test de mai jos produce rezultate:

Two Three
Two Three
NULL
cdefg
CDEFG
CdEfG
NULL
zzzz
NULL

zzzzz
NULL

int main(void) 
{
    char* test = stristr( "One TTwo Three", "two" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "One Two Three", "two" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "One wot Three", "two" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "abcdefg", "cde" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "ABCDEFG", "cde" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "AbCdEfG", "cde" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "1234567", "cde" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "zzzz", "zz" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "zz", "zzzzz" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "", "" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "zzzzz", "" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr( "", "zzzz" ) ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    test = stristr("AAABCDX","AABC") ;
    printf( "%s
", test == 0 ? "NULL" : test  ) ;

    return 0;
}

Comentarii

  • Și eu am fost prins în cazuri limită încercând să mă comport ca strstr("","zz"), strstr("zz", ""), strstr("",""). –  > Por chux – Reinstaurați-o pe Monica.
  • Corectat "","" și "zzzz", "" cazuri de test – ambele returnează str1 conform standardului strstr(). –  > Por Clifford.
  • Nu reușește cu stristr("AAABCDX","AABC") –  > Por Bernardo Ramos.
  • @BernardoRamos : Mulțumesc – a fost fixat. p1 este acum resetat la caracterul care urmează imediat după potrivirea inițială (r + 1) atunci când este detectată o neconcordanță. A fost adăugat un caz de testare. Acum, de asemenea, const-correct. –  > Por Clifford.
  • @Clifford: problema este simplă: există un potențial comportament nedefinit dacă char este semnat și șirul conține caracterele < 0. Soluția este simplă: utilizați tolower((unsigned char)*p1). Este regretabil că char poate fi semnat, ceea ce nu este în concordanță cu comportamentul lui getc() și strcmp() dar această alegere istorică nu poate fi schimbată, însă consecințele sale pentru <ctype.h> funcțiilor pot fi prevenite. În țara mea, este destul de frecvent ca șirurile de caractere să conțină caractere non-ASCII, comportamentul nedefinit ar trebui evitat chiar dacă comportamentul specific localității nu este perfect. –  > Por chqrlie.
Orion Edwards

Dacă sunteți pe Windows, puteți utiliza StrStrI. Acesta funcționează la fel ca și GNU strcasestr sau a altor aplicații implementate manual stristr din alte răspunsuri de aici.

De ex:

const char needle[] = "and";
const char haystack[] = "me and you";

const char* pAnd = StrStrIA(haystack, needle); // explicitly call ascii version as windows defaults to wchar
printf("%s
", pAnd); // Prints "and you";

Comentarii

  • Iată! A durat atât de mult să găsesc asta?! –  > Por colin lamarre.
  • În sfârșit! A funcționat. Mulțumesc. –  > Por Thomas Okonkwo.
  • Din păcate, acest lucru nu funcționează pe mașina linux –  > Por Thomas Okonkwo.
chux – Reinstaurați-o pe Monica

După acceptarea răspunsului

Inspirat de @Clifford și @Weather Vane, m-am gândit să încerc să rostogolesc o soluție care să folosească doar funcții de bibliotecă standard.

char* stristr3(const char* haystack, const char* needle) {
  do {
    const char* h = haystack;
    const char* n = needle;
    while (tolower((unsigned char) *h) == tolower((unsigned char ) *n) && *n) {
      h++;
      n++;
    }
    if (*n == 0) {
      return (char *) haystack;
    }
  } while (*haystack++);
  return 0;
}

Oarecum complicat să se potrivească cu cazurile de colț ale strstr() cu intrări precum "x","", , "","x", , "",""

Comentarii

  • Frumos – am corectat-o pe a mea pentru a produce aceeași ieșire ca a ta. Poate că al tău este ceva mai succint. Distribuțiile nu sunt necesare (tolower() ia un int – trecerea unui char este sigură și normală), iar semnătura lui strstr() în C este char* strstr( char*, const char* ), , astfel încât cast-ul din return nu ar fi necesar dacă îl folosiți. –  > Por Clifford.
  • @Clifford C11 C11 7.4 Manipularea caracterelor spune „argumentul este un an. int, , a cărui valoare trebuie să poată fi reprezentată ca un caracter unsigned char sau trebuie să fie egală cu valoarea macroului EOF. Dacă argumentul are orice altă valoare, comportamentul este nedefinit.” Deci, dacă char este semnată și ch < 0, , atunci trecerea lui la tolower(ch) are ca rezultat ch rămânând un număr negativ și nefiind mapat în unsigned char interval – deci UB. Prin prima turnare, (unsigned char) ch, codul se asigură că o valoare non-negativă este transmisă către tolower(). –  > Por chux – Reintroduceți-o pe Monica.
  • M-am întrebat de necesitatea unui char fără semn, deoarece toate literele sunt sub valoarea ASCII 128. Apoi am încercat cu „Über” și „über” și oricum nu funcționează, pentru că, spre deosebire de alfabetul englezesc cu majuscule/ minuscule, valorile ASCII 129 și 154 nu sunt la 32 de caractere distanță. –  > Por Veleta meteo.
  • @Weather Vane Când char valorile sunt non-ASCII (0-127), intră în joc tot felul de probleme. Când ai spus „Am încercat cu „Über” și „über” și oricum nu merge”, ce este „it”? –  > Por chux – Reinstaurați-o pe Monica.
  • @Weather Vane Pentru a gestiona bine „Über” și „über”, pagina de coduri de caractere și potrivirea setlocale() sunt necesare. BTW, valorile mele Üü diferă și sunt la 32 de diferență. C, sincer, este slab în acest domeniu și bănuiesc că UTF8 va prelua în cele din urmă redarea tuturor caracterelor simple char coduri ca acesta să fie depășite. –  > Por chux – Reintroduceți-o pe Monica.
chqrlie

Iată o versiune puțin mai eficientă, care nu apelează tolower() de două ori pentru fiecare caracter din haystack șir:

#include <ctype.h>

char *stristr4(const char *haystack, const char *needle) {
    int c = tolower((unsigned char)*needle);
    if (c == '')
        return (char *)haystack;
    for (; *haystack; haystack++) {
        if (tolower((unsigned char)*haystack) == c) {
            for (size_t i = 0;;) {
                if (needle[++i] == '')
                    return (char *)haystack;
                if (tolower((unsigned char)haystack[i]) != tolower((unsigned char)needle[i]))
                    break;
            }
        }
    }
    return NULL;
}

Comentarii

  • O() pentru aceasta și pentru aceea sunt ambele O(h_len * n_len) O variantă cu adevărat mai eficientă ar folosi un O(h_len + n_len) abordare. –  > Por chux – Reîntrerupeți-o pe Monica.
  • @chux: Sunt de acord, și am scris puțin mai eficient. Versiuni alternative cu O(h_len + n_len) are un cost de configurare care o face mai lentă în majoritatea cazurilor. O(h_len * n_len) este cel mai rău caz care se întâmplă numai pentru șiruri de argumente patologice. –  > Por chqrlie.
  • Testele mele indică faptul că acesta este măsurabil mai rapid, (și aceeași funcționalitate). –  > Por chux – Reintroduceți-o pe Monica.
  • Ceea ce mi s-a părut cel mai interesant a fost faptul că precompletarea unui fișier int tl[256] cu tolower() valori, înainte de testare, a accelerat lucrurile de 4 ori. –  > Por chux – Reintroduceți-o pe Monica.
  • @chux: câte teste efectuați? preumplerea unui array cu tolower() valori va încărca tabelele ctype în memoria cache, dar acest lucru ar trebui să aibă impact asupra timpilor doar pentru cele câteva cazuri inițiale. tolower() este afectată de setările locale curente în moduri non-triviale. –  > Por chqrlie.
Weather Vane

Implementare a stristr()

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

char *stristr (const char *str, const char *strSearch) {
    char *sors, *subs, *res = NULL;
    if ((sors = strdup (str)) != NULL) {
        if ((subs = strdup (strSearch)) != NULL) {
            res = strstr (strlwr (sors), strlwr (subs));
            if (res != NULL)
                res = str + (res - sors);
            free (subs);
        }
        free (sors);
    }
    return res;
}

int main()
{
    char *str1 = "One Two Three";
    char *str2 = "two";
    char *sptr = stristr(str1, str2);
    if (sptr)
        printf ("Substring is at index %d
", sptr - str1);
    return 0;
}

Comentarii

  • O implementare oarecum „costisitoare”. –  > Por Clifford.
  • Aceasta este o implementare completă, +1 pentru asta. Din moment ce este destinată unui sistem care nu dispune de ea, este logic să fie implementată doar cu funcții C standard și să nu depindă de alte funcții pe care sistemul poate să nu le aibă, cum ar fi strdup() și strlwr(). –  > Por chux – Reinstaurați-o pe Monica.
  • Mulțumesc… aș putea scrie strdup() și strlwr() prea 😉 –  > Por Vremea Vane.
  • Am testat performanțele celor trei implementări date (până acum) cu datele de intrare "One TTwo Three", "two", , pentru 100000 de apeluri. Evident, platformele variază, așa că acestea sunt comparative: A mea: 15 ms, @chux: 16ms, a ta: 172ms. Cu toate acestea, atunci când am prefixat str1 cu 94 de caractere care nu se potrivesc, rezultatele au fost de 250 ms, 125 ms, respectiv 218 ms, deci al tău este cel mai puțin sensibil la variațiile de intrare, chux are cea mai mare performanță în toate cazurile, iar al meu este nașpa la scalabilitate! –  > Por Clifford.
  • @Clifford &; Weather Vane Mă întreb cu privire la rezultatul pe care îl poate obține cineva în cazul în care folosim la upper mai degrabă decât la lower, deoarece bănuiesc că ar putea diferi odată ce se folosesc litere non-AZ. Am experimentat o problemă de portare cu stricmp() în care un sistem de operare a mers pentru toupper și altul pentru tolower. A dat peste cap sortarea atunci când '_' a fost implicat. –  > Por chux – Reintroduceți-o pe Monica.
Abhinav Gupta

Cel mai bun mod de a rezolva acest lucru fără a scrie vreo funcție poate fi prin convertirea mai întâi a ambelor șiruri în minuscule/majuscule folosind „tolower”/”toupper” și apoi să folosiți „strstr” 🙂

Comentarii

  • Dar conversia în majuscule/minuscule se face cel mai bine într-o funcție – deci veți fi scris o funcție. Ce este în neregulă cu scrierea unei funcții, oricum? –  > Por Clifford.
  • da… mă refeream la biblioteca standard… deoarece este deja optimizată. –  > Por Abhinav Gupta.
  • Conversia șirurilor de caractere este o cheltuială inutilă – mai întâi trebuie să le duplicați, astfel încât optimizarea bibliotecii devine irelevantă. Sugestia dvs. este exact soluția sugerată de Weather Vane, iar eu am făcut o analiză a performanței în acest sens și am adăugat rezultatele în comentarii. În orice caz, nu este un simplu „one-liner”, așa că tot ar fi mai bine să vă descurcați să scrieți o funcție ca în răspunsul lui Weather Vane. Biblioteca de șiruri de caractere C (și orice extensii) poate fi într-adevăr optimizată, dar gestionarea șirurilor de caractere C este fundamental ineficientă și strdup() este deosebit de costisitoare. –  > Por Clifford.
  • Poate că ați putea profita de faptul că un caracter minuscul este cu 32 (zecimal) mai mare decât aceeași literă în majuscule. Deci, având în vedere masca de biți corectă, acestea sunt identice. Există 1 bit diferit, care provine din momentul în care ASCII a trecut de la majuscule la minuscule. Teletipurile din anii ’60 sunt toate cu majuscule, de exemplu. –  > Por Alan Corey.
BattleTested_закалённый в бою

Încearcă asta function :

char* stristr(const char* String, const char* Pattern)
{
      char *pptr, *sptr, *start;

      for (start = (char *)String; *start; start++)
      {
            /* find start of pattern in string */
            for ( ; (*start && (toupper(*start) != toupper(*Pattern))); start++)
                  ;
            if (!*start)
                  return 0;

            pptr = (char*)Pattern;
            sptr = (char*)start;

            while (toupper(*sptr) == toupper(*pptr))
            {
                  sptr++;
                  pptr++;
                  /* if end of pattern then pattern was found */
                  if (!*pptr)
                        return (start);
            }
      }
      return 0;
}

Tags:,