суббота, 28 апреля 2012 г.

Cygwin: grep взбунтовался?

Интересные вещи творятся в GNU grep 2.5.4. С мест сообщают - grep -o - тормозит как неизвестно что.


Поэтому надо писать так:


gawk '{match($0, "REGEXP") ; if (RSTART) print substr($0, RSTART, RLENGTH);}'


А желающие могут сравнить:

#!/bin/sh
for x in {1..10000} ; do echo {a..z} @where=gdetotam@ {a..z}{a..z} ; done > tmp.txt
export LC_ALL=C
time cat tmp.txt | grep -o "@where=[^@]\+@" > /dev/null #медленно, 2 секунды
time cat tmp.txt | grep "@where=[^@]\+@" > /dev/null #быстро, 0.1 секунды
time cat tmp.txt | awk '{match($0, "@where=[^@]+[@]") ; if (RSTART) print substr($0, RSTART, RLENGTH);}' > /dev/null
На моём Cygwin с grep 2.5.1 - всё ОК.

Cygwin: Ловим ошибки

Не забываем добавлять в начало bash-скрипта (сразу после адреса):

set -euo pipefail

чтобы увидеть ошибки в коде, переменных и пайпах.

А вот ошибки в функциях нам так и не покажут.

C#: Number Range в Combobox

Например, нам нужно положить в telerik-оподобный control список из вот таких элементов:
< telerik:radcomboboxitem runat="server" text="2010" value="10" >

Такие штуки надо генерировать в одну строку:
private const int CC_VALID_YEARS_RANGE = 9;
....................................................................................
ddlYear.DataSource = Enumerable.Range(DateTime.Today.Year, CC_VALID_YEARS_RANGE)
                               .ToDictionary(item => item.ToString(),
                                             item => (item % 1000).ToString());
ddlYear.DataTextField = "Key";
ddlYear.DataValueField = "Value";
ddlYear.DataBind();
А если надо добавить ещё одну строку Year с пустым значением - нужно предварительно сбросить Dictionary в ToList() (получится List < KeyValuePair < string, string > >) и сделать insert в 0-ой индекс. Дело в том, что Dictionary<> сам по себе не сортируется - соответственно, Insert-а в нём нет и foreach перебирает его в том же порядке, в каком элементы добавились.
private const int CC_VALID_YEARS_RANGE = 9;
....................................................................................
List < nKeyValuePair < string, string > > yearsList =
              Enumerable.Range(DateTime.Today.Year, CC_VALID_YEARS_RANGE)
                        .ToDictionary(item => item.ToString(),
                                      item => (item % 1000).ToString()).ToList();
yearsList.Insert(0, new KeyValuePair < string,string > ("Year", String.Empty));
ddlYear.DataSource = yearsList;
ddlYear.DataTextField = "Key";
ddlYear.DataValueField = "Value";
ddlYear.DataBind();

Cygwin: Резервное копирование

Умеренно универсальный скрипт для резервного копирования. На входе - директория. Можно добавить дополнительные параметры. На выходе - она же, запакованная в tar.gz.

#!/bin/sh
BackUpDir="/cygdrive/D/Backup"
if ([ $1 ]) then
  BackUpDir=$1
fi
if ([ ! -d $BackUpDir ]) then
  mkdir -pv $BackUpDir
fi
if ([ $2 ]) then
 if ([ -d $2 ]) then
  From=$2
 else
  From=`dirs`
 fi
fi
ArchiveName="$BackUpDir/"`date +"%d.%m.%y(%H.%M)"``echo "$From" | sed 's!^\(/\?.*\)*/\(.*\)/\?$!\2!g'`".tar"
tar -cf "$ArchiveName" *
gzip $ArchiveName

Cygwin: Лучше, чем у Лекса Кравецкого

Во время оно lex-kravetski был не только коммунистом, но ещё и программистом. Например, написал большой пост о том, как конвертировать wav в mp3 консольным конвертером и 2 BAT-файлами.

При всем нашем уважении к пакетным файлам, то же самое на cygwin выглядит не в пример лучше, лаконичней и помещается в 1 скрипт:

#!/bin/sh
LameDir="C:/lame"
if ([ $1 ]) then
 Artist="$1"
else
 echo -n "Artist="
 read Artist
fi
if ([ $2 ]) then
 Genre="$2"
else
 echo -n "Genre="
 read Genre
fi
CurrentDir=`dirs`
ParamAlbum=`echo $CurrentDir | sed 's!^\(/\?.*\)*/\(\([0-9]\{4\}\)[ -]\+\)\?\(.*\)$!--ty "\3" --tl "\4"!g'` #RegExp: ^(/.*)*/(([0-9]{4})[ -]+)?(.*)$
for filename in *.wav
do
  FileShortName=`echo $filename | sed 's!\^(.*\).wav$!\1!g'` #RegExp: ^(.*).wav$
  ParamTrack=`echo $FileShortName | sed 's!^\([0-9]\{2\}\)[ -.]\+\(.*\)!--tn "\1" --tt "\2"!g'` #RegExp: ^([0-9]{2})[ -.]+(.*)
  echo "$LameDir/lame.exe -V2 \"$CurrentDir/$FileShortName.wav\" \"$CurrentDir/$FileShortName.mp3\" --pad-id3v2 --ta=\"$Artist\" $ParamAlbum $ParamTrack --tg=\"$Genre\""
done

Как использовать?
  1. Сохраняем скрипт в нашу директорию со скриптами под именем tomp3 (или ещё каким-нибудь). Во второй строке указываем директорию, куда мы распаковали Lame.
  2. Копируем файлы с CD в wav в таком виде:

    2009 - Album / 01 - Song.wav

    Или в другом:

    Album/01 - Song with a long-ling name.wav

    (год может быть и пустым)

  3.  Открываем cygwin И переходим в папку с Wav-ками
  4. Набираем SH ~/tomp3 "Artist" "Genre". Если без них - скрипт сам спросит.
  5. После завершения кодирования: rm *.wav
  6. И quit
К сожалению, скрипта с подсветкой синтаксиса для bash-скриптов у меня нет. Поэтому я подсветил как brush:perl. Получилось ярко и доступно.

BAT: Неизвестные факты из жизни пакетных файлов

Многие слышали про bat-файлы. Но не все знают что:

  1. Помимо BAT, были ещё CMD, WSH (Windows Script Host с поддержкой аж двух языков - JScript и VBScript), а чуть позже появился Power Shell. JScript и VBScript интересны тем, что были намного сложнее и непонятнее BAT. Но и на них писали.
  2. BAT работали ещё в DOS-е, поэтому его выполняет command.com. А вот CMD - это более новый формат, его выполняет cmd.exe. Поэтому лучше использовать расширение CMD.
  3. BAT и CMD не понимают вложенный if (то есть if может быть только на 1 уровне). Поэтому чтобы сделать условия, приходится рисовать блок-схему и расставлять везде GO TO. Да-да, ту самую блок-схему, которую ещё во времена "Мифического человеко-месяца" рисовали уже после завершения проекта.
  4. @echo off нужно, чтобы bat-ник не писал вызовы на экран.
  5. rem - это строка комментария.
  6. Если написать в файле script1.bat строку script2 (причём script2.bat существует - т.е. мы вызываем его как команду), то script2.bat запустится, а script1.bat - прекратит выполнение. Чтобы не прекращал, надо писать EXEC script2.
  7. Переход в директорию, где лежит скрипт - cd %~dp0.
  8. Можно поставить cygwin и наслаждаться *nix-овой командной строкой под Windows. Кстати, там можно вкладывать сколько угодно If-ов. А ещё можно поставить ActivePerl. К сожалению, писать скрипт прямо в командной строке у Perl под Windows не получится - не такой стандарт для кавычек.
  9. Параметры командной строки лежат в переменных  %1..%9. %0 - имя скрипта (без расширения). %10 и больше - нет.
  10. Включать-выключать сервисы из пакетных файлов - это просто:  Проверить:
    SC QUERY "XService" | find "RUNNING"
    IF not "%ERRORLEVEL%" == "0" GOTO StartService
    ................
    :StartService
    
     Запустить:
    NET START "XService"
    
     Остановить:
    NET STOP "XService"
    
  11. Откомпилировать и запустить пример для wxWidget:
    • Cоздаём в каталоге, прописанном в AUTOEXEC, вот такой makesample.cmd
      @echo off
      make -f makefile.bcc
      for %%f in (*.exe) do start %%f
      exit
      
    • Заходим в каталог с файлами
    • Запускаем (нужно, чтобы был установлен компилятор make)

