Kapitel 7
Tekststrenge

Kurt Nørmark ©
Institut for Datalogi, Aalborg Universitet


Sammendrag
Forrige lektion Næste lektion
Stikord Referencer Indhold
I denne lektion studerer vi forskellige aspekter af tekststrenge.


Strenge og tekststrenge

Strenge og tekststrenge
Slide Indhold Stikord
Referencer Lærebog 

Begrebet streng: En streng er en sekvens af data af samme type
Begrebet tekststreng: En tekststreng er en streng af datatypen tegn

  • Almen notation for tekststrenge:

    • "En tekststreng"

  • Den tomme streng

    • Strengen der ikke indeholder data

    • Strengen af længde 0 - den kortest mulige streng

    • Den tomme tekststreng noteres naturligt som ""

Tekststrenge spiller en vigtig rolle i de fleste programmer vi skriver


Tekststrenge i C

Tekststrenge og arrays
Slide Indhold Stikord
Referencer Lærebog 

En tekststreng i C er et nulafsluttet array med elementtypen char.

Figur. En illustration af en nulafsluttet tekststreng i C

  • Det afsluttende nultegn kaldes en sentinel

    • Oversættes direkte som en 'vagt'

    • Eksplicit markering af afslutningen af tekststrengen

    • Som et alternativt kunne vi holde styr på den aktuelle længde af tekststrengen

Figur. En tekststreng som er placeret 'i midten' af et array of char

En tekststreng i C skal ikke nødvendigvis udfylde hele det omkringliggende array

Initialisering af tekststrenge
Slide Indhold Stikord
Referencer Lærebog 

En tekststreng kan initialiseres på flere forskellige måder

Program: Forskellige initialiseringer af tekststrenge.
  char str_1[] = {'A', 'a', 'l', 'b', 'o', 'r', 'g', '\0'};

  char str_2[] = "Aalborg";

  char *str_3 = "Aalborg";

  char str_4[8];
  str_4[0] = 'A';  str_4[1] = 'a';  str_4[2] = 'l';  
  str_4[3] = 'b';  str_4[4] = 'o';  str_4[5] = 'r';  
  str_4[6] = 'g';  str_4[7] = '\0';   

Program: Hele programmet inklusive udskrivninger af de fire tekststrenge.
#include <stdio.h>

int main(void) {

  char str_1[] = {'A', 'a', 'l', 'b', 'o', 'r', 'g', '\0'};

  char str_2[] = "Aalborg";

  char *str_3 = "Aalborg";

  char str_4[8];
  str_4[0] = 'A';  str_4[1] = 'a';  str_4[2] = 'l';  
  str_4[3] = 'b';  str_4[4] = 'o';  str_4[5] = 'r';  
  str_4[6] = 'g';  str_4[7] = '\0';     

  printf("%s\n%s\n%s\n%s\n", str_1, str_2, str_3, str_4);
  
  return 0;
}

Program: Tilsvarende forsøg på assignments til de fire tekststrenge.
#include <stdio.h>

int main(void) {

  char str_1[8], str_2[8], *str_3, str_4[8];

  /* Ulovlig brug af initializer */
  str_1 = {'A', 'a', 'l', 'b', 'o', 'r', 'g', '\0'};

  /* Ulovlig assignment til array navn. Implicit kopiering finder ikke sted */
  str_2 = "Aalborg";

  /* Alternativ: kopier tegnene selv */
  strcpy(str_2, "Aalborg");

  /* Pointer assignment - OK */
  str_3 = "Aalborg";

  /* Tegnvis assignment - OK */
  str_4[0] = 'A';  str_4[1] = 'a';  str_4[2] = 'l';  
  str_4[3] = 'b';  str_4[4] = 'o';  str_4[5] = 'r';  
  str_4[6] = 'g';  str_4[7] = '\0';     

  printf("%s\n%s\n%s\n%s\n", str_1, str_2, str_3, str_4);
  
  return 0;
}

Ved initialisering via tekstkonstanter tilføjes nultegnet automatisk af compileren

