Posts Tagged ‘ code

Porównywanie dat w ActionScript

Implementując pewną funkcjonalność stanąłem przed prostym problemem porównania dwóch dat. W ActionScript do tego celu służy klasa ObjectUtil,
a w niej metoda compareDate.

public static function dateCompare(a:Date, b:Date):int
{
				if (a == null && b == null)
					return 0;

				if (a == null)
					return 1;

				if (b == null)
					return -1;

				var na:Number = a.getTime();
				var nb:Number = b.getTime();

				if (na < nb)
					return -1;

				if (na > nb)
					return 1;

				return 0;
}

Jam można zauwazyć wewnątrz metody daty porównywane są na podstawie zwróconej ilości milisekund od roku 1970 metodą getTime. Potrzebowałem jednak czegoś bardziej specyficznego, mianowicie chciałem porównać daty pomijając przy tym czas (godziny, minuty itd.). Utworzyłem w tym celu nową metodę w której trzeci parametr określa taką możliwość.

public function compareDate(a:Date, b:Date, compareWithTime:Boolean=false):int
{
				var aa:Date;
				var bb:Date;

				if (a == null && b == null)
				{
						return 0;
				}

				if (a == null)
				{
						return 1;
				}

				if (b == null)
				{
						return -1;
				}

				if (compareWithTime)
				{
						return ObjectUtil.dateCompare(a, b);
				}
				else
				{
						//porownanie bez czasu
						//tworze nowe obiekty dat wylaczajac przy tym czas
						aa = new Date(a.fullYear, a.getMonth(), a.getDate());
						bb = new Date(b.fullYear, b.getMonth(), b.getDate());

						return ObjectUtil.dateCompare(aa, bb);
				}
}

Przeprowadziłem test dla dwóch dat i otrzymałem następujące wyniki:

var dOne:Date = new Date(2010, 2, 20, 12, 12, 12);
var dTwo:Date = new Date(2010, 2, 20, 12, 11, 12);

var resultOne:int = compareDate(dOne, dTwo, false);
var resultTwo:int = compareDate(dOne, dTwo, true);

//resultOne = 0; dOne = dTwo
//resultTwo = 1; dOne > dTwo

Prosty błąd – proste rozwiązanie

Zmagam się od jakiegoś czasu z ASP .NET Web Service-ami. Utworzyłem nowy Web Service i postanowiłem zmienić nazwę pliku Web Servicu ze standardowego Service1 na ServiceAdmin. Zmiana przebiegła bezproblemowo po uruchomieniu otrzymałem taki oto błąd:

webserviceerror

Zgodnie ze wskazówkami błędu swoją uwagę skierowałem na plik .asmx – standardowo kliknąłem dwukrotnie w Visual Studio oczekując na pojawienie się magicznej xml-owej linijki. Niestety Visual Studio przekierował mnie do kodu Web Servicu. Po dobrych 10 minutach moje mało spostrzegawcze oczy po kliknięciu prawym przyciskiem myszy na plik ujrzały opcję: Open With… -> XML Editor:

<%@ WebService Language="C#" CodeBehind="ServiceAdmin.asmx.cs" Class="WBAdmin.Service1" %>

Visual Studio nie zrefaktorował atrybutu „Class” – wprowadziłem ręcznie to czego potrzebował kompilator podczas uruchamiania.

<%@ WebService Language="C#" CodeBehind="ServiceAdmin.asmx.cs" Class="WBAdmin.ServiceAdmin" %>

W przeglądarce ujrzałem magiczne „Hellow World”. ;)

Zewnętrzne źródło danych w raportach MS Access

Podczas praktyk studenckich które odbyłem kilka tygodni temu zapoznałem się z możliwościami tworzenia raportów w Microsoft Access. Pracowałem nad rozwoje oprogramowania raportującego. Jednym z postawionych przede mną zadań było stworzenie mechanizmu umożliwiającego wywoływanie procedur składowanych z MS SQL Server i podpięcie otrzymanych wyników do utworzonych wcześniej raportów. Uzyskane wyniki miałem „zapakować” w Visual Basic-owy twór zwany RecordSet-em i podpiąć go bezpośrednio do raportu.

Uruchomienie procedury składowanej z poziomu MS Access utworzonej np. w MS SQL Server umożliwia tzw. kwerenda przekazująca. Cały ciężar związaną z wykonaniem zapytania czy też wspomnianej już procedury składowanej spada na „barki” silnika bazodanowego do którego zostanie wysłane polecenie. Mamy proste narzędzia do tworzenia raportów w MS Access – natomiast filtrowanie i przechowywanie ogromnych ilości danych przed udostępnienem ich w raporcie pozostawiamy większemu silnikowi bazodanowemu. – I to wszystko dzięki magicznej kwerendzie przekazującej. :)

