Хэл Фултон - Программирование на языке Ruby стр 23.

Шрифт
Фон

Листинг 2.1. Специализированное сравнение строк

class String

alias old_compare <=>

def <=>(other)

a = self.dup

b = other.dup

# Удалить знаки препинания.

a.gsub!(/[\,\.\?\!\:\;]/, "")

b.gsub!(/[\,\.\?\!\:\;]/, "")

# Удалить артикли из начала строки.

a.gsub!(/^(a |an | the )/i, "")

b.gsub!(/^(a |an | the )/i, "")

# Удалить начальные и хвостовые пробелы.

a.strip!

b.strip!

# Вызвать старый метод <=>.

# a.old_compare(b)

end

end

title1 = "Calling All Cars"

title2 = "The Call of the Wild"

# При стандартном сравнении было бы напечатано "yes".

if title1 < title2

puts "yes"

else

puts "no" # А теперь печатается "no".

end

Обратите внимание, что мы "сохранили" старый метод <=> с помощью ключевого слова alias и в конце вызвали его. Если бы мы вместо этого воспользовались методом <, то был бы вызван новый метод <=>, что привело бы к бесконечной рекурсии и в конечном счете к аварийному завершению программы.

Отметим также, что оператор == не вызывает метод <=> (принадлежащий классу-примеси Comparable). Это означает, что для специализированной проверки строк на равенство пришлось бы отдельно переопределить метод ==. Но в рассмотренном случае в этом нет необходимости.

Допустим, что мы хотим сравнивать строки без учета регистра. Для этого есть встроенный метод casecmp; надо лишь добиться, чтобы он вызывался при сравнении. Вот как это можно сделать:

class String

def <=>(other)

casecmp(other)

end

end

Есть и более простой способ:

class String

alias <=> casecmp(other)

end

Но это не все. Надо еще переопределить оператор ==, чтобы он вел себя точно так же:

class String

def ==(other)

casecmp(other) == 0

end

end

Теперь все строки будут сравниваться без учета регистра. И при всех операциях сортировки, которые определены в терминах метода <=>, регистр тоже не будет учитываться.

2.8. Разбиение строки на лексемы

Метод split разбивает строку на части и возвращает массив лексем. Ему передаются два параметра: разделитель и максимальное число полей (целое).

По умолчанию разделителем является пробел, а точнее, значение специальной переменной $; или ее англоязычного эквивалента $FIELD_SEPARATOR. Если же первым параметром задана некоторая строка, то она и будет использоваться в качестве разделителя лексем.

s1 = "Была темная грозовая ночь."

words = s1.split # ["Была", "темная", "грозовая", "ночь]

s2 = "яблоки, груши, персики"

list = s2.split(", ") # ["яблоки", "груши", "персики"]

s3 = "львы и тигры и медведи"

zoo = s3.split(/ и /) # ["львы", "тигры", "медведи"]

Второй параметр ограничивает число возвращаемых полей, при этом действуют следующие правила:

1. Если параметр опущен, то пустые поля в конце отбрасываются.

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

3. Если параметр - отрицательное число, то количество возвращаемых полей не ограничено, а пустые поля в конце сохраняются.

Ниже приведены примеры:

str = "alpha,beta,gamma,,"

list1 = str.split(",") # ["alpha","beta","gamma"]

list2 = str.split(",",2) # ["alpha", "beta,gamma,,"]

list3 = str.split(",",4) # ["alpha", "beta", "gamma", ","]

list4 = str.split(",",8) # ["alpha", "beta", "gamma", "", "")

list5 = str.split(",",-1) # ["alpha", "beta", "gamma", "", ""]

Для сопоставления строки с регулярным выражением или с другой строкой служит метод scan:

str = "I am a leaf on the wind..."

# Строка интерпретируется буквально, а не как регулярное выражение.

arr = str.scan("а") # ["а","а","а"]

# При сопоставлении с регулярным выражением возвращаются все соответствия.

arr = str.scan(/\w+/) # ["I", "am", "a", "leaf", "on", "the",

"wind"]

# Можно задать блок.

str.scan(/\w+/) {|x| puts x }

Класс StringScanner из стандартной библиотеки отличается тем, что сохраняет состояние сканирования, а не выполняет все за один раз:

require 'strscan'

str = "Смотри, как я парю!"

ss = StringScanner.new(str)

loop do

word = ss.scan(/\w+/) # Получать по одному слову.

break if word.nil?

puts word

sep = ss.scan(/\W+/) # Получить следующий фрагмент,

# не являющийся словом.

break if sep.nil?

end

2.9. Форматирование строк

В Ruby, как и в языке С, для этой цели предназначен метод sprintf. Он принимает строку и список выражений, а возвращает строку. Набор спецификаторов в форматной строке мало чем отличается от принятого в функции sprintf (или printf) из библиотеки С.

name = "Боб"

age =28

str = sprintf("Привет, %s... Похоже, тебе %d лет.", name, age)

Спрашивается, зачем нужен этот метод, если можно просто интерполировать значения в строку с помощью конструкции #{expr}? А затем, что sprintf позволяет выполнить дополнительное форматирование - например, задать максимальную ширину поля или максимальное число цифр после запятой, добавить или подавить начальные нули, выровнять строки текста по левой или правой границе и т.д.

str = sprintf("%-20s %3d", name, age)

В классе String есть еще метод %, который делает почти то же самое. Он принимает одно значение или массив значений любых типов:

str = "%-20s %3d" % [name, age] # To же, что и выше

Имеются также методы ljust, rjust и center; они принимают длину результирующей строки и дополняют ее до указанной длины пробелами, если это необходимо.

str = "Моби Дик"

s1 = str.ljust(12) # "Моби Дик"

s2 = str.center(12) # " Моби Дик "

s3 = str.rjust(12) # " Моби Дик"

Можно задать и второй параметр, который интерпретируется как строка заполнения (при необходимости она будет урезана):

str = "Капитан Ахав"

s1 = str.ljust(20,"+") # "Капитан Ахав++++++++"

s2 = str.center(20,"-") # "----Капитан Ахав----"

s3 = str.rjust(20,"123") # "12312312Капитан Ахав"

2.10. Строки в качестве объектов ввода/вывода

Помимо методов sprintf и scanf, есть еще один способ имитировать ввод/вывод в строку: класс StringIO.

Из-за сходства с объектом IO мы рассмотрим его в главе, посвященной вводу/выводу (см. раздел 10.1.24).

Ваша оценка очень важна

0
Шрифт
Фон

Помогите Вашим друзьям узнать о библиотеке