Tekststrenge og pointere
Slide Indhold Stikord
Referencer Lærebog 

En tekststreng opfattes i C som en pointer til det første tegn

Dette følger direkte af den generelle sammenhæng mellem arrays og pointere

Program: Et program der ved brug af pointere kopierer strengen "Aalborg" ind midt i en anden streng.
#include <stdio.h>

int main(void) {
  char str_aal[] = "Aalborg";
  char str[14];

  char *str_1, *str_2;
  int i;

  /* fill str with '-' */
  for(i = 0; i < 14; i++)
    str[i] = '-';

  /* let str_1 and str_2 be running pointers */
  str_1 = str;  str_2 = str_aal;

  /* copy str_all into the middle of str */
  str_1 += 2;
  for( ; *str_2 != '\0'; str_1++, str_2++)
    *str_1 = *str_2;

  /* terminate str */
  *str_1 = '\0';

  printf("%s\n", str);  
  
  return 0;
}

Program: Output fra programmet.
--Aalborg

Ændringer af tekststrenge
Slide Indhold Stikord
Referencer Lærebog 

Tekststrenge, til hvilke der er allokeret plads i et array, kan ændres (muteres)

Tekststrenge, der er angivet som en strengkonstant refereret af en pointer, kan ikke ændres

Program: Et program der ændrer det andet tegn i Aalborg fra 'a' til 'A'.
  char str_1[] = {'A', 'a', 'l', 'b', 'o', 'r', 'g', '\0'};
  *(str_1 + 1) = 'A';

  char str_2[8] = "Aalborg";
  *(str_2 + 1) = 'A';

  char *str_3 = "Aalborg";
  *(str_3 + 1) = 'A';  /* Fejl. Segmentation fault under kørsel */

  char str_4[8];
  str_4[0] = 'A';  str_4[1] = 'a';  str_4[2] = 'l';  
  str_4[3] = 'b';  str_4[4] = 'o';  str_4[5] = 'r';  
  str_4[6] = 'g';  str_4[7] = '\0';     
  *(str_4 + 1) = 'A';

Program: Hele programmet der ændrer det andet tegn i Aalborg fra 'a' til 'A'.
#include <stdio.h>

int main(void) {

  char str_1[] = {'A', 'a', 'l', 'b', 'o', 'r', 'g', '\0'};
  *(str_1 + 1) = 'A';

  char str_2[8] = "Aalborg";
  *(str_2 + 1) = 'A';

  char *str_3 = "Aalborg";
  *(str_3 + 1) = 'A';  /* Fejl. Segmentation fault under kørsel */

  char str_4[8];
  str_4[0] = 'A';  str_4[1] = 'a';  str_4[2] = 'l';  
  str_4[3] = 'b';  str_4[4] = 'o';  str_4[5] = 'r';  
  str_4[6] = 'g';  str_4[7] = '\0';     
  *(str_4 + 1) = 'A';

  printf("%s\n%s\n%s\n%s\n", str_1, str_2, str_3, str_4);
  
  return 0;
}

Tekststrenge i forhold til tegn
Slide Indhold Stikord
Referencer Lærebog 

Tekststrenge er sammensat af tegn

De to værdier 'a' og "a" meget forskellige

  • 'a'

    • Et enkelt tegn af typen char

    • Reelt heltallet 97

  • "a"

    • Et array med to elementer

    • Nulte element er tegnet 'a' og første element er tegnet '\0'

Den tomme streng og NULL
Slide Indhold Stikord
Referencer Lærebog 

Man skal kunne skelne mellem den tomme tesktstreng og en NULL pointer

NULL og "" er meget forskellige

  • NULL

    • NULL er en pointer værdi

    • NULL værdien bruges for en pointer, der ikke peger på en plads i lageret

    • Reelt heltallet 0

  • Den tomme streng ""

    • Den tomme string "" er en streng værdi

    • "" er et array med ét element, nemlig '\0' tegnet


Leksikografisk ordning

