Pokazywanie postów oznaczonych etykietą string. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą string. Pokaż wszystkie posty

niedziela, 15 czerwca 2014

[C#|Visual Studio] String, ToString()

Typ System.String znany także pod aliasem string towarzyszy nam przez całe nasze .NET-owe życie. W tym poście przedstawione zostaną pewne mniej znane konstrukcje, które mogą przydać się w codziennej pracy z C#.

W C# ciągi znaków są immutable, co oznacza, że nie można ich modyfikować. Każda operacja modyfikująca utworzy na stercie nową instancję. Typ System.String dziedziczy po object i implementuje takie interfejsy, jak IComparable, ICloneable, IConvertible, IEnumerable, IEquatable. Dodatkowo w C# można deklarować tzw. verbatim strings - ciągi znaków wewnątrz których każdy znak ma być traktowany jako część ciągu znaków - przed takim stringiem umieszczamy @.

String interning

Jest to technika optymalizująca zużycie pamięci w .NET. Polega ona na tym, że dla każdego ciągu znaków liczony jest hash i taki ciąg znaków wkładany jest do specjalnego słownika, gdzie kluczem jest właśnie ten hash. Słownik istnieje w obrębie całej AppDomain i podczas alokacji nowego string-a, jeżeli wyliczony hash znajduje się w słowniku, to instancja jest reużywana.

Konstruktory

Warto znać przeciążenia konstruktora. Poniżej przykład na to,  jak utworzyć ciąg 10 zer na 4 sposoby. Sposób wpisania literal-u wydaje się najprostszy, ale możemy to zrobić na inne, bardziej wygodne sposoby za pomocą konstruktorów.

char[] array = new char[10];
for (int i = 0; i < array.Length; i++)
    array[i] = '0';

char[] array2 = new char[12];
for (int i = 0; i < array2.Length; i++)
    array2[i] = '0';

Console.WriteLine(new string(array));
Console.WriteLine(new string(array2, 1, array2.Length -2));
Console.WriteLine(new string('0', 10));
Console.WriteLine("0000000000");

StringBuilder

W C# możemy sklejać stringi za pomocą operatora +. W pewnych sytuacjach (wiele sklejeń) jest on jednak bardzo niewydajny, dlatego zaleca się korzystać z klasy StringBuilder. Obiekt taki zawiera pole, które wskazuje na tablicę struktur typu Char. StringBuilder pozwala na manipulowanie taką tablicą. Jeżeli przekroczymy zaalokowane miejsce, to StringBuilder automatycznie skopiuje znaki i zbuduje nową tablicę. Wywołanie ToString spowoduje stworzenie nowego obiektu String na stercie i zwrócenie referencji do niego. Domyślna pojemność StringBuildera to 16 znaków. Jeżeli operacje wykonywane na nim próbują przekroczyć pojemność, to StringBuilder podwaja zaalokowane miejsce.

var random = new Random();
var sb = new StringBuilder();
var next = random.Next() % 100;
while (next % 2 == 0)
{
    sb.Append(next);
    next = random.Next() % 100;
}
Console.WriteLine(sb.ToString());

Instancyjne metody


var str = "Jambalaya";
//IEnumerable extension methods
Console.WriteLine(new string(str.Distinct().ToArray()));

//Walidacja
if(str.EndsWith("ya") && str.StartsWith("Ja") && str.Contains("ala"))
    Console.WriteLine("Matches");

Console.WriteLine(new string(str.Except("aya").ToArray())); //Jmbl
//traktuje aya jako IEnumerable<char>

//Analiza
Console.WriteLine(str.IndexOf('a'));
Console.WriteLine(str.IndexOfAny(new char[] {'a', 'J'}));
Console.WriteLine(str.LastIndexOf('a'));
Console.WriteLine(str.LastIndexOfAny(new char[] { 'l', 'y' }));

//Tworzenie nowych instancji
Console.WriteLine(str.Insert(3, "error")); //Jamerrorbalaya

//Uzupełnia podanym znakiem do zadanej długości (20), domyślnie spacja
Console.WriteLine(str.PadLeft(20, '_')); // ___________Jambalaya
Console.WriteLine(str.PadLeft(20)); //           Jambalaya

//3 oznacza maksymalną liczbę itemów
var items = str.Split(new char[] {'a'}, 3, 
    StringSplitOptions.RemoveEmptyEntries); //J,mb,laya
foreach (var item in items)
    Console.WriteLine(item);

str += "\n \t";
Console.WriteLine(str.Length); //12
//usuwa wszystkie biale spacje
str = str.Trim();
Console.WriteLine(str.Length); //9

Statyczne metody


var s1 = "me";
var s2 = "Mark";
Console.WriteLine(string.Compare(s1, s2)); //1

var s3 = string.Format("{0} and {1} went to school", s1, s2);
Console.WriteLine(s3);

Console.WriteLine(string.IsNullOrEmpty("")); //true
//drugi argument to params
Console.WriteLine(string.Join(",", s1, s2));

 

Numeryczne konwersje


float price = 99.99f;
Console.WriteLine("C - currency: " +
    price.ToString("C")); //99.99 zł
Console.WriteLine(price.ToString("C", 
    CultureInfo.CreateSpecificCulture("en-US"))); //$99.99

int num = 70;
Console.WriteLine("D - decimal: " + num.ToString("D4")); //0070

long number = 70000000000000000;
Console.WriteLine("E - exponential: " + 
    number.ToString("E")); // 7,000000E+016

Console.WriteLine("N - number: " +
    number.ToString("N")); // 70 000 000 000 000 000,00

double perc = 0.02;
Console.WriteLine("P - percent: " + perc.ToString("P")); //2,00%

int value = 1024;
Console.WriteLine("X - hexadecimal: " + value.ToString("X4")); //0040

poniedziałek, 3 marca 2014

[NoSQL] Redis: Typy danych, funkcjonalności

Redis wspiera 5 typów danych:

1. STRING


Podstawowy typ danych, który może reprezentować całe obiekty zserializowane do stringów,  może też być liczbą.

Podstawowe operacje na stringach:

redis 127.0.0.1:6379> set user:1 "{'name': 'Joe', 'email': 'joe@joe.com'}"
OK
redis 127.0.0.1:6379> get user:1
"{'name': 'Joe', 'email': 'joe@joe.com'}"
redis 127.0.0.1:6379> set user:id 1
OK
redis 127.0.0.1:6379> incr user:id
(integer) 2
redis 127.0.0.1:6379> append user:1 " extra data"
(integer) 50
redis 127.0.0.1:6379> get user:1
"{'name': 'Joe', 'email': 'joe@joe.com'} extra data"
redis 127.0.0.1:6379> getrange user:1 5 9
"e': '"
redis 127.0.0.1:6379> mset order:1 "o1 data" order:2 "o2 data"
OK
redis 127.0.0.1:6379> mget order:1 order:2
1) "o1 data"
2) "o2 data"
redis 127.0.0.1:6379> strlen user:1
(integer) 50