Kod tworzący tymczasowo taką kwerende i jej wywołanie przedstawia się następująco:

Funkcja tworząca i wykonująca zapytanie SQL lub procedure składowaną.

'qSQL = Zapytanie do bazy danych
'return RecordSet
Public Function ExecutePassThruQuery(qSQL As String) As Recordset
    Dim db As DAO.Database
    Dim qryDef As DAO.QueryDef
    Dim recSet As DAO.Recordset
    Dim queryName As String

    On Error GoTo ErrorHandling:
    'Obiekt bazy danych
    Set db = CurrentDb

    'Nazwa tymczasowej kwerendy przekazujacej
    queryName = "tempQuery"
    'Usuniecie kwerendy jesli istnieje
    RemovePassThruQueryIfExists (queryName)

    'Utworzenie nowej kwerendy przekazujacej
    Set qryDef = db.CreateQueryDef(queryName)

    qryDef.ReturnsRecords = True
    'polaczenie z baza danych
    qryDef.Connect = connectionString
    qryDef.SQL = qSQL

    'Zwraca obiekt Recordset-u z danymi, w przeciwnym wypadku zwraca Nothing
    Set ExecutePassThruQuery = qryDef.OpenRecordset(dbOpenSnapshot)

    Set db = Nothing
    Set qryDef = Nothing
    Set recSet = Nothing

    Exit Function
ErrorHandling:
    MsgBox Err.Description
End Function

Funkcja wywołująca pracedure składowaną (wykorzystuje powyższą funkcję do utworzenia i wywołania bezpośrednio na bazie danych procedury) – wzasadzie tworzy specyficzny string w postaci np. „nazwa_procedury parametr1, parametr2, parametr3″.

'params = kolekcja parametrow procedury skladowanej jesli jakies przyjmuje
'procName = nazwa procedury skladowanej
'return DAO.RecordSet
Public Function CallStoredProcedure(procName As String, params As Collection) As Recordset
    Dim recSet As DAO.Recordset
    Dim queryName As String
    Dim counter As Integer

    On Error GoTo ErrorHandling:

    queryName = "tempQuery"

    'Dolaczam parametry do procedury
    If Not params Is Nothing Then
        If params.Count > 0 Then
            procName = procName & " " & CStr(params(1))
            For counter = 2 To params.Count
                procName = procName & ", " & CStr(params.Item(counter))
            Next counter
        End If
    End If

    'Wywoluje Funkcje zwracajaca rezultat wykonanej procedury skladowanej
    Set recSet = ExecutePassThruQuery(procName)

    Set CallStoredProcedure = recSet

    Set recSet = Nothing

    Exit Function
ErrorHandling:
    MsgBox Err.Description
End Function

Dodatkowo utworzyłem procedurę usuwającą tymczasową kwerende przekazującą.

'Usuwa tymczasowa kwerende
'queryName = nazwa kwerendy do usuniecia
Private Sub RemovePassThruQueryIfExists(queryName)
    Dim qryDef As QueryDef
    Dim db As Database

    On Error GoTo ErrorHandling
    Set db = CurrentDb

    For Each qryDef In db.QueryDefs
        If qryDef.Name = queryName Then
            db.QueryDefs.Delete (qryDef.Name)
            Exit For
        End If
    Next

    Set qryDef = Nothing
    Set db = Nothing
    Exit Sub
ErrorHandling:
    MsgBox Err.Description
End Sub

Załóżmy, że wywoływana procedura składowana w MS SQL Server ma nazwę „mojaProcedura” i pobiera dwa parametry typu VARCHAR. Wartości parametrów które chcemy przekazac do procedury są następujące: param1 = „ala”, param2 = „ola”. Kwerenda przekazująca w swoim wnętrzu miała by string w takiej oto postaci: „mojaProcedura ala, ola”.

Wywołanie:

Dim recSet As DAO.Recordset
Dim col As New Collection

col.Add "ala"
col.Add "ola"

Set recSet = CallStoredProcedure "mojaProcedura", col

W zasadzie to wszystko – tyle wystarczy, aby otrzymac obiekt RecordSet-u który następnie możemy bez żadnych przeszkód podpiąć do utworzonego wcześniej w MS Access raportu. :)

Zabawa w szyfrowanie – RSA