Leksikografisk ordning af strenge
Slide Indhold Stikord
Referencer Lærebog 

Ordningen af tegn inducerer en ordning af tegnstrenge (tekststrenge)

Den normale alfabetiske ordning af tekststrenge spiller en vigtig rolle ved opslag i leksika, telefonbøger, mv.

Vi vil her definere hvad det betyder af strengen s er mindre end strengen t

  • Lad e betegne den tomme streng "" og lad s og t være to tekststrenge

  • s < t hvis og kun hvis der findes tegn c og d samt to kortere strenge u og v

    • s = e
      t = c u                 eller

    • s = c u
      t = d v

      • c < d

      • c = d og u < v

Lighed af strenge (1)
Slide Indhold Stikord
Referencer Lærebog 

Der er to mulige fortolkninger af lighed af to strenge s og t

Figur. En illustration af reference lighed og strukturel lighed mellem char * variable

Lighed af strenge (2)
Slide Indhold Stikord
Referencer 

  • Pointerværdierne indeholdt i de to variable s og t er ens

    • s og t peger på det samme array af tegn

    • Hvis vi ændrer på tegnene i s ændres tegnene i t automatisk

    • Reference lighed

  • s og t udpeger forskellige arrays, med tegn der parvis er ens

    • Hvis vi ændrer på tegnene i s ændres intet i t

    • Strukturel lighed

Hvis to strenge er reference ens er de også strukturelt ens, men ikke nødvendigvis omvendt

Funktionen strcmp fra string.h
Slide Indhold Stikord
Referencer Lærebog 

Functionen strcmp fra string.h implementerer den leksikografiske ordning samt strukturel lighed på tekststrenge i C

  • Tre mulige output af strcmp(str1,str2):

    • Positivt heltal: str1 > str2

    • Nul: str1 = str2         ( str1 og str2 er ens i strukturel forstand)

    • Negativt heltal: str1 < str2

Henvisning

Program: Et program der illustrerer den leksikografiske ordning af tekststrenge i C.
#include <stdio.h>
#include <string.h>

void pr(char*, char*, int);

int main(void) {

  int b1 = strcmp("book", "shelf");    
  int b2 = strcmp("shelf", "book");    
  int b3 = strcmp("","book");
  int b4 = strcmp("book","bookshelf"); 
  int b5 = strcmp("book", "book");
  int b6 = strcmp("7book", "book");
  int b7 = strcmp("BOOK", "book");

  pr("book", "shelf", b1);
  pr("shelf", "book", b2);
  pr("", "book", b3);
  pr("book", "bookshelf", b4);
  pr("book", "book", b5);
  pr("7book","book", b6);
  pr("BOOK", "book", b7);
  
  return 0;
}

void pr(char *s, char *t, int r){
 printf("strcmp(\"%s\",\"%s\") = %i\n", s,t,r);
}

Program: Output fra programmet.
strcmp("book","shelf") = -17
strcmp("shelf","book") = 17
strcmp("","book") = -98
strcmp("book","bookshelf") = -115
strcmp("book","book") = 0
strcmp("7book","book") = -43
strcmp("BOOK","book") = -32

En af dagens opgaver går ud på at programmere strcmp

Man kan enten gå efter en iterativ løsning eller en rekursiv løsning


Tidligere eksempler

Konvertering mellem talsystemer
Slide Indhold Stikord
Referencer Lærebog 

I en tidligere lektion har vi programmeret funktioner der konverterer tal mellem forskellige talsystemer

De involverede funktioner læste/skrev input/output med getchar og putchar

Det er mere alsidigt at skrive funktioner, der modtager eller returnerer tekststrenge

Henvisning

Program: En funktion der konverterer et tal n i base talsystemet (en streng) til et decimalt tal.
/* Convert the string n to a decimal number in base and return it.
   Assume that input string is without errors */
