суббота, 9 апреля 2011 г.

Жучка или Отчет о поиске 2

С целью повысить удобство пользования Жучкой я решил написать web-версию с использованием Google App Engine в качестве фреймворка.

Нельзя сказать, что разработка отняла слишком много времени, но некоторые вещи потребовали дополнительного внимания. Результат можно посмотреть на search-report.appspot.com.

Консольная версия тоже претерпела улучшения - теперь достаточно только файла с поисковыми фразами.

Интересно, что развернутая на appspot версия дает худшие результаты, чем развернутая локально (больше вероятность того, что приложение будет распознано как робот).

Пример выполнения на localhost (dev_appserver).


Пример выполнения на appspot.


Как видно, Гугл и Яндекс отклонили запросы приложения, развернутого на appspot (хотя так происходит не всегда), что весьма неприятно, но в любом случае можно использовать локальную (консольную или поднять у себя сервер) версию - все же это лучше, чем составлять отчет о поиске вручную :).

пятница, 1 апреля 2011 г.

Extreme Programming Night 2011

Совсем недавно завершился конкурс Extreme Programming Night, в котором удалось достичь скромного, но результата (4 место).





Фото с конкурса можно посмотреть в этом альбоме.

Немного об отборочном этапе: чтобы попасть в финал, необходимо было разработать маленькую игрушку Pathman.

Чуть ниже скриншот и видео моей версии решения.




Релиз для Win32 можно скачать отсюда.
Исходный код в открытом доступе.

четверг, 31 марта 2011 г.

Отчет о поиске для сайта магистра

Всем, кто разрабатывает собственный сайт на портале магистров ДонНТУ, необходимо составить отчет о поиске.

Что это такое? Это информация о количестве документов, найденных разными поисковиками по ключевым фразам, касающимся магистерской работы и выводы.

Вводить вручную запросы в поисковик и потом опять же вручную заполнять таблицу как-то не охота, поэтому было решено автоматизировать этот процесс. Результат - небольшое приложение Juchka на Python. Узнав, что мой одногруппник разрабатывает нечто похожее, я решил залить Жучку в открытый репозитарий с целью обмена опытом и, возможно, совместной доработки.

Ссылка: http://www.assembla.com/code/juchka/subversion/nodes

На данный момент приложением уже можно пользоваться - мануал в readme.txt.

P. S. Для тех, кто никогда не пользовался SVN - это не сложно - погуглите "TortoiseSVN Export" (если используете Windows, или "svn export" иначе).

четверг, 17 марта 2011 г.

Проблема "точки и запятой" при преобразовании строки в число с плавающей точкой в .NET (C#)

Достаточно часто возникает задача преобразовать string в float/double. При этом разработчик сталкивается с проблемой "точки и запятой" (разделителя целой и дробной частей десятичной дроби). Задача, в принципе, тривиальная, но все-таки хотелось бы рассмотреть некоторые моменты поподробнее.

Какие есть варианты ее решения?

1) заменить запятую на точку и распарсить:

public static float AsFloat1(this string s)
{
 return float.Parse(s.Replace(",", "."), CultureInfo.InvariantCulture);
}

2) попробовать распарсить с использованием ожидаемой (или как вариант - текущей) локали, и если что не так (FormatException), попробовать распарсить в инвариантной локали (InvariantCulture).

public static float AsFloat2(this string s)
{
 try
 {
  return float.Parse(s, CultureInfo.GetCultureInfo("uk"));
 }
 catch (FormatException)
 {
  return float.Parse(s, CultureInfo.InvariantCulture);
 }
}

3) "более правильный 1-й вариант": заменять запятую не на точку, а на символ, определенный в качестве разделителя в инвариантной локали:

public static float AsFloat3(this string s)
{
 return float.Parse(
  s.Replace(",", CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator),
  CultureInfo.InvariantCulture);
}

Какой же из вариантов наиболее подходящий?

