Week 9

Práca s reťazcami, nástroj tr

Prezentácia

Zdrojový kód

Poznámky k prednáške

Unix translate utility: tr

  • Nástroj tr nájdete bežne v každej inštalácii linuxového/unixového systému.
  • Používa sa hlavne na preklad alebo mazanie znakov čítaných zo štandardného vstupu a následný výpis výsledku na štandardný výstup:
$ tr aeiouy AEIOUY
Ahoj, ako sa mas?
AhOj, AkO sA mAs?
Hello, how are you?
HEllO, hOw ArE YOU?

Poznámka

Ak chcete ukončiť zadávanie textu, použite klávesovú skratku Ctrl+d.
  • Nástroj môžeme použiť aj na preklad pomocou Cézarovej šifry (minulotýždňová prednáška). Príklad na preklad s krokom 5:
$ tr a-zA-Z f-za-eF-ZA-E
Ahoj, ako sa mas?
Fmto, fpt xf rfx?
Hello, how are you?
Mjqqt, mtb fwj dtz?
  • Ak sa chcete dozvedieť viac o používaní tohto nástroja, použite manuálovú stránku (man tr). My sa pokúsime o jednoduchú implementáciu tohto nástroja v jazyku C.

Znaky, ktoré je potrebné preložiť

  • Najskôr načítame zo vstupu reťazec, ktorý bude reprezentovať prvú množinu znakov (určenú na preklad).
  • O tejto množine budú platiť tieto pravidlá:
  • Množina môže byť tvorená len znakmi ASCII tabuľky od hodnoty 33 do hodnoty 126 (vrátane). To znamená, že znakmi môžu byť všetky zobraziteľné znaky okrem bielych znakov.
  • Znaky z tejto množiny sa nesmú opakovať, pretože rovnaký znak by tak súčasne mohol byť nahradený niekoľkými inými znakmi.
  • Keďže splnenie prvého pravidla je možné očakávať predvolene, zabezpečíme vo svojej implementácii splnenie druhého pravidla. Ak teda dôjde k porušeniu tohto pravidla, vypíšeme na obrazovku správu Wrong input! a program ukončíme (tr-1.c):
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

void read_set(char set[]);
bool contains_same_letters(const char set[]);


int main() {
    // reading set 1
    char set1[50];
    read_set(set1);

    // check for same letters
    if (contains_same_letters(set1)) {
        printf("Wrong input.\n");
        return -1;
    }

    return 0;
}

void read_set(char set[]) {
    printf("Enter set 1: ");
    scanf("%s", set);
}

bool contains_same_letters(const char set[]) {
    for (int index = 0, len = (int) strlen(set); index < len - 1; index++) {
        for (int index2 = index + 1; index2 < len; index2++) {
            if (set[index] == set[index2]) {
                return true;
            }
        }
    }
    return false;
}

Znaky, ktoré prekladajú

  • Ďalej zo vstupu načítame reťazec, ktorý bude reprezentovať druhú množinu znakov (tie, ktoré prekladajú). Vieme využiť tú istú funkciu na načítanie vstupu, len ju trochu upravíme, aby prompt obsahoval číslo množiny.
  • Jedinou podmienkou, ktorú je potrebné splniť pri načítaní druhej množiny, je, aby sa počet znakov tejto množiny zhodoval s počtom znakov prvej množiny. T.j. ku každému znaku prvej množiny musí existovať znak, ktorým bude nahradený (tr-2.c):
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define SET_LEN 50

void read_set(int set_number, char set[]);
bool contains_same_letters(const char set[]);


int main() {
    // reading set 1
    // ...

    // check for same letters
    // ...

    // reading set 2
    char set2[SET_LEN];
    read_set(2, set2);

    // check for the same length
    if (strlen(set1) != strlen(set2)) {
        printf("Length of sets is different.\n");
        return -1;
    }

    return 0;
}

void read_set(int set_number, char set[]) {
    printf("Enter set %d: ", set_number);
    scanf("%s", set);
}

Text na preklad

  • Teraz načítame text, ktorý je potrebné preložiť. Text bude tvorený reťazcom, ktorý bude na vstupe ukončený novým riadkom.
  • Text sa môžeme pokúsiť načítať niektorou z nasledovných funkcií: scanf(), getchar(), gets() alebo fgets() (pričom miesto uvedenia referencie na súbor použijeme referenciu s názvom stdin (štandardný vstup)).
  • Použitie každej z uvedených funkcií má svoje špecifiká (až na gets(), ktorú prekladač označí za deprecated a odmietne preložiť program). Ak použijeme funkciu scanf(), musíme si byť vedomí toho, že viacnásobné medzery, resp. biele znaky, budú ignorované. Rovnako tak sa musíme rozhodnúť pre vhodnú dĺžku buffra na načítanie vstupu.
  • V našom prípade použijeme funkciu fgets(), keďže tú sme si na prednáškach zatiaľ neukázali (tr-3.c):
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define SET_LEN 50
#define INPUT_LEN 256

void clear_buffer();
void read_set(int set_number, char set[]);
void read_input_text(char text[]);
bool contains_same_letters(const char set[]);

int main() {
    // reading set 1
    // ...

    // check for same letters
    // ...

    // reading set 2
    // ...

    // check for the same length
    // ...

    // clear buffer
    clear_buffer();

    // reading text input
    char text[INPUT_LEN];
    read_input_text(text);

    return 0;
}

void clear_buffer() {
    while (getchar() != '\n');
}

void read_input_text(char text[]) {
    printf("Enter text to translate: ");
    fgets(text, INPUT_LEN, stdin);
}

Preklad načítaného textu

  • Ak sa na vstupe bude nachádzať písmeno, ktoré sa nachádza aj v prvej množine, na výstup sa vypíše také písmeno z druhej množiny, ktoré sa nachádza na rovnakej pozícii ako písmeno z prvej množiny.
  • Ak sa na vstupe bude nachádzať písmeno, ktoré sa v prvej množine nenachádza, rovno sa vypíše na výstup (bez prekladu).
  • Preklad môže vyzerať nasledovne (tr-4.c):
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define SET_LEN 50
#define INPUT_LEN 256

void clear_buffer();
void read_set(int set_number, char set[]);
void read_input_text(char text[]);
bool contains_same_letters(const char set[]);
int find_position(const char[], char);
void translate(const char set1[], const char set2[], const char text[], char translated[]);

int main() {
    // reading set 1
    // ...

    // check for same letters
    // ...

    // reading set 2
    // ...

    // check for the same length
    // ...

    // clear buffer
    // ...

    // reading text input
    // ...

    // translation
    char translated[strlen(text) + 1];
    translate(set1, set2, text, translated);
    printf("%s", translated);

    return 0;
}

int find_position(const char set1[], const char letter) {
    for (int index = 0, len = (int) strlen(set1); index < len; index++) {
        if (letter == set1[index]) {
            return index;
        }
    }
    return -1;
}

void translate(const char set1[], const char set2[], const char text[], char translated[]) {
    int len = (int) strlen(text);
    for (int index = 0; index < len; index++) {
        int position = find_position(set1, text[index]);
        translated[index] = position != -1 ? set2[position] : text[index];
    }
    translated[len] = '\0';
}
  • Použitie prekladu môže vyzerať nasledovne:
$ ./tr
Enter set1: aeiouy
Enter set2: xxxxxx
Enter text to translate: Hello world!
Hxllx wxrld!

Doplňujúce zdroje

  1. Unix translate utility