W ramach zaliczenia laboratorium jednego z przedmiotów szkolnych miałem za zadanie napisać niewielki programik szyfrujący. Wybrałem szyfr RSA – jak podaje wikipedia jego nazwa jest akronimem utworzonym z pierwszych liter nazwisk jego twórców – Rivest, Shamir, Adleman. Szyfrowanie i odszyfrowanie tekstu z wykorzystaniem RSA nie jest procesem zbyt skomplikowanym – wymaga jednak operowania dość dużymi liczbami pierwszymi co zmusiło mnie do rezygnacji ze standardowego typu int w C# który miał za mały zakres. W bezdennych zasobach internetowych znalazłem strukturę którą wykorzystałem w swojej implementacji -> BigInt.

Etapy szyfrowania umożliwiające zakodowanie i odkodowanie tekstu:

  1. Wylosuj dwie duże liczby pierwsze p i q
  2. Oblicz n = p * q
  3. Oblicz phi = (p – 1) * (q – 1)
  4. Oblicz e -> Para (e, n) tworzy klucz publiczny wykorzystywany do szyfrowania
  5. Oblicz d -> Para (d, n) tworzy klucz prywatny wykorzystywany do odszyfrowania
  6. Szyfrowanie znaku:
    • Zamiana znaku na kod Ascii
    • Oznaczmy c jako zaszyfrowany znak c = asciiNume mod n
  7. Odszyfrowanie znaku:
    • Aby otrzymać na powrót zaszyfrowany znak: t = cd mod n

Zamiana znaku na kod Ascii i na odwrót:

          char ch = "A";
          int asciiNum = (int)ch;
          ch = (char)asciiNum;

Nie chcę jednak zagłębiać się maksymalnie w poszczególne etapy szyfrowania i odszyfrowania, a skupię się na samym specyficznym sposobie zapisu zaszyfrowanych znaków który sobie wymyśliłem i zakodziłem. ;)

Przykład 1:

Powiedzmy, że mam wyraz „Ola”, który zaszyfrowałem i chcę zapisać do pliku następnie odczytać i odszyfrować. Liczby powstałe w procesie szyfracji: 186220802, 16730814, 137205435. I tutaj pojawia się pytanie w jaki sposób mogę zapisać liczby tak aby osoba otwierająca plik nie domyśliła się, że poszczególne liczby reprezentują znaki? Jednym ze sposobów jest na przykład oddzielanie liczba przecinkami, kropkami lub innymi znakami interpunkcyjnymi. Mnie to jednak nie zadowalało wpadłem więc na pomysł, aby ustalić na stałe znaki „specjalne”, które zostaną zakodowane i „dodane” do powstałych liczb.

Przykład 2:

Ustalam, że moim znakiem specjalnym będzie przecinek następnie szyfruje jego kod i otrzymuję na przykład liczbę: 164916224.
Dodaje do każdej z wyżej powstałych liczb znak specjalny i do pliku wędruję taki oto ciąg liczb 18622080216491622416730814164916224137205435 :)

Jest jednak problem w sytuacji gdy ciąg znaków wygląda tak „O,l,a,,,”, a znakiem specjalnym jest przecinek. Odczytanie z pliku zaszyfrowanego ciągu z użyciem jako znak specjalny przecinka znacząco utrudnia odszyfrowanie – mamy długi ciąg liczb chcemy go podzielić po przecinku, ale tak naprawdę nie wiemy ile razy przecinek mógł wystąpić w danym wyrażeniu. By mieć możliwość swobodnego podziału tego typu ciągu liczb i odzyskać poszczególne liczby, aby je odkodować następnie odczytać oryginalny tekst źródłowy ustaliłem znaki specjalne spoza kodów Ascii. – Są to losowa przeze mnie wybrane trzy dziwne „krzaczki” które mi pięknie oddzielają poszczególne zakodowane znaki.

Klasę realizującą szyfrowanie i odszyfrowanie na podstawie obliczonych kluczy nazwałem Rsa. Jako, że spróbuję zmierzyć się z implementacją innych sposobów szyfrowania utworzyłem interfejs który w moim programie będzie reprezentował klasy szyfrujące i deszyfrujące.

Interfejs ICipher:

public interface ICipher
{
    StringCollection Encrypt(StringCollection sc);
    StringCollection Encrypt(string path);
    StringCollection Decrypt(StringCollection sc);
    StringCollection Decrypt(string path);
    void SaveToFile(string savePath);
}

Klasa Rsa:

/// <summary>
/// Klasa szyfrujaca RSA
/// </summary>
public class Rsa : ICipher
{
   #region Fields & Properties
   private BigInt _n;
   private BigInt _e;
   private BigInt _d;

   /// <summary>
   /// Tablica znakow specjalnych
   /// </summary>
   private readonly char[] _chars = { 'ฒ', 'ฑ', 'ฐ' };
   private StringCollection _scTemp;

   /// <summary>
   /// Zwraca tablice zawierajaca klucz prywatny i publiczny
   /// Indexy: 0 = n, 1 = e, 2 = d
   /// </summary>
   public BigInt[] Keys
   {
        get
        {
            return new BigInt[]
            {
                 _n,
                 _e,
                 _d
             };
         }
   }
   #endregion

   /// <summary>
   /// Konstruktor przyjmujacy wartosci umozliwiajace szyfrowanie
   /// </summary>
   /// <param name="n">n</param>
   /// <param name="e">e</param>
   /// <param name="d">d</param>
   public Rsa(BigInt n, BigInt e, BigInt d)
   {
            _n = n;
            _e = e;
            _d = d;
   }

    /// <summary>
    /// Konstruktor tworzacy obiekt Rsa tylko do odszyfrowywania danych
    /// Przyjmuje pare tworzaca klucz prywatny
   /// </summary>
   /// <param name="n">n</param>
   /// <param name="d">d</param>
   public Rsa(BigInt n, BigInt d)
   {
         _n = n;
         _d = d;
   }

   #region ICipher Members
   /// <summary>
   /// Szyfruje podana kolekcje stringow
   /// </summary>
   /// <param name="sc">Kolekcja stringow</param>
   /// <returns>Kolekcja zaszyfrowanych stringow</returns>
   public StringCollection Encrypt(StringCollection sc)
   {
       StringCollection cipheredText = new StringCollection();
       _scTemp = null;

       foreach (string s in sc)
       {
            //Zamiana stringa na tablice znakow
            char[] charArray = s.ToCharArray();

            int index = 0;
            string str = string.Empty;

            //Pobranie zakodowanych znakow specjalnych
            BigInt[] biArray = GetCipheredSpecialChar();

            foreach (char ch in charArray)
            {
                //Wylosowanie znaku specjalnego wykorzystanego do szyfracji
                index = Randomizer.UniqueInstance.Next(0, biArray.Count());

                //Szyfrowanie
                BigInt c = BigInt.Parse(ch.GetNumberOfChar().ToString());
                c = c.FastModuloPower(_e, _n);

                //Znak + znak specjalny
                str += c.ToString() + biArray[index].ToString();
            }

            cipheredText.Add(str);
       }
       _scTemp = cipheredText;

       return cipheredText;
   }

   /// <summary>
   /// Szyfruje wskazana zawartosc pliku
   /// </summary>
   /// <param name="path">Sciezka do pliku</param>
   /// <returns>Kolekcja zaszyfrowana</returns>
   public StringCollection Encrypt(string path)
   {
        StringCollection sc = null;
        _scTemp = null;

        try
        {
            string[] stringArray = File.ReadAllLines(path, Encoding.Default);
            sc = new StringCollection();
            sc.AddRange(stringArray);
            sc = Encrypt(sc);
        }
        catch (IOException)
        {
            throw;
        }
        catch (Exception)
        {
            throw;
        }

        return sc;
   }

   /// <summary>
   /// Deszyfracja z kolekcji stringow
   /// </summary>
   /// <param name="sc">Kolekcja stringow</param>
   /// <returns>Zwraca rozszyfrowany text</returns>
   public StringCollection Decrypt(StringCollection sc)
   {
        _scTemp = null;
        //Konwersja zakodowanych znakow specjalnych
        //i wrzucenie wszytkich do tablicy string
        string[] cipheredSpecialCharArray = GetCipheredSpecialChar().ToList()
                     .ConvertAll<string>((BigInt bi) =>
                {
                    return bi.ToString();
                }).ToArray();

        StringCollection decodedTextCollection = new StringCollection();

        foreach (string s in sc)
        {
             //Podział zakodowanych znakow metoda Split wedlug znakow specjalnych
             List<string> cStringArray = s.Split(cipheredSpecialCharArray, StringSplitOptions.RemoveEmptyEntries).ToList();

             string decodedText = string.Empty;
             //Iteracja po tablicy zawierajacej poszczegolne znaki w postaci zakodwanych liczb
             foreach (string str in cStringArray)
             {
                 BigInt de = BigInt.Parse(str).FastModuloPower(_d, _n);
                 char c = de.GetCharFromNumber();
                 decodedText += c;
             }
             //Dodanie do kolekcji utworzonej linii textu
             decodedTextCollection.Add(decodedText);
        }

        _scTemp = decodedTextCollection;

        return decodedTextCollection;
   }