Operacje set i get służą do wstawiania / pobierania wartości pod podany klucz. Podobnie można wykorzystać operacje mset i mget, do których można podać wiele par klucz - wartość i wstawić je do bazy w obrębie jednej operacji. Wartości, które Redis rozpozna jako numeryczne, można inkrementować operacją incr. Kolejne operacje dotyczące typowo ciągów znaków to: append - doklejanie stringa na koniec wartości znajdującej się pod podanym kluczem, getrange - pobranie podzbioru znaków spod podanego klucza oraz strlen, czyli operacja zwracająca długość stringa znajdującego się pod podaną wartością. 

2. LISTA


Zbiór danych posortowanych po kolejności, w jakiej zostały dodane. Podstawowe operacje na listach:


redis 127.0.0.1:6379> lpush mylist "a"
(integer) 1
redis 127.0.0.1:6379> lpush mylist "b"
(integer) 2
redis 127.0.0.1:6379> lpush mylist "c"
(integer) 3
redis 127.0.0.1:6379> lrange mylist 0 2
1) "c"
2) "b"
3) "a"
redis 127.0.0.1:6379> ltrim mylist 0 1
OK
redis 127.0.0.1:6379> lrange mylist 0 2
1) "c"
2) "b"
redis 127.0.0.1:6379> lindex mylist 1
"b"
redis 127.0.0.1:6379> lindex mylist 0
"c"
redis 127.0.0.1:6379> lpop mylist
"c"
redis 127.0.0.1:6379> lrange mylist 0 2
1) "b"
redis 127.0.0.1:6379> rpush mylist "e"
(integer) 2
redis 127.0.0.1:6379> lrange mylist 0 2
1) "b"
2) "e"

Dane dodajemy operatorem lpush. Pobieranie odbywa się po zakresie za pomocą lrange gdzie podajemy zakres indeksów. Element spod konkretnego indeksu pobieramy przez lindex. Lista może działać jako kolejka, gdzie dodajemy dane z prawej strony (rpush) i pobieramy z lewej (lpop).

3. SET


Kolekcja unikalnych stringów. Mamy do dyspozycji operacje z teorii zbiorów. Podstawowe operacje na setach:

redis 127.0.0.1:6379> sadd names "joe" "bob" "mary"
(integer) 3
redis 127.0.0.1:6379> scard names
(integer) 3
redis 127.0.0.1:6379> smembers names
1) "bob"
2) "mary"
3) "joe"
redis 127.0.0.1:6379> sadd names2 "joe" "tim" "mary"
(integer) 3
redis 127.0.0.1:6379> sdiff names names2
1) "bob"
redis 127.0.0.1:6379> sinter names names2
1) "mary"
2) "joe"

Elementy do zbioru dodajemy za pomocą operatora sadd. Liczność zbioru możemy podejrzeć za pomocą scard, a jego elementy pobieramy przez smembers. Przykładowe operacje na zbiorach to różnica (sdiff) i przecięcie (sinter).

