Настройка табуляции в MS Visual Studio

Всем программистам известно, что правильное форматирование облегчает чтение кода. И расстановка отступов — одна из самых важных задач при форматировании текстов программ. Отступы позволяют увидеть общую структуру кода, просто просмотрев его «по диагонали». Благодаря им можно быстро перейти к нужному блоку. Думаю, никто с этим спорить не будет. Но при форматировании кода иногда возникают вопросы, не имеющие единственно правильного ответа. Один из таких вопросов — «Что использовать для отступов: символы табуляции или пробелы?».
У символов табуляции свои преимущества:

  • Символы табуляции занимают меньше места в исходном тексте программы. (Правда, в наше время это редко бывает актуальным; скорость процессоров и объём дисков выросли настолько, что для абсолютного большинства программ разница будет просто незаметна.)
  • По отступам, состоящим из символов табуляции, можно быстрее перемещаться. Такие отступы и удалить можно гораздо быстрее. Для этого просто нужно меньше нажатий клавиш.
  • Ширина символа табуляции настраивается в большинстве текстовых редакторов. Т.е. каждый человек, который будет читать Ваш код, сможет настроить, как у него будут отображаться отступы, в соответствии со своими предпочтениями и размером монитора.

Преимущества есть и у пробелов:

  • В программном коде отступ начала строки от левого края экрана зависит не только от уровня вложенности блока, к которому эта строка относится. Иногда нужно выровнять первый символ строки относительно какого-нибудь символа в предыдущей строке. Такая ситуация, как правило, встречается при разбиении одной длинной инструкции на несколько строк. Такую «тонкую» настройку нельзя делать символами табуляции. Для этого вида форматирования подходят только пробелы. Если отступы, показывающие уровень вложенности блока, создаются с помощью символов табуляции, а более «тонкое» выравнивание делается с помощью пробелов, то возникает путаница. Программист должен постоянно внимательно следить, какой символ нужно использовать. Это, мягко говоря, не всем нравится. Особенно остро проблема встаёт перед большими командами разработчиков.
  • Символ табуляции выглядит так же, как один или несколько пробелов, но ведёт себя по-другому. Быстро понять, где в коде пробелы, а где табуляция может быть не просто.

Многие из описанных проблем могут решаться «умными» текстовыми редакторами. Особенно продвинутыми являются редакторы, специально созданные для работы с программным кодом. Если такой редактор «знает» синтаксис языка программирования, он пытается «понять», что именно Вы хотите сделать, и автоматически отформатировать код согласно заданным настройкам. Разумеется, даже самый «умный» редактор кода может ошибаться и неправильно понимать намерения программиста. Но программы этого типа становятся всё лучше и уже помогли множеству программистов сделать их работу комфортнее. Настройки, регулирующие работу с пробелами и символами табуляции, имеют не только текстовые редакторы. Они есть во многих других инструментах для работы с программным кодом (например, в системах контроля версий).

В этом посте я хочу описать настройки MS Visual Studio, регулирующие работу с пробелами и символами табуляции. В первую очередь нужно в главном меню выбрать пункт Tools->Options. Откроется окно Options. В его левой части расположено древовидное меню в нём нужно выбрать пункт TextEditor->AllLanguages->Tabs. Там находятся настройки отступов, которые действуют для всех языков программирования и разметки, поддерживаемых MS Visual Studio. Если требуется изменить настройки только для конкретного языка, то в дереве слева вместо пункта AllLanguages следует выбрать нужный язык (см. рисунок 1).

Рисунок 1

Пункт Tabs существует для каждого языка. Если для разных языков установлены разные параметры табуляции, то на странице AllLanguages->Tabs будет показано сообщение о том, что параметры табуляции для разных текстовых форматов конфликтуют друг с другом. («The tab settings for individual text formats conflict with each other.») После того, как он был выбран, в правой части окна появятся две группы настроек: Indenting и Tab. Все настройки, которые определяют, какого размера должны быть отступы, и из каких символов они должны состоять, находятся в группе Tab. (см. рисунок 2).

Рисунок 2

Рассмотрим подробно каждый параметр из этой группы:

  • Tab size — это размер интервала табуляции. Он задаёт расстояние между позициями табуляции в пробелах. Проще говоря, этот параметр определяет, какому количеству пробелов равен один символ табуляции.
  • Indent size — это размер автоматического отступа в пробелах. Для заполнения указанного размера отступа могут использоваться символы табуляции, символы пробела или оба этих вида символов. Что именно будет использовано, зависит от того, какая из следующих 2 радиокнопок выбрана.
  • Insert spaces — вставлять только пробелы. Когда выбран этот параметр, отступ будет всегда состоять только из пробелов. Причём неважно, каким способом отступ создаётся: его автоматически вставляет текстовый редактор IDE, пользователь нажимает клавишу «Tab» или кнопку «Increase Indent» на панели инструментов.
  • Keep tabs — сохранять символы табуляции. Когда выбран этот параметр, для создания отступа используется максимально возможное число символов табуляции. Символ табуляции вставляет такое число пробелов, которое указано в поле «Tab size». Если размер отступа не кратен размеру интервала табуляции, для заполнения разницы добавляются знаки пробелов.