   /// <summary>
   /// Deszyfruje text odczytany z pliku
   /// </summary>
   /// <param name="path">Sciezka do pliku</param>
   /// <returns>Zwraca rozszyfrowany text</returns>
   public StringCollection Decrypt(string path)
   {
        _scTemp = null;

        StringCollection sc = null;
        try
        {
             string[] stringArray = File.ReadAllLines(path, Encoding.Default);
             sc = new StringCollection();
             sc.AddRange(stringArray);
             sc = Decrypt(sc);
        }
        catch (IOException)
        {
              throw;
        }
        catch (Exception)
        {
              throw;
        }

        return sc;
   }

   /// <summary>
   /// Metoda realizujaca zapis do pliku
   /// </summary>
   /// <param name="savePath">Sciezka do zapisu</param>
   /// <param name="sc">Kolekcja do zapisu</param>
   public void SaveToFile(string savePath)
   {
         if (!String.IsNullOrEmpty(savePath) && _scTemp != null)
         {
              StreamWriter writer = null;
              try
              {
                  foreach (string s in _scTemp)
                  {
                      if (writer == null)
                      {
                          writer = new StreamWriter(savePath, false, Encoding.UTF8);
                      }

                      writer.WriteLine(s);
                 }
             }
             catch (IOException)
             {
                   throw;
             }
             catch (Exception)
             {
                  throw;
             }
             finally
             {
                  if (writer != null)
                  {
                       writer.Close();
                  }
             }
        }
   }
    #endregion

    #region Help Methods

    /// <summary>
    /// Metoda umozliwiajaca zapis kluczy do pliku
    /// </summary>
    /// <param name="savePath">Sciezka do zapisu</param>
    public void SaveKeysToFile(string savePath)
    {
         StreamWriter writer = null;
         try
         {
             writer = new StreamWriter(savePath, false, Encoding.UTF8);
             writer.WriteLine("Klucz publiczny:");
             writer.WriteLine("KP: (" + _e.ToString() + "; "
                           + _n.ToString() + ")");
             writer.WriteLine("Klucz prywatny:");
             writer.WriteLine("KPr: ( d=" + _d.ToString() + "; n="
                           + _n.ToString() + ")");
         }
         catch (IOException)
         {
              throw;
         }
         catch (Exception)
         {
              throw;
         }
         finally
         {
                if (writer != null)
                {
                    writer.Close();
                }
         }
    }

    /// <summary>
    /// Metoda pomocnicza szyfrujaca kody znakow specjalnych
    /// </summary>
    /// <returns>Tablica kodow znakow specjalnych</returns>
    private BigInt[] GetCipheredSpecialChar()
    {
         BigInt[] biArray = new BigInt[_chars.Length];
         for (int i = 0; i < _chars.Length; i++)
         {
             biArray[i] = BigInt.Parse(_chars[i].GetNumberOfChar()
                                 .ToString()).FastModuloPower(_e, _n);
         }

         return biArray;
    }
    #endregion
}

Konwersja liczb binarny => dziesiętny i nauka po fakcie. ;)

W swoim projekcie z algorytmów genetycznych potrzebowałem przekonwertować liczbę zapisaną w systemie binarnym na system dziesiętny.
Liczba ta była przechowywana w tablicy typu int.

Utworzyłem taką oto funkcję konwertującą liczby z systemu binarnego na dziesiętny „na piechotę” ;):

public int ConvertFromBinaryToInt(int[] binaryNum)
{
    //Wykladnik potegi
    int power = 0;
    //Aktualna wartosci
    int value = 0;
    //Czytam zawartosc tablicy od konca
    for (int i = binaryNum.Length - 1; i >= 0; i--)
    {
         //Jesli napotkam w tablicy 1
         if (binaryNum[i] == 1)
         {
             //value + 2^power
             value += (int)Math.Pow(2, power);
         }
         power++;
     }

     return value;
}

Zorientowałem się jednak po napisaniu powyższej metody że można to osiągnąć w trywialny sposób:

public int ConvertFromBinaryToInt(int[] binaryNum)
{
  object[] objArray = new object[binaryNum.Length];
  binaryNum.CopyTo(objArray, 0);
  //lacze zawartosc tablicy
  //object[] w jeden string
  string str = String.Concat(objArray);

 //Konwersja na system
 //dziesietny z dwojkowego
  return Convert.ToInt32(str, 2);
}

Chyba każdemu zdarzyło się napisać coś, a dopiero później dojść do wniosku, że można było to osiągnąć w dużo prostszy sposób. ;)