4. HASH


Typ danych mapujący stringowe pola na stringowe wartości. Redis jest zoptymalizowany pod kątem hashy z niewielką liczbą wartości.

redis 127.0.0.1:6379> hset user:1:h name "joe"
(integer) 1
redis 127.0.0.1:6379> hset user:1:h email "joe@joe.com"
(integer) 1
redis 127.0.0.1:6379> hgetall user:1:h
1) "name"
2) "joe"
3) "email"
4) "joe@joe.com"
redis 127.0.0.1:6379> hmget user:1:h name email
1) "joe"
2) "joe@joe.com"
redis 127.0.0.1:6379> hkeys user:1:h
1) "name"
2) "email"
redis 127.0.0.1:6379> hvals user:1:h
1) "joe"
2) "joe@joe.com"

Dane do hasha o nazwie user:1:h  pod pole name wstawiamy poleceniem hset. Wartości możemy pobrać na kilka sposobów. Jawnie podając pola (hmget), pobierając wszystkie (hvals). Same nazwy pól pobieramy poleceniem hkeys, a klucze i wartości komendą hgetall.

5. SORTED SET


Zbiory unikalnych danych posortowane po rankingu.

redis 127.0.0.1:6379> zadd hs 120 "joe" 100 "bob" 100 "mary" 90 "tim"
(integer) 4
redis 127.0.0.1:6379> zrank hs "bob"
(integer) 1

Dodajemy dane jako pary ranking - klucz poleceniem zadd, natomiast ranking konkretnego klucza (indeks) pobieramy za pomocą zrank.

Pozostałe funkcjonalności:


Redis może pracować jako message bus poprzez proste subskrybowanie / publikowanie wiadomości.

subscribe greetings //klient 1
publish greetings "hello redis" //klient 2
psubscribe greet* // subskrybuje na wszystkie kanały spełniające pattern
unsubscribe / punsubscribe

Ciekawą funkcjonalnością niekoniecznie spotykaną w bazach NoSQL jest zaimplementowana transakcyjność. Transakcję otwieramy poleceniem multi, po którym podajemy operacje mające się wykonać w obrębie transakcji. Kończymy poleceniem exec.
Z poziomu aplikacji klienckiej możemy także włączyć tryb monitorowania operacji, jakie wykonywane są na bazie poleceniem monitor. 

Redis przechowuje dane w pamięci z opcją "persisted to disk". Klient komunikuje się z serwerem po TCP wykorzystując specjalny protokół Redis-a. Baza domyślnie robi snapshoty danych na dysk co określoną liczbę zmian wartości. Plik redis.conf przechowuje konfigurację w sposób tekstowy. Można tam ustawić, co ile sekund lub ile zmian kluczy ma się robić snapshot. Przykładowo:

save 300 10 (co najmniej 300 sekund i 10 zmian kluczy)

Replikacja: działa na zasadzie master / slave(s) - slave'y pozostają domyślnie w trybie read only.

Prosta autentykacja polega na tym, że klient podaje hasło, które jest ustawione w pliku redis.conf na serwerze.

niedziela, 1 lipca 2012

[Wzorce projektowe] Facade

Analogia z życia: 

Telewizja satelitarna, taka jak na przykład Cyfra+ w swej ofercie proponuje klientowi kilkaset kanałów. Często jednak zdarza się, że nie wszystkie z nich interesują klienta i co więcej nie potrzebuje do nich dostępu. W związku z tym może sobie wykupić pakiet, zawierający podzbiór wszystkich oferowanych kanałów.



Zastosowanie:

Kiedy mamy dostęp do pewnego API, z którego interesuje nas jedynie podzbiór usług, jakie ono oferuje. Fasadę stosujemy głównie, aby uprościć pewne API i wybrać z niego tą część, która nas interesuje. Może się też zdarzyć, że pewne API, które otrzymaliśmy zostało źle zaprojektowane - tu także do akcji wkracza wzorzec Fasady. Oczywiście nie jest powiedziane że Fasada dotyczy tylko jednego API, można także opakowywać wiele API w jeden interfejs dostosowany do bieżących potrzeb. Wraz z rozwojem programu fasada często będzie wymagała rozbudowywania.

Zasada działania:

Podobnie jak w przypadku Adaptera, przeważnie tworzy się klasę opakowującą pewne API, gdzie metody tej klasy wywołują metody z opakowywanego API.

Przykład:

Często tworzy się Fasady dla operacji I/O tak, aby zapis lub odczyt realizował się wywołaniem jednej metody.

Przykład implementacyjny:

Fasadę można utworzyć nawet dla dot Netowego API do pracy z ciągami znakowymi.

public class StringFacade
    {
        private string _str;

        public StringFacade(string  s)
        {
            _str = s;
        }

        public string Substring(int from, int to)
        {
            return _str.Substring(from, to - from);
        }

        public string Append(string s)
        {
            StringBuilder sb = new StringBuilder(_str);
            sb.Append(s);
            return sb.ToString();
        }
    }