С одной стороны 2-й вариант кажется весьма логичным: от пользователя ожидаются данные в формате, определяемом его локалью (явно, например, через настройки приложения, или неявно), и если что не так, то пытаемся использовать инвариантную локаль.

Но с другой стороны: как у этих методов с производительностью?

private static void Main()
{
 var strings = new[] {"12,3", "12.3"};
 var functions = new Func<string, float>[]
      {
       x => x.AsFloat1(),
       x => x.AsFloat2(),
       x => x.AsFloat3()
      };

 var watch = new Stopwatch();
 var tests = from function in functions
    select new Action<string>(x =>
             {
              watch.Restart();
              var n = function(x);
              watch.Stop();
              Console.WriteLine("String: {0}; Number: {1}; Time: {2} ms.",
                 x, n, watch.Elapsed.TotalMilliseconds);
             });

 var testIndex = 0;
 foreach (var test in tests)
 {
  Console.WriteLine("Method: AsFloat{0}", ++testIndex);
  foreach (var s in strings)
   test(s);
 }
}

Оказывается, что:

Method: AsFloat1
String: 12,3; Number: 12,3; Time: 0,1989 ms.
String: 12.3; Number: 12,3; Time: 0,0018 ms.
Method: AsFloat2
String: 12,3; Number: 12,3; Time: 0,2546 ms.
String: 12.3; Number: 12,3; Time: 44,7575 ms.
Method: AsFloat3
String: 12,3; Number: 12,3; Time: 0,2659 ms.
String: 12.3; Number: 12,3; Time: 0,0032 ms.
Для продолжения нажмите любую клавишу . . .

Поэтому, с точки зрения производительности, наиболее верным есть 1-й вариант (что, в принципе, не удивительно). Хотя 3-й вариант, который не полагается на знание того, что в инвариантной локали разделитель - точка, не сильно отстал и показывает примерно то же время.

Конечно, это не единственные варианты, можно придумать и еще, но в качестве пищи к размышлению, скорее всего, этого достаточно.

воскресенье, 6 марта 2011 г.

"Access volation" при загрузке моделей X-формата (DirectX) в Irrlicht 1.7.2

В некоторых случаях можно столкнуться с ошибкой нарушения прав доступа при чтении памяти (access violation), пытаясь загрузить X-модель в Irrlicht 1.7.2.

device->getSceneManager()->getMesh("Models/model.x");

Где возникает ошибка (первая строка здесь - это 1019 строка в CXMeshFileLoader.cpp):

u32* data = new u32[datasize];
for (u32 j=0; j<datasize; ++j)
 data[j]=readInt();
if (dataformat&0x102) // 2nd uv set
{
 mesh.TCoords2.reallocate(mesh.Vertices.size());
 u8* dataptr = (u8*) data;
 const u32 size=((dataformat>>8)&0xf)*sizeof(core::vector2df);
 for (u32 j=0; j<mesh.Vertices.size(); ++j)
 {
  mesh.TCoords2.push_back(*((core::vector2df*)(dataptr)));
  dataptr += size;
 }
}

Почему возникает ошибка: всего указателем data адресуются datasize*4 байт памяти, при загрузке модели может оказаться, что mesh.Vertices.size()*size больше, чем datasize*4 (в моем случае так и было) - отсюда и ошибка.

Что предлагается:

u32* data = new u32[datasize];
for (u32 j=0; j<datasize; ++j)
 data[j]=readInt();
if (dataformat&0x102) // 2nd uv set
{
 mesh.TCoords2.reallocate(mesh.Vertices.size());
 u8* dataptr = (u8*) data;
 const u32 size=((dataformat>>8)&0xf)*sizeof(core::vector2df);
 u32 boundary = (datasize << 2)/size;
 for (u32 j=0; j<mesh.Vertices.size() && j<boundary; ++j)
 {
  mesh.TCoords2.push_back(*((core::vector2df*)(dataptr)));
  dataptr += size;
 }
}

Решение, безусловно не идеальное (что произойдет из-за преждевременного завершения цикла в общем случае?), но для всех моделей, которые мне пришлось загружать оказалось подходящим.

В любом случае в ближайшее время сообщу о найденной проблеме сообществу проекта, а там уже пусть решают.

Успешной разработки!

суббота, 22 января 2011 г.

jQuery Validation Plugin и поля формы с одинаковыми именами

Широко используемый плагин Validation для jQuery имеет одну не совсем приятную особенность: имена полей должны быть уникальными, иначе восприниматься будет только первое из всех полей с одинаковым именем.

Это следует учитывать, создавая, например, формы с переменным количеством однотипных полей. В этом случае проблема легко решается, например, генерацией уникальных имен (например, в качестве постфикса к имени можно использовать текущее время) и заданием одинакового класса.

воскресенье, 19 декабря 2010 г.

ASP.NET MVC 2: Controller.File

Тем, кто решит вернуть пользователю файл с кириллическим именем, следует учесть, что такое имя будет воспринято только Firefox'ом (тестировал на версии 3.6), Chrome (тестировал на версии  8.0) и IE (тестировал на версии 8.0) заменят имя на имя действия контроллера.

В принципе, поведение объяснимо.

Пример.

public ActionResult Plain(string text)
{
   File(text.ToBytes(), "text/plain", "Текст.txt");
}

Файрфокс отобразит в диалоге сохранения файла: "Текст.txt", Хром же, как и Эксплорер, отобразят "Plain.txt".

Как вариант решения может быть использована транслитерация.

Пример.

public static class TranslitExtensions
{
    private static readonly Dictionary<string, string> letters 
        = new Dictionary<string, string>
        {
            {"№", "N"},
            {"а", "a"},
            {"б", "b"},
            {"в", "v"},
            {"г", "g"},
            {"д", "d"},
            {"е", "e"},
            {"ё", "yo"},
            {"ж", "zh"},
            {"з", "z"},
            {"и", "i"},
            {"й", "j"},
            {"к", "k"},
            {"л", "l"},
            {"м", "m"},
            {"н", "n"},
            {"о", "o"},
            {"п", "p"},
            {"р", "r"},
            {"с", "s"},
            {"т", "t"},
            {"у", "u"},
            {"ф", "f"},
            {"х", "h"},
            {"ц", "c"},
            {"ч", "ch"},
            {"ш", "sh"},
            {"щ", "sch"},
            {"ъ", "j"},
            {"ы", "i"},
            {"ь", "j"},
            {"э", "e"},
            {"ю", "yu"},
            {"я", "ya"},
            {"А", "A"},
            {"Б", "B"},
            {"В", "V"},
            {"Г", "G"},
            {"Д", "D"},
            {"Е", "E"},
            {"Ё", "Yo"},
            {"Ж", "Zh"},
            {"З", "Z"},
            {"И", "I"},
            {"Й", "J"},
            {"К", "K"},
            {"Л", "L"},
            {"М", "M"},
            {"Н", "N"},
            {"О", "O"},
            {"П", "P"},
            {"Р", "R"},
            {"С", "S"},
            {"Т", "T"},
            {"У", "U"},
            {"Ф", "F"},
            {"Х", "H"},
            {"Ц", "C"},
            {"Ч", "Ch"},
            {"Ш", "Sh"},
            {"Щ", "Sch"},
            {"Ъ", "J"},
            {"Ы", "I"},
            {"Ь", "J"},
            {"Э", "E"},
            {"Ю", "Yu"},
            {"Я", "Ya"}
        };

    /// <summary>
    /// Performs translation from Russian to Translit.
    /// </summary>
    /// <param name="russian">String in Russian to translate.</param>
    /// <returns>String in Translit.</returns>
    public static string ToTranslit(this string russian)
    {
        var builder = new StringBuilder(russian);

        foreach (var letter in letters)
        {
            builder.Replace(letter.Key, letter.Value);
        }

        return builder.ToString();
    }
}

public ActionResult Plain(string text)
{
   File(text.ToBytes(), "text/plain", "Текст.txt".ToTranslit());
}