Ещё одна полезная возможность — показ на месте знаков табуляции и пробела специальных символов. Это позволяет с первого взгляда отличать эти символы друг от друга. Эту настройку можно найти в меню Edit->Advanced->ViewWhiteSpace. Ещё один способ включить/выключить отображение пробельных символов — это использование «горячих клавиш». Последовательное нажатие сочетаний клавиш Ctrl+R, Ctrl+W включит отображение пробельных символов, если оно было выключено. Если отображение пробельных символов было включено, то та же комбинация «горячих клавиш» его выключит. Пробел будет показан как точка на уровне середины строки, а начало каждого интервала табуляции будет обозначено стрелочкой (см. рисунок 3).

Рисунок 3

Я использовал MS Visual Studio 2015, но показанные настройки должны работать и в более новых, и в более старых версиях.

Подсчёт частоты вхождения слов в текст

Сегодня я покажу как написать программу на языке высокого уровня, которая подсчитывает частоту вхождения слов в текст. Для этого воспользуемся языком Ruby. Он очень хорошо подходит для работы с текстом.
Идея программы очень проста. Мы хотим определить сколько раз каждое слово встречается в тексте. Для этого прежде всего нужно определиться со структурой данных, в которую мы будем записывать результаты вычислений. Лучше всего, если не придётся создавать временных контейнеров в оперативной памяти для промежуточных вычислений. То есть промежуточные результаты и результат финальный должны храниться в одном месте и в одинаковом виде. Во всех современных языках программирования есть структуры данных, которые позволяют хранить данные в формате «ключ»-«значение». И «ключ», и «значение» могут быть любого типа. Во многих языках программирования такие структуры данных называются ассоциативными массивами (например, в PHP). В Ruby они называются хэшами. Такая структура данных лучше всего подходит для наших целей. Ключом будет являться слово, а значением — количество его вхождений в текст. Когда программа будет находить в тексте новое слово, в хэш будет добавляться новая пара «ключ»-«значение». А если программа встречает в тексте слово, которое уже добавлено в хэш, то нужно всего лишь прибавить 1 к «значению» соответствующего ключа. Описанная структура данных схематически изображена на рисунке 1.

Рисунок 1

Теперь, когда мы определились со структурой данных, нужно подумать об алгоритме. Думаю, что проще всего сразу же показать блок-схему. На ней все шаги алгоритма показаны подробно и без привязки к Ruby или какому-либо языку программирования.

Рисунок 2

Одновременно с подсчётом частоты вхождения мы вычисляем максимальную частоту вхождения слов в текст, а также сколько слов имеют эту частоту.
Поскольку приведённая схема не привязана к какому-либо языку программирования, в блоках проверки условий используется обычное «=» из математики. При реализации алгоритма на конкретном языке программирования важно заменить его на оператор сравнения, который используется в этом языке. В Ruby используется C-подобная форма оператора сравнения: «==». А «=» обозначает оператор присваивания.
Вот код программы на Ruby, которая реализует этот алгоритм:

begin
  frequency_table=Hash.new

  puts 'Enter name of the file:'
  file_name = gets.chomp!
  data = IO.read(file_name)
  words=data.scan(/[A-Za-z]+(?:'s)?/)

  words.each do |word|
    if (frequency_table.include? word.downcase) then
      frequency_table[word.downcase]+=1
    else
      frequency_table.[]=(word.downcase,1)
    end
  end
  max_word_len=0
  counter=0
  puts "Only words more frequent than 'limit' will be shown. Enter 'limit':"
  limit = gets.to_i
  frequency_table.each do |key, value|
    if value>max_word_len then
      max_word_len=value
      counter=1 #Начинаем подсчёт заново
    else
      if value==max_word_len then
        counter+=1
      end
    end
    if (value > limit) then print "#{key}: #{value}\n" end
  end
  puts "The highest frequency is #{max_word_len}. Words which have this frequency repeats in text #{counter} times."
  gets
rescue=>err
  puts err
  gets
end

Программа полностью соответствует алгоритму, приведённому выше. Отдельно стоит остановится только на регулярном выражении «[A-Za-z]+(?:'s)?». Оно находит последовательности из букв английского алфавита, в которые не вклиниваются другие символы. Регулярное выражение учитывает, что слова могут заканчиваться на «’s». Это позволяет распознавать прилагательные, которые в английском языке обозначают принадлежность. Важно использовать группировку без запоминания: «(?: )». При использовании обычной группировки «( )» метод scan будет работать совсем не так, как ожидается. Не вдаваясь в подробности, скажу, что он не будет искать соответствия регулярному выражению в тексте.
Проверим программу на текстовом файле. Я для этих целей использовал специально созданный файл example.txt. Объём текста в нём совсем небольшой, и работу программы легко можно проверить вручную. У меня содержимое этого файла следующее: «Girl are playing with her dog. … Dog’s name is Snuppy. Girl’s nAme is Katty.». Я внёс в текст некоторые искажения специально, чтобы проверить, как программа с ними справится. На рисунке 3 представлен скриншот работы программы у меня. Программа работает именно так как ожидалось.

Рисунок 3

Чтобы запустить эту программу и опробовать её у себя нужен только установленный интерпретатор Ruby.
Программа, которую мы сегодня рассмотрели, разумеется, очень проста. В частности, она не учитывает морфологию. Разные формы одного слова программа считает разными словами. В следующих постах я покажу как можно её усовершенствовать. Заодно рассмотрим на примере этой программы некоторые полезные приёмы и инструменты для работы с текстами.

Получение курсов валют

Получение курсов валют — задача, которая встречается очень часто. Курсы могут понадобиться чтобы просто показать их на веб-странице. Другой не менее распространённый случай — необходимость пересчитать цену в разных валютах. Какой бы ни была цель, важно чтобы курсы были получены из надёжного источника. Не менее важна их актуальность.

Самый надёжный и актуальный источник курсов валют — сайт Центробанка РФ. Через него можно получить доступ к XML-документу с курсами. Для этого нужно обратиться по URL ‘http://www.cbr.ru/scripts/XML_daily.asp’. В документе будет курсы валют на текущую дату. Также можно добавить к адресу параметр date_req, который позволяет явно указать дату, на которую нужны курсы. Тогда URL будет выглядеть следующим образом: ‘http://www.cbr.ru/scripts/XML_daily.asp?date_req=d.m.Y’. d.m.Y — это формат даты: день.месяц.год.
Кстати, PHP прекрасно понимает такие форматы дат.
XML-документ с курсами валют имеет следующую структуру: внутри корневого элемента находятся тэги <Valute>. Внутри каждого такого тэга находятся тэги, содержащие информацию о конкретной валюте. Для примера приведу скриншот XML-документа:

Рисунок 1

Тэгов внутри каждого элемента <Valute> довольно много, но чаще всего нужны 2 — <CharCode> и <Value>. С первым всё понятно, а второй — это курс валюты к рублю.
Таким образом, чтобы получить нужную нам информацию, надо сначала получить из PHP доступ к корневому XML-элементу, а потом перебрать все вложенные в него элементы <Valute>. Для каждого из них нужно просмотреть содержимое тех тэгов, которые описывают нужные нам свойства валюты.
Получить доступ ко вложенным XML-элементам из PHP совсем не сложно. Элементы можно получить по названию тэга с помощью функции getElementsByTagName(). Она возвращает коллекцию подходящих элементов. Для элементов <Valute> надо просто перебрать эту коллекцию в цикле, как уже говорилось выше. Когда мы ищем элементы, хранящие свойтва наподобие <CharCode>, внутри элемента <Valute>, функция getElementsByTagName() вернёт коллекцию из одного элемента. К нему можно обратиться следущим образом: item(0). Текст внутри тегов XML-элемента хранит свойство объекта nodeValue.
Теперь рассмотрим код примера на PHP:

<html>
<?php
//Xml-документ с курсами валют к рублю с сайта ЦБ РФ
$xml = new DOMDocument();
$url = 'http://www.cbr.ru/scripts/XML_daily.asp?date_req=' . date('d.m.Y');

//Пробуем скопировать xml-документ с курсами валют с сайта ЦБ РФ на наш сервер
$local_exchange_rates = "exchange_rates.xml";
@copy($url, $local_exchange_rates);

//Поддерживаемые валюты и их курс к рублю
$supported_currencies = ['USD' => NULL, 'EUR' => NULL];

//Загружаем xml-документ с курсами
$is_loaded = false;
if (@$xml->load($url))
  $is_loaded = true;
elseif (@$xml->load($local_exchange_rates))
  $is_loaded = true;

//Обновляем курсы поддерживаемых валют
if ($is_loaded)
{
  $root = $xml->documentElement;
  $currencies = $root->getElementsByTagName('Valute');
  foreach ($currencies as $currency)
  {
    $char_code = $currency->getElementsByTagName('CharCode')->item(0)->nodeValue;
    if (array_key_exists($char_code, $supported_currencies))
    {
      $value = $currency->getElementsByTagName('Value')->item(0)->nodeValue;
      $supported_currencies[$char_code] = floatval(str_replace(',', '.', $value));
    }
  }
  foreach ($supported_currencies as $key => $value)
  {
    echo $key.' '.$value.'<br>';
  }
}
else
  echo "Не удалось загрузить курсы валют";
?>
</html>

Стоит обратить внимание на 2 вещи:

  • Мы выводим курсы только для тех валют, которые отметили как поддерживаемые. Для этого мы объявили массив $supported_currencies и поместили туда буквенные коды тех валют, которые хотим отслеживать.
  • При каждом обращении к сайту ЦБ РФ мы сохраняем копию XML-документа локально. Если при следующем обращении к сайту ЦБ РФ он будет недоступен, то мы возьмём данные из локальной копии.
    Рисунок 2

    Эта система очень проста, но далеко не идеальна. Если предыдущее обращение было давно, то пользователь получит неактуальные курсы валют. Более сложный, но и более надёжный способ — вынести код создания локальной копии в отдельную программу и запускать эту программу как задание с определённой периодичностью. Но в этом посте мы не будем рассматривать как это сделать.