int to_decimal_number(char *n, int base){
  int ciffer_number, res = 0;
  char *ciffer_ptr = &n[0], ciffer = *ciffer_ptr;

  do {
    if (ciffer >= '0' && ciffer <= '9')
      ciffer_number = ciffer - '0';
    else if (ciffer >= 'a' && ciffer <= 'z')
      ciffer_number = ciffer - 'a' + 10;
    else ciffer_number = -1;   /* error */

    if (ciffer_number >= 0 && ciffer_number < base)
      res = res * base + ciffer_number;

    ciffer_ptr++; ciffer = *ciffer_ptr;
  }
  while (ciffer != '\0');

  return res;
}   

Program: Hele programmet.
#include <stdio.h>
#include <stdlib.h>

int read_in_base(int);

int main(void) {
  int i, n, base;
  char *the_number[20];

  for (i = 1; i <= 5; i++){
    printf("Enter number base (a decimal number)"
           "and a number in that base: ");
    scanf("%d %s", &base, the_number); 
    printf("The decimal number is: %d\n",
            to_decimal_number(the_number, base));
  }
  
  return 0;
}

/* Convert the string n to a decimal number in base and return it.
   Assume that input string is without errors */
int to_decimal_number(char *n, int base){
  int ciffer_number, res = 0;
  char *ciffer_ptr = &n[0], ciffer = *ciffer_ptr;

  do {
    if (ciffer >= '0' && ciffer <= '9')
      ciffer_number = ciffer - '0';
    else if (ciffer >= 'a' && ciffer <= 'z')
      ciffer_number = ciffer - 'a' + 10;
    else ciffer_number = -1;   /* error */

    if (ciffer_number >= 0 && ciffer_number < base)
      res = res * base + ciffer_number;

    ciffer_ptr++; ciffer = *ciffer_ptr;
  }
  while (ciffer != '\0');

  return res;
}   

Udskrivning af enumeration konstanter
Slide Indhold Stikord
Referencer Lærebog 

I en tidligere lektion har vi programmeret funktioner der udskriver navnene på enumeration konstanter

De involverede funktioner skrev output med printf

Det er mere alsidigt at skrive funktioner, returnerer tekststrenge

Henvisning

Program: En funktion som returnerer en symbolsk ugedag (en streng) givet dagens nummer.
/* Return the symbolic name of day d */
char *print_name_of_day(days  d){
  char *result;
  switch (d) {
    case sunday: result = "Sunday";
       break;
    case monday: result = "Monday";
       break;
    case tuesday: result = "Tuesday";
       break;
    case wednesday: result = "Wednesday";
       break;
    case thursday: result = "Thursday";
       break;
    case friday: result = "Friday";
       break;
    case saturday: result = "Saturday";
       break;
  }
  return result;
}  

Program: Hele programmet.
#include <stdio.h>

enum days {sunday, monday, tuesday, wednesday, thursday, 
           friday, saturday};
typedef enum days days;

days  next_day_of(days  d){
  return ( days ) (((int) d + 1) % 7);
}  

/* Return the symbolic name of day d */
char *print_name_of_day(days  d){
  char *result;
  switch (d) {
    case sunday: result = "Sunday";
       break;
    case monday: result = "Monday";
       break;
    case tuesday: result = "Tuesday";
       break;
    case wednesday: result = "Wednesday";
       break;
    case thursday: result = "Thursday";
       break;
    case friday: result = "Friday";
       break;
    case saturday: result = "Saturday";
       break;
  }
  return result;
}     

int main(void){
  
  days  day1 = saturday,  another_day;
  int i;

  printf("Day1 is %d\n", day1);

  printf("Day1 is also %s\n", print_name_of_day(day1));

  another_day = day1;
  for(i = 1; i <= 3; i++)
    another_day = next_day_of(another_day);
  
  printf("Three days after day1: %s", 
          print_name_of_day(another_day));
  printf("\n");

  return 0;
}    


Biblioteket string.h

Oversigt
Slide Indhold Stikord
Referencer 

  • char *strcat(char *s1, const char *s2);

    • Sammensætter s1 og s2 i s1. Først s1 dernæst s2. Kræver plads nok i s1.

  • char *strcpy(char *s1, const char *s2);

    • Kopierer s2 ind i starten af s1, inklusive 0 tegnet.

  • char *strncpy(char *s1, const char *s2, size_t n);

    • Kopierer n tegn (eller indtil nultegn) fra s2 ind i starten af s1.

  • char *strchr(const char *s, int c);

    • Returnerer pointer til første forekomst af c i s.

  • char *strstr(const char *s1, const char *s2);

    • Returnerer en pointer til den første forekomst af s2 i s1. Ellers NULL.

  • size_t strlen(const char *s);

    • Returnerer antallet af tegn i s, eksklusive nultegnet.

  • int strcmp(const char *s1, const char *s2);

    • Leksikografisk sammenligning af s1 og s2. Negativ, nul, eller positiv returværdi.

Ligesom de andre standardbibliotekter er string.h beskrevet i appendix A af C by Dissection (se side 552).

Sammensætning af strenge
Slide Indhold Stikord
Referencer Lærebog 

Funktionen strcat indsætter en streng i bagenden af en anden streng forudsat der er plads efter nultegnet

Program: Et program der illustrerer strcat og strlen.
#include <stdio.h>
#include <string.h>

int main(void) {

  char s[25] = "Aalborg";  
  char t[] = "University";
  char *res;

  res = strcat(strcat(s," "),t);
  printf("%s: %i chars\n", res, strlen(s));
  
  return 0;
}

En alternativ funktion til strcpy
Slide Indhold Stikord
Referencer Lærebog 

Som de fleste andre funktioner i string.h allokerers der ikke lager i strcpy

Her vil vi programmere en streng-kopierings funktion der allokerer plads til en ny kopi

Program: Et funktioner der allokerer plads til og kopierer en streng.
/* Copy s to a fresh allocated string and return it */
char *string_copy(const char *s){
  
 static char *new_str;

 new_str = (char *)malloc(strlen(s)+1);
 strcpy(new_str,s);

 return new_str;
}

Program: Hele programmet.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


/* Copy s to a fresh allocated string and return it */
char *string_copy(const char *s){
  
 static char *new_str;

 new_str = (char *)malloc(strlen(s)+1);
 strcpy(new_str,s);

 return new_str;
}


int main(void) {

  char s[] = "Aalborg University", *t;
  t = string_copy(s);
  strcpy(s,"---");      /* destroy s */

  printf("The original is: %s.\nThe copy is: %s\n", s, t);
  
  return 0;
}

Program: En variant af string_copy der opbygger kopien i lokalt lager. Vi har valgt kun at kopiere 7 tegn.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


/* Copy s to a locally allocated string and return it */
char *string_copy(const char *s){
  
 static char new_str[8];  /* static is essential. Zero initialized */
 strncpy(new_str,s,7);

 return new_str;
}


int main(void) {

  char s[] = "Aalborg University", *t;
  t = string_copy(s);
  strcpy(s,"---");      /* destroy s */

  printf("The original is: %s.\nThe copy is: %s\n", s, t);
  
  return 0;
}

Substring ved brug af strncpy
Slide Indhold Stikord
Referencer Lærebog 

Der er ofte behov for at udtrække en substreng af en anden streng

Dette kan gøres med strncpy

Program: Et program der udtrækker en substreng af en streng. Det er essentielt at target initialiseres med nultegn, idet det viser sig, at strncpy ikke overfører et nultegn.
#include <stdio.h>
#include <string.h>

#define LEN 25

int main(void) {

  char str[] = "The Aalborg University basis year";
  char target[25];
  int i; 

  for(i = 0; i < LEN; i++) target[i] = '\0';  

  strncpy(target, str+4, 18);

  printf("The substring is: %s\nLength: %i\n",
          target, strlen(target));
  
  return 0;
}


Andre emner om tekststrenge

Arrays af tekststrenge
Slide Indhold Stikord
Referencer Lærebog 

Et array af tekststrenge kan enten forstås som en to dimensionel char tabel eller som en pointer til en char pointer

Program: Et program der allokerer og tilgår et array af tre tekststrenge.
  char *numbers[] = {"one", "two", "three"};
  char ch1, ch2, ch3, ch4;

  ch1 = **numbers;
  ch2 = numbers[0][0];
  ch3 = *(*(numbers+1) + 1);
  ch4 = numbers[2][2];  

Figur. En illustration af variablen numbers fra ovenstående program

Program: Hele programmet.
#include <stdio.h>

int main(void) {

  char *numbers[] = {"one", "two", "three"};
  char ch1, ch2, ch3, ch4;

  ch1 = **numbers;
  ch2 = numbers[0][0];
  ch3 = *(*(numbers+1) + 1);
  ch4 = numbers[2][2];  

  printf("ch1 = %c, ch2 = %c, ch3 = %c, ch4 = %c\n", ch1, ch2, ch3, ch4);
  
  return 0;
}

Program: Output fra programmet.
ch1 = o, ch2 = o, ch3 = w, ch4 = r

Variablen numbers kunne alternativt erklæres og initialiseres som char numbers[][6] = {"one", "two", "three"}

En af dagens opgaver handler om et to dimensionelt array af tekststrenge

Program: Programmet med alternativ array erklæring.
#include <stdio.h>

int main(void) {

  /* char *numbers[] = {"one", "two", "three"}; */
  char numbers[][6] = {"one", "two", "three"};
  char ch1, ch2, ch3, ch4;
  int i;

  ch1 = **numbers;
  ch2 = numbers[0][0];
  ch3 = *(*(numbers+1) + 1);
  ch4 = numbers[2][2];  

  printf("ch1 = %c, ch2 = %c, ch3 = %c, ch4 = %c\n", ch1, ch2, ch3, ch4);

  /* Printing 18 characters */
  for(i=0; i < 18; i++)
    printf("%c", *(&numbers[0][0] + i));
  printf("\n");

  /* Printing 3 strings */
  for(i=0; i < 3; i++)
    printf("%s", numbers[i]);

  printf("\n");
  return 0;
}

Program: Output fra det alternative program (0 tegn konverteret til N).
ch1 = o, ch2 = o, ch3 = w, ch4 = r
oneNNNtwoNNNthreeN
onetwothree

Input og output af tekststrenge
Slide Indhold Stikord
Referencer Lærebog 

Input af teststrenge med scanf har specielle regler

Output af tekststrenge med printf virker som forventet

Program: Et program der indlæser og udskriver en tekststreng med scanf og printf.
#include <stdio.h>
#include <string.h>

int main(void) {
  char input[100];

  do {
    printf("Enter a string: ");
    scanf("%s", input);
    printf("You entered \"%s\"\n", input);
  }  while (strcmp(input, "exit"));
  
  return 0;
}

Programparametre
Slide Indhold Stikord
Referencer Lærebog 

Operativsystemet overfører et array af strenge til main funktionen med information om, hvordan programmet er kaldt

Dette giver muligheder for at overføre programparametre som tekststrenge

Program: Et program der udskriver de overførte programparametre.
/* Echo the command line arguments. */

#include <stdio.h>

int main(int argc, char *argv[])
{
   int   i;

   printf("\n  argc = %d\n\n", argc);
   for (i = 0; i < argc; ++i)
      printf("   argv[%d] = %s\n", i, argv[i]);
   putchar('\n');
   return 0;
}

Program: Input til (fed) og output (rød) fra programmet.
[normark@localhost strings]$ a.out programming in C

  argc = 4

   argv[0] = a.out
   argv[1] = programming
   argv[2] = in
   argv[3] = C


Samlede referencer
Indhold Stikord
Tidligere program der genererer ASCII tegntabel
Tidligere programmer
Tidligere program

 

Kapitel 7: Tekststrenge
Kursets hjemmeside     Forfatteren's hjemmeside     Om frembringelsen af disse sider     Forrige lektion (top)     Næste lektion (top)     Forrige lektion (bund)     Næste lektion (bund)     
Genereret: 7. Juli 2010, 15:11:58