пятница, 27 апреля 2012 г.

C++: Вопросы на собеседовании

"Вопросы на собеседовании по С++" - один из самых популярных запросов, по которым находят этот блог.

А значит, настало время расширить этот постинг.

Т.к. такие списки периодически пропадают из Сети, я буду делать свой - с приложенными ответами.

Итак, понеслось:
Напоследок - картинка от jia3ep-а.

среда, 18 апреля 2012 г.

Life: Bug

Поиск и устранение бага - 5 часов рабочего времени. Чтобы исправить, нужно было в одном месте заменить + на -.

четверг, 12 апреля 2012 г.

Sitecore: Не искать ли через SQL?

ASP.Net: Ловим Enter на TextBox

Как сделать, чтобы при нажатии Enter на TextBox "нажималась" определённая кнопка? Некоторые до сих пор рекомендуют безумный способ с добавлением невидимого TextBox. Но правильный вариант - это дописывать в Page_Load:

textBox.Attributes.Add("onkeydown", "if(event.which || event.keyCode){if ((event.which == 13) || (event.keyCode == 13)) {document.getElementById('" + buttonDoh.ClientID + "').click();return false;}} else {return true};");

C#: Собираем вместе команды и транзакции

Если нужно отправить в базу много команд - надо сразу делать вот так:

List < sqlcommand >  sqlCommands = new List < sqlcommand > ();
foreach (ParsedAddress parsedAddress in parsedAddresses)
{
    if (parsedAddress.IsError)
        continue;

    SqlCommand sqlCommand = new SqlCommand(sqlcmd_SAVE_PARSED_VALUES);
    sqlCommand.Parameters.Add(new SqlParameter("Param1", "Param1"));
    SqlParameter NullableParameter = new SqlParameter("NullableParameter", SqlDbType.Int);
    NullableParameter.IsNullable = true;
    if (itemToWrite.NullableValue != null)
        NullableParameter.Value = itemToWrite.NullableValue.ID;
    else
        NullableParameter.Value = DBNull.Value;
    sqlCommand.Parameters.Add(NullableParameter);
    sqlCommand.Parameters.Add(new SqlParameter("Param2", "Param2"));
    sqlCommand.Parameters.Add(new SqlParameter("Param3", 13));
    sqlCommands.Add(sqlCommand);
}
SqlConnection connection = new SqlConnection(connectionStringWrite);
connection.Open();
SqlTransaction transaction = connection.BeginTransaction();
try
{
    foreach (SqlCommand cmd in sqlCommands)
    {
        cmd.Connection = connection;
        cmd.Transaction = transaction;
        cmd.ExecuteNonQuery();
        cmd.Dispose();
    }
    transaction.Commit();
}
catch (SqlException sqlError)
{
    transaction.Rollback();
    throw sqlError;
}
finally
{
    connection.Close();
}

Тогда в случае exception-а произойдёт Rollback и ничего не случится.

А Exception уйдёт "наверх", где его можно будет обработать.

среда, 11 апреля 2012 г.

C#: List<> в DataGrid

Когда-то List<> скидывали в DataGrid вот так:

dataGridView.DataSource = null;
dataGridView.DataSource = list;

Но увы - если загружать list таким образом, в ViST2008 мы будем получать IndexOutOfRangeException каждый раз, когда попытаемся его выделить. :(

Поэтому надо привязывать BindingList, который умеет обновляться автоматически. А обновлять только ширину колонок:

dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader);