Funzioni di libreria: trattamento stringhe

Le funzioni per il trattamento stringhe rappresentano il nucleo di Proteus; ecco perché questa categoria comprende un numero elevato di funzioni.
Gli indici sulle stringhe in Proteus vanno da 1 alla lunghezza della stringa (a differenza del C, in cui gli indici vanno da 0 a lunghezza - 1). I valori ritornati da queste funzioni in caso di errore sono 0 (-1 in caso di handle errato) oppure una stringa vuota.

Per facilitarne la ricerca, le funzioni di questa categoria sono state ulteriormente suddivise in:

Si ricordi che tutti i test logici di uguaglianza/disuguaglianza sulle stringhe hanno il prefisso comune STR (STREQ, STRNEQ, ..); ciò serve a distinguere tali funzioni da quelle che operano sui numeri (EQ, NEQ, ..). Un errore comune è quello di utilizzare l'una per l'altra, come in questo caso:

SET S1 = "PROTEUS"
SET S2 = "TEXT"
.. 
IF EQ(S1, S2)
  ; Errore! S1 ed S2 sono due stringhe diverse, ma questo test sarebbe
  ; soddisfatto in quanto entrambe le stringhe hanno 0 come interpretazione
  ; numerica (non essendo numeri validi)
  ..
FI

Estrazione sottostringhe:

STRDUP(c)

ritorna una copia di c, valutato come stringa

SUBSTR([@]c, nStart[, nLength])

restituisce la sottostringa di c, a partire da nStart, di lunghezza pari a nLength caratteri (o il resto della stringa, se non specificato); se nLength eccede il numero di caratteri disponibili, ritorna fino alla fine di c; se nStart è fuori intervallo, ritorna una stringa vuota

RESTFROM([@]c, nStart)

restituisce la sottostringa di c che va da nStart alla fine di c; ritorna una stringa vuota se nStart è fuori intervallo

LEFT([@]c, n)

ritorna una copia dei primi n caratteri da sinistra di c; se n è negativo, ritorna LEFT(cADD(STRLEN(c), n)); se n è maggiore della lunghezza di c, ritorna una copia di c

RIGHT([@]c, n)

ritorna una copia degli ultimi n caratteri di c; se n è negativo, ritorna RIGHT(c, ADD(STRLEN(c), n)); se n è maggiore della lunghezza di c, ritorna una copia di c

CHOP([@]c)

ritorna l'ultimo carattere della stringa c; nel caso di passaggio per riferimento, la stringa c è accorciata del carattere ritornato

STRTRAVERSE(c, udffunction)

permette di scandire la stringa c, invocando udffunction per ogni carattere della stringa; la UDF deve accettare due soli parametri (carattere, posizione in c) e ritorna 0 (continua con il prossimo carattere) oppure 1 (termina l'attraversamento della stringa); STRTRAVERSE ritorna -1 se c è vuota, 0 se è stata attraversata completamente, 1 se la UDF ha richiesto la terminazione anticipata

Formattazione stringhe:

STRIPQUOTES([@]c)

ritorna una copia di c con le virgolette (") iniziali e finali rimosse

CENTER([@]c, nLength)

centra la stringa c in una nuova stringa di nLength caratteri; ritorna la stringa c se nLength è minore della lunghezza di c

JUSTIFY([@]c, nLength)

giustifica a pacchetto c in una stringa di nLength caratteri; ritorna la stringa c se nLength è minore della lunghezza di c

PACKCHAR([@]c, cPackChar)

sostituisce occorrenze multiple del primo carattere di cPackChar in c con un singolo carattere; ritorna c se cPackChar è una stringa vuota

DETAB([@]c, nTabInterval)

ritorna c con le tabulazioni espanse; nTabInterval è l'intervallo di tabulazione (= numero di colonne; se <= 0, le tabulazioni sono solo rimosse)

ENTAB([@]c, nTabInterval)

sostituisce gli spazi in c con opportune tabulazioni, in modo da mantenere l'allineamento attuale se una tabulazione è presente ogni nTabInterval colonne; ritorna la stringa stessa se nTabInterval <= 1

PADL([@]c, nLength, cPadChar)

ritorna la stringa c preceduta dal primo carattere in cPadChar ripetuto un numero di volte sufficiente a far assumere alla stringa risultante lunghezza nLength; se c è più lunga di nLength, viene troncata ad nLength caratteri; se nLength è negativo, ritorna una stringa vuota

PADR([@]c, nLength, cPadChar)

ritorna la stringa c seguita dal primo carattere in cPadChar ripetuto un numero di volte sufficiente a far assumere alla stringa risultante lunghezza nLength; se c è più lunga di nLength, viene troncata ad nLength caratteri; se nLength è negativo, ritorna una stringa vuota

STRCAT([@]c1, [@]c2[, c3..])

ritorna una nuova stringa corrispondente alle stringhe concatenate; Proteus ammette comunque anche la concatenazione per giustapposizione

LTRIM([@]c, cTrimChars)

ritorna una copia di c da cui sono stati rimossi tutti i caratteri iniziali (cioè a sinistra) contenuti nella stringa cTrimChars

RTRIM([@]c, cTrimChars)

ritorna una copia di c da cui sono stati rimossi tutti i caratteri finali (cioè a destra) contenuti nella stringa cTrimChars

ALLTRIM([@]c, cTrimChars)

ritorna una copia di c da cui sono stati rimossi tutti i caratteri iniziali e finali (a destra e a sinistra) contenuti nella stringa cTrimChars

UPPER([@]c)

ritorna una copia di c in cui tutti i caratteri alfabetici sono stati convertiti in maiuscolo

LOWER([@]c)

ritorna una copia di c in cui tutti i caratteri alfabetici sono stati convertiti in minuscolo

CAPITALIZE([@]c)

ritorna una copia di c in cui l'iniziale di ogni parola (se carattere alfabetico) è stata trasformata in maiuscolo, mentre tutti gli altri caratteri sono stati resi minuscoli; la procedura riconosce l'apostrofo come separatore di parola, tuttavia non converte in maiuscolo le singole lettere che seguono un apostrofo (es. il genitivo sassone inglese) e mantiene in maiuscolo II, III, IV (ordinali latini)

CTRAN([@]c)

ritorna una nuova stringa calcolata da c interpretando tutte le costanti C-like

CINVTRAN([@]c)

ritorna una nuova stringa calcolata da c in questo modo: i caratteri speciali sono rimpiazzati dalle rispettive sequenze di escape (dove necessario), i caratteri non stampabili sono trasformati in costanti esadecimali

STRTRAN([@]c, cSearch, cReplace)

ritorna una copia di c in cui ogni occorrenza di cSearch è stata sostituita da cReplace

REXSTRTRAN([@]c, xExpSearch, cReplace)

ritorna una copia di c in cui ogni parte che soddisfa l'espressione regolare estesa xExpSearch è sostituita dalla stringa cReplace

REXISTRTRAN([@]c, xExpSearch, cReplace)

ritorna una copia di c in cui ogni parte che soddisfa l'espressione regolare estesa xExpSearch è sostituita dalla stringa cReplace; il confronto è effettuato in modo insensibile alle maiuscole (A = a e viceversa)

REXFSTRTRAN([@]c, xExpSearch, funcreplace)

ritorna una copia di c in cui ogni parte che soddisfa l'espressione regolare estesa xExpSearch è sostituita dal valore ritornato dalla UDF funcreplace, la quale prende un solo parametro corrispondente alla stringa che soddisfa xExpSearch

REXIFSTRTRAN([@]c, xExpSearch, funcreplace)

ritorna una copia di c in cui ogni parte che soddisfa l'espressione regolare estesa xExpSearch è sostituita dal valore ritornato dalla UDF funcreplace, la quale prende un solo parametro corrispondente alla stringa che soddisfa xExpSearch; il confronto è effettuato in modo insensibile alle maiuscole (A = a e viceversa)

INSERT([@]c, nStart, cInsert)

ritorna una copia di c in cui è stata inserita cInsert a partire dal carattere nStart; se nStart è maggiore della lunghezza di c, cInsert è concatenata a c

DELETE([@]c, nStart, nLength)

restituisce una copia di c in cui sono stati cancellati nLength caratteri a partire dal carattere nStart

STUFF([@]c, nStart, nLength, cInsert)

combinazione di INSERT + DELETE: cancella nLength caratteri a partire da nStart in c e vi inserisce la stringa cInsert

MAPNEW(cFrom, cTo)

crea una nuova mappatura da ogni carattere in cFrom ad ogni corrispondente carattere in cTo; se cFrom è più lunga di cTo, ogni carattere senza corrispondente viene posto uguale a sé stesso; se cFrom è più corta di cTo, i caratteri addizionali di cTo sono ignorati; ritorna lo handle della nuova mappatura

MAPFREE(mHandle)

libera la memoria allocata per la mappatura corrispondente allo handle; ritorna -1 se mHandle è errato, 0 altrimenti

MAP(mHandle, [@]c)

ritorna la stringa c trasformata secondo la mappatura corrispondente a mHandle; se mHandle non è valido, ritorna una stringa vuota

Test logici:

ISEMPTY(c)

ritorna un valore logico che indica se c è una stringa vuota (soli spazi o tabulazioni)

ISNOTEMPTY(c)

ritorna un valore logico che indica se c non è vuota

ISALPHA(c)

ritorna un valore logico che indica se c contiene solo caratteri alfabetici

ISDIGIT(c)

ritorna un valore logico che indica se c contiene solo cifre 0-9

STREQ(c1, c2)

ritorna un valore logico che indica se le stringhe c1 e c2 sono identiche

STRIEQ(c1, c2)

ritorna un valore logico che indica se le stringhe c1 e c2 sono uguali (confronto insensibile alle maiuscole)

STRNEQ(c1, c2)

ritorna un valore logico che indica se le stringhe c1 e c2 non sono identiche

STRINEQ(c1, c2)

ritorna un valore logico che indica se le stringhe c1 e c2 sono diverse (confronto insensibile alle maiuscole)

MATCH(c, rExp)

ritorna un valore logico che indica se c è simile a rExp (espressione regolare semplice)

IMATCH(c, rExp)

ritorna un valore logico che indica se c è simile a rExp (espressione regolare semplice); il confronto è effettuato in modo insensibile alle maiuscole (A = a e viceversa)

REXMATCH(c, xExp)

ritorna un valore logico che indica se c corrisponde a xExp (espressione regolare estesa); REXMATCH cambia i valori di due variabili predefinite: R_START ed R_LENGTH; al termine di un match positivo, R_START è impostato all'inizio della prima stringa che ha determinato il match ed R_LENGTH è impostato alla lunghezza di tale stringa; se REXMATCH fallisce, entrambe le variabili sono impostate a 0

REXIMATCH(c, xExp)

ritorna un valore logico che indica se c corrisponde a xExp (espressione regolare estesa); il confronto è effettuato in modo insensibile alle maiuscole (A = a e viceversa); REXMATCH cambia i valori di due variabili predefinite: R_START ed R_LENGTH; al termine di un match positivo, R_START è impostato all'inizio della prima stringa che ha determinato il match ed R_LENGTH è impostato alla lunghezza di tale stringa; se REXMATCH fallisce, entrambe le variabili sono impostate a 0

IN(cFrom, cTo)

ritorna un valore logico che indica se cFrom è contenuta in cTo

OF(c, cAlphabet)

ritorna un valore logico che indica se c consiste interamente di caratteri in cAlphabet

Confronto stringhe:

STRCMP(c1, c2)

confronto fra stringhe; ritorna:
0 se c1 == c2
< 0 se c1 < c2
> 0 se c1 > c2

STRICMP(c1, c2)

confronto insensibile alle maiuscole fra stringhe; ritorna:
0 se c1 == c2
< 0 se c1 < c2
> 0 se c1 > c2

Ricerca posizioni:

STRPBRK(c, cAlphabet)

ritorna la prima occorrenza in c di uno dei caratteri in cAlphabet (0 se nessun carattere di cAlphabet è in c)

STRCPBRK(c, cAlphabet)

ritorna la prima occorrenza in c di uno dei caratteri non in cAlphabet (0 se c è interamente costituito da caratteri in cAlphabet)

STRSTR(c, cSearch)

ritorna la posizione della prima occorrenza in c di cSearch

STRISTR(c, cSearch)

ritorna la posizione della prima occorrenza in c di cSearch; il confronto è effettuato in modo insensibile alle maiuscole

STRRSTR(c, cSearch)

ritorna la posizione della prima occorrenza in c di cSearch (partendo dalla fine)

STRIRSTR(c, cSearch)

ritorna la posizione della prima occorrenza in c di cSearch (partendo dalla fine); il confronto è effettuato in modo insensibile alle maiuscole

POSDIFF(c1, c2)

ritorna la posizione alla quale c1 differisce da c2, 0 se identiche

POSIDIFF(c1, c2)

ritorna la posizione alla quale c1 differisce da c2, 0 se identiche; il confronto è effettuato in modo insensibile alle maiuscole

Lunghezze e ripetizioni:

STRLEN(c)

numero di caratteri in c

COUNTSTR(c, cSearch)

numero di volte in cui la sequenza cSearch compare in c

COUNTISTR(c, cSearch)

numero di volte in cui la sequenza cSearch compare in c; il confronto è effettuato in modo insensibile alle maiuscole

STRSPN(c, cAlphabet)

lunghezza del più lungo segmento iniziale di c che è costituito solo da caratteri di cAlphabet

STRCSPN(c, cAlphabet)

lunghezza del più lungo segmento iniziale di c che non contiene caratteri in cAlphabet

Funzioni tokenizzatrici dinamiche:

TOKEN([@]c, nToken, cSeparators)

restituisce il token nToken di c; cSeparators è una stringa contenente i caratteri separatori ammessi; se non esiste il token nToken, restituisce una stringa vuota

NUMTOKEN(c, cSeparators)

numero di token in c, determinati dai separatori in cSeparators

POSTOKEN(c, nToken, cSeparators)

posizione in c del token nToken, determinato dai separatori in cSeparators; se non esiste il token nToken, ritorna 0

REXTOKEN([@]c, nToken, xExp)

restituisce il token nToken di c; xExp è l'espressione regolare estesa soddisfatta dai separatori ammessi; se non esiste il token nToken, restituisce una stringa vuota

REXITOKEN([@]c, nToken, xExp)

restituisce il token nToken di c; xExp è l'espressione regolare estesa soddisfatta dai separatori ammessi; se non esiste il token nToken, restituisce una stringa vuota; il confronto è effettuato in modo insensibile alle maiuscole

REXPOSTOKEN(c, nToken, xExp)

posizione in c del token nToken, determinato dai separatori che soddisfano xExp; se non esiste il token nToken, ritorna 0

REXIPOSTOKEN(c, nToken, xExp)

posizione in c del token nToken, determinato dai separatori che soddisfano xExp; se non esiste il token nToken, ritorna 0; il confronto è effettuato in modo insensibile alle maiuscole

REXNUMTOKEN(c, xExp)

numero di token in c, determinati dai separatori che soddisfano xExp

REXINUMTOKEN(c, xExp)

numero di token in c, determinati dai separatori che soddisfano xExp; il confronto è effettuato in modo insensibile alle maiuscole

Funzioni tokenizzatrici statiche:

le funzioni tokenizzatrici statiche, al contrario di quelle dinamiche, permettono una maggiore velocità di esecuzione perché creano delle copie in memoria di tutti i token e poi vi accedono, senza ricalcolarli.
Le funzioni di questa categoria sono:

TOKNEW(c, cSeparators)

tokenizza c con i separatori in cSeparators; restituisce l'handle (un intero), da utilizzare per ottenere tutte le informazioni relative all'operazione di tokenizzazione; TOKFREE deve essere invocato non appena tali informazioni non sono più utili; ciò è essenziale perché altrimenti le successive invocazioni di TOKNEW finirebbero con saturare la memoria

REXTOKNEW(c, xExp)

tokenizza c con i separatori che soddisfano xExp e restituisce l'handle, come TOKNEW; TOKFREE deve essere invocato non appena tali informazioni non sono più utili

REXITOKNEW(c, xExp)

tokenizza c con i separatori che soddisfano xExp e restituisce l'handle, come TOKNEW; il confronto è effettuato in modo insensibile alle maiuscole; TOKFREE deve essere invocato non appena tali informazioni non sono più utili

TOKNUM(kHandle)

numero di token ottenuti con l'operazione di tokenizzazione corrispondente all'handle kHandle; se l'handle è errato ritorna -1

TOKGET(kHandle, nToken)

restituisce il token nToken dell'operazione di tokenizzazione relativa all'handle kHandle; se l'handle o nToken sono invalidi, ritorna una stringa vuota

TOKPOS(kHandle, nToken)

restituisce la posizione del token nToken dell'operazione di tokenizzazione relativa all'handle kHandle; se l'handle o nToken è invalido, ritorna 0

TOKFREE(kHandle)

libera la memoria riservata dall'operazione di tokenizzazione kHandle; ritorna 0 se liberato con successo, -1 se l'handle è invalido

Altre:

CHR(n)

ritorna una stringa con il solo carattere ASCII n

ASC(c)

ritorna il codice ASCII del primo carattere di c

STRRANDOM(cAlphabet, nLength, nSeed)

ritorna una stringa pseudo-casuale di lunghezza nLength con caratteri presi da cAlphabet, che è una stringa di caratteri qualsiasi (eventualmente ripetuti); il numero intero nSeed è impiegato come valore iniziale della sequenza casuale; pertanto, valori uguali di nSeed e cAlphabet producono stringhe uguali; per creare sequenze di caratteri completamente casuali, usare RANDOM(-1) come valore di nSeed

CRC32(c, [@]n)

ritorna il Cyclic Redundancy Test a 32 bit sulla stringa calcolato secondo Ansi X3.66; per calcolare il CRC su di un file, inizializzare n a 0xFFFFFFFF e, al termine del file, complementare il risultato. Il programma di esempio crc32.prt illustra l'uso di questa funzione.

REVERSE([@]c)

ritorna una copia della stringa c invertita ("ABC" diventa "CBA")

REPLICATE([@]c, nTimes)

ritorna c duplicata nTimes volte; se nTimes <= 0, ritorna una stringa vuota

STRXOR([@]c1, [@]c2[, c3..])

OR esclusivo carattere a carattere tra le stringhe indicate; se c2, c3.. sono più corte di c1, sono duplicate più volte fino a coprire completamente c1; la stringa risultante ha la lunghezza di c1

CRYPT([@]c, cKey1[, cKey2..])

cifra il buffer c con le chiavi cKey1, cKey2.. e lo ritorna; è richiesta almeno una chiave; per decifrare si utilizza la funzione DECRYPT con le stesse chiavi nello stesso ordine; le chiavi sono sensibili alle maiuscole; è anche possibile cifrare più volte uno stesso buffer, sebbene ciò non incrementi considerevolmente la sicurezza. Il programma di esempio encrypt.prt illustra l'uso di questa funzione. N.B. Se a CRYPT viene passata una chiave vuota, viene considerata una chiave di un singolo carattere pari a CHR(255)

DECRYPT([@]c, cKey1[, cKey2..])

decifra il buffer c con le chiavi cKey1, cKey2.. e lo ritorna; è richiesta almeno una chiave; per cifrare si utilizza la funzione CRYPT con le stesse chiavi nello stesso ordine; le chiavi sono sensibili alle maiuscole. Il programma di esempio decrypt.prt illustra l'uso di questa funzione.

PFORMAT(cMask, [@]exp)

ritorna exp formattato in base alla maschera cMask, la cui sintassi corrisponde a quella di printf (la funzione C standard), priva del '%'.
Non è ammesso l'uso di '*' o '%' e la trasformazione deve avere senso; in caso contrario, ritorna una stringa vuota.
Ecco come è costruita cMask:

[flag][ampiezza][.precisione][h|l|L][tipo] 

flag =

ampiezza = minima ampiezza del risultato; può essere:

.precisione = massimo numero di caratteri (o minimo numero di cifre intere) da stampare; può essere:

[h|l|L] = modificano l'interpretazione del tipo seguente:

tipo = interpretazione di exp:

La lunghezza della stringa risultante non può eccedere i 511 caratteri.

MIME64([@]c, @nStatus, vB64par, nEndFlag)
DEMIME64([@]c, @nStatus, vB64par)

queste due funzioni permettono di codificare una stringa nel formato MIME Base 64 e di decodificarla; questo formato di codifica è ampiamente utilizzato per l'interscambio di file binari attraverso posta elettronica; la funzione MIME64 prende quattro parametri:

c stringa da codificare; se passata per riferimento, vi memorizza al termine la stringa codificata
nStatus numero intero, da passare per riferimento (è necessario sempre indicare il carattere '@' prima del nome dell'identificatore); al termine, assume uno dei seguenti valori:
0 = funzione eseguita regolarmente
-1 = vB64par non è lo handle di un vettore di lunghezza 3
1, 2 = se nEndFlag vale 1, indica i caratteri eventualmente in eccesso nella codifica (vedi più sotto)
vB64par handle di un vettore di tre elementi, inizializzati a 0 prima della prima chiamata
nEndFlag deve essere impostato ad 1 se la stringa c chiude i dati da codificare, a 0 altrimenti

Il vettore vB64par è necessario a mantenere accumulatore e registri di shift tra chiamate successive della funzione MIME64; per parificare l'ultima riga (nel caso in cui la lunghezza dei dati da codificare non sia un multiplo di 3), è possibile che la procedura aggiunga 1 o 2 caratteri rispetto alla lunghezza attesa della stringa codificata, che è di norma pari a MUL(DIV(STRLEN(c), 3), 4); in questo caso, nStatus è impostato al numero di caratteri aggiunti; un blocco Base 64 è generalmente preceduto dalle due righe di intestazione MIME; il programma di esempio mime64.prt contiene un semplice codificatore che impiega MIME64.

La funzione DEMIME64 prende i seguenti parametri:

c stringa da decodificare; se passata per riferimento, vi memorizza al termine la stringa decodificata
nStatus numero intero, da passare per riferimento (è necessario sempre indicare il carattere '@' prima del nome dell'identificatore); al termine, assume uno dei seguenti valori:
1 = non è ancora stata raggiunta la fine dei dati codificati
0 = fine dello stream Base 64
-1 = errore (vB64par non è un handle, errore nei dati)
vB64par handle di un vettore di tre elementi, inizializzati a 0 prima della prima chiamata

Il programma di esempio demime64.prt illustra un decodificatore elementare per file contenenti un singolo blocco in formato MIME Base 64.

UUENCODE([@]c)
UUDECODE([@]c)

queste due funzioni permettono di codificare una stringa nel formato UU-encoding e di decodificarla; questo formato di codifica è ampiamente utilizzato per l'interscambio di file binari attraverso posta elettronica; se il numero di bytes nella riga non è divisibile per 3, sono impiegati uno o due bytes fittizi impostati a zero. La lunghezza della stringa risultante (se divisibile per 3) è di (1 + |c| / 3 * 4), dove |c| è la lunghezza di c. Il programma di esempio uuencode.prt contiene un semplice codificatore che impiega UUENCODE, mentre il programma uudecode.prt implementa un decodificatore per file UUencoded in singola sezione.

URLENCODE([@]c1[, c2..])

questa funzione permette di effettuare la codifica URL in un'unica stringa di c1, c2, ..; ogni stringa deve essere del tipo "etichetta=valore"; questa funzione può essere impiegata per passare dei parametri ad altri script CGI

URLDECODE(c)
ENVURLDECODE(c)

queste funzioni permettono di decodificare una stringa URL-encoded; la funzione URLDECODE restituisce lo handle di un albero AVL contenente gli elementi in c (il nome del campo indicizza le informazioni nell'albero), oppure -1 se s è vuota; la funzione ENVURLDECODE ritorna sempre 0 oppure -1 se c è vuota ed imposta le variabili di ambiente corrispondenti ai nomi dei campi in c al loro rispettivo valore

Inizio pagina Prossimo argomento Argomento precedente Indice per argomenti Indice analitico
Midnight Lake iPhone Case Black Women Shoes Black Flat Shoes Leather Flats Black Patent Ballerinas Black Ballet Shoes Casual Shoes Black Shoes Women Balle Record Player Cufflinks Best iPhone XR Clear Cases iPhone XS/XS Max Leather Cases Sale Best iPhone 8/8 Plus Silicone Cases iPhone 7/7 Plus Cases & Screen Protector New Cases For iPhone 6/6 Plus iPhone 8 Case Sale iPhone Xr Case Online iPhone 7 Case UK Online iPhone X Case UK Sale iPhone X Case Deals iPhone Xs Case New Case For iPhone Xr UK Online Case For iPhone 8 UK Outlet Fashion Silver Cufflinks For Men Best Mens Cufflinks Outlet Online The Gold Cufflinks Shop Online Cheap Shirt Cufflinks On Sale Nice Wedding Cufflinks UK Online Top Black Cufflinks UK Online Mens Cufflinks Online Silver Cufflinks For Men Men Cufflinks UK Sale Gold Cufflinks UK Online Gold Cufflinks UK Silver Cufflinks UK Shirt Cufflinks Discount Online Mens Cufflinks Deals & Sales Girls Shoes For Dance Fashion Ballet Dance Shoes Best Ballet Flats Shoes UK Online Cheap Ballet Pointe Shoes UK Online Best Ballet Shoes Outlet Best Dance Shoes Sale Cheap Ballet Flats Sale UK Best Pointe Shoes Online UK Ballet Dance Shoes UK Shoes For Dance UK Best Ballet Slippers Shop Best Yoga Shoes Hotsell