класс String
BigDecimal расширяет встроенный класс String, предоставляя метод to_d.
Когда вам нужен BigDecimal в вашем приложении, этот метод будет доступен для объектов String.
Добавления к классу String для нормализации Юникода
Объект String хранит и обрабатывает произвольную последовательность байтов, обычно представляющую символы. Объекты String могут быть созданы с помощью String::new или в качестве литералов.
Из-за проблем с алиасами пользователи строк должны быть осведомлены о методах, которые изменяют содержимое объекта String. Обычно методы с именами, оканчивающимися на «!», изменяют свой получатель, а те, у которых нет «!», возвращают новый объект String. Однако есть исключения, такие как String#[]=.
Общедоступные методы экземпляров
# File lib/rake/ext/string.rb, line 13
def ext(newext='')
return self.dup if ['.', '..'].include? self
newext = (newext =~ /^\./) ? newext : ("." + newext) if newext != ''
self.chomp(File.extname(self)) << newext
end Заменяет расширение файла на newext. Если в строке нет расширения, добавляет новое расширение в конец. Если новое расширение не указано или пустое, удаляет существующее расширение.
ext — это метод, добавленный пользователем для класса String.
Это расширение String из Rake
# File ext/nkf/lib/kconv.rb, line 263 def iseuc; Kconv.iseuc(self) end
Возвращает, является ли кодировка self EUC-JP или нет.
# File ext/nkf/lib/kconv.rb, line 275 def isjis; Kconv.isjis(self) end
Возвращает, является ли кодировка self ISO-2022-JP или нет.
# File ext/nkf/lib/kconv.rb, line 269 def issjis; Kconv.issjis(self) end
Возвращает, является ли кодировка self Shift_JIS или нет.
# File ext/nkf/lib/kconv.rb, line 281 def isutf8; Kconv.isutf8(self) end
Возвращает, является ли кодировка self UTF-8 или нет.
# File ext/nkf/lib/kconv.rb, line 204 def kconv(to_enc, from_enc=nil) from_enc = self.encoding if !from_enc && self.encoding != Encoding.list[0] Kconv::kconv(self, to_enc, from_enc) end
Преобразует self в to_enc. to_enc и from_enc заданы как константы объектов Kconv или Encoding.
# File lib/rake/ext/string.rb, line 135
def pathmap(spec=nil, &block)
return self if spec.nil?
result = ''
spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
case frag
when '%f'
result << File.basename(self)
when '%n'
result << File.basename(self).ext
when '%d'
result << File.dirname(self)
when '%x'
result << File.extname(self)
when '%X'
result << self.ext
when '%p'
result << self
when '%s'
result << (File::ALT_SEPARATOR || File::SEPARATOR)
when '%-'
# do nothing
when '%%'
result << "%"
when /%(-?\d+)d/
result << pathmap_partial($1.to_i)
when /^%\{([^}]*)\}(\d*[dpfnxX])/
patterns, operator = $1, $2
result << pathmap('%' + operator).pathmap_replace(patterns, &block)
when /^%/
fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
else
result << frag
end
end
result
end Преобразует путь согласно заданному описанию. Описание определяет детали преобразования. Признаются следующие специальные шаблоны:
-
%p -
Полный путь.
-
%f -
Основное имя файла пути с расширением файла, но без каких-либо каталогов.
-
%n -
Имя файла пути без расширения файла.
-
%d -
Список каталогов пути.
-
%x -
Расширение файла пути. Пустая строка, если расширения нет.
-
%X -
Всё кроме расширения файла.
-
%s -
Альтернативный разделитель файлов, если определен, иначе используется стандартный разделитель файлов.
-
%% -
Знак процента.
У спецификатора %d также может быть числовой префикс (например, '%2d'). Если число положительное, возвращаются (до) n каталогов в пути, начиная слева. Если число n отрицательное, возвращаются (до) n каталогов справа пути.
Примеры:
'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b'
'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d' Также операторы %d, %p, %f, %n, %x, и %X могут принимать аргумент шаблона/замены для выполнения простых подстановок строк в определенной части пути. Шаблон и замена разделены запятой и заключены в фигурные скобки. Спецификация замены идёт после символа % но перед буквой оператора (например, “%{old,new}d”). Несколько спецификаций замены должны быть разделены точкой с запятой (например, “%{old,new;src,bin}d”).
Для шаблона могут использоваться регулярные выражения, а в тексте замены могут использоваться ссылки на обратные подстроки. Фигурные скобки, запятые и точки с запятой исключаются как из шаблона, так и из текста замены (для сохранения разумной обработки).
Например:
"src/org/onestepback/proj/A.java".pathmap("%{^src,class}X.class")
возвращает:
"class/org/onestepback/proj/A.class"
Если текст замены — «*», то может быть предоставлен блок для выполнения произвольных вычислений для замены.
Например:
"/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
ext.downcase
}
Возвращает:
"/path/to/file.txt"
Это расширение String из Rake
# File lib/rake/ext/string.rb, line 24 def pathmap_explode head, tail = File.split(self) return [self] if head == self return [tail] if head == '.' || tail == '/' return [head, tail] if head == '/' return head.pathmap_explode + [tail] end
Разделяет путь на отдельные компоненты. Используется pathmap.
Это расширение String из Rake
# File lib/rake/ext/string.rb, line 38
def pathmap_partial(n)
dirs = File.dirname(self).pathmap_explode
partial_dirs =
if n > 0
dirs[0...n]
elsif n < 0
dirs.reverse[0...-n].reverse
else
"."
end
File.join(partial_dirs)
end Извлекает частичный путь из пути. Включает n каталогов спереди (слева), если n положительное. Включает |n| каталогов сзади (справа), если n отрицательное.
Это расширение String из Rake
# File lib/rake/ext/string.rb, line 56
def pathmap_replace(patterns, &block)
result = self
patterns.split(';').each do |pair|
pattern, replacement = pair.split(',')
pattern = Regexp.new(pattern)
if replacement == '*' && block_given?
result = result.sub(pattern, &block)
elsif replacement
result = result.sub(pattern, replacement)
else
result = result.sub(pattern, '')
end
end
result
end Выполняет операции замены pathmap на заданном пути. Шаблоны имеют вид 'pat1,rep1;pat2,rep2…'.
Это расширение String из Rake
static VALUE
string_to_c(VALUE self)
{
char *s;
VALUE num;
rb_must_asciicompat(self);
s = RSTRING_PTR(self);
if (s && s[RSTRING_LEN(self)]) {
rb_str_modify(self);
s = RSTRING_PTR(self);
s[RSTRING_LEN(self)] = '\0';
}
if (!s)
s = (char *)"";
(void)parse_comp(s, 0, &num);
return num;
} Возвращает комплекс, который обозначает строковую форму. Парсер игнорирует начальные пробелы и хвостовой мусор. Любые последовательности цифр могут быть разделены подчеркиванием. Возвращает ноль для нулевой или мусорной строки.
'9'.to_c #=> (9+0i) '2.5'.to_c #=> (2.5+0i) '2.5/1'.to_c #=> ((5/2)+0i) '-3/2'.to_c #=> ((-3/2)+0i) '-i'.to_c #=> (0-1i) '45i'.to_c #=> (0+45i) '3-4i'.to_c #=> (3-4i) '-4e2-4e-2i'.to_c #=> (-400.0-0.04i) '-0.0-0.0i'.to_c #=> (-0.0-0.0i) '1/2+3/4i'.to_c #=> ((1/2)+(3/4)*i) 'ruby'.to_c #=> (0+0i)
См. Kernel.Complex.
# File ext/bigdecimal/lib/bigdecimal/util.rb, line 59 def to_d BigDecimal(self) end
Преобразует string в BigDecimal и возвращает его.
require 'bigdecimal' require 'bigdecimal/util' "0.5".to_d # => #<BigDecimal:1dc69e0,'0.5E0',9(18)>
# File ext/nkf/lib/kconv.rb, line 223 def toeuc; Kconv.toeuc(self) end
Преобразует self в EUC-JP
# File ext/nkf/lib/kconv.rb, line 217 def tojis; Kconv.tojis(self) end
Преобразует self в ISO-2022-JP
# File ext/nkf/lib/kconv.rb, line 253 def tolocale; Kconv.tolocale(self) end
Преобразует self в кодировку локали
# File ext/nkf/lib/kconv.rb, line 229 def tosjis; Kconv.tosjis(self) end
Преобразует self в Shift_JIS
# File ext/nkf/lib/kconv.rb, line 241 def toutf16; Kconv.toutf16(self) end
Преобразует self в UTF-16
# File ext/nkf/lib/kconv.rb, line 247 def toutf32; Kconv.toutf32(self) end
Преобразует self в UTF-32
# File ext/nkf/lib/kconv.rb, line 235 def toutf8; Kconv.toutf8(self) end
Преобразует self в UTF-8
scanf
↑ topМетоды публичного класса
static VALUE
rb_str_init(int argc, VALUE *argv, VALUE str)
{
VALUE orig;
if (argc > 0 && rb_scan_args(argc, argv, "01", &orig) == 1)
rb_str_replace(str, orig);
return str;
} Возвращает новый объект строки, содержащий копию str.
static VALUE
rb_str_s_try_convert(VALUE dummy, VALUE str)
{
return rb_check_string_type(str);
} Попытка преобразовать obj в строку, используя метод #to_str. Возвращает преобразованную строку или nil, если obj не может быть преобразован по какой-либо причине.
String.try_convert("str") #=> "str"
String.try_convert(/re/) #=> nil
Публичные методы экземпляра
static VALUE
rb_str_format_m(VALUE str, VALUE arg)
{
VALUE tmp = rb_check_array_type(arg);
if (!NIL_P(tmp)) {
VALUE rv = rb_str_format(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), str);
RB_GC_GUARD(tmp);
return rv;
}
return rb_str_format(1, &arg, str);
} Форматирование—Использует str в качестве спецификации формата и возвращает результат её применения к arg. Если спецификация формата содержит более одной подстановки, то arg должен быть Array или Hash, содержащим значения для подстановки. Подробности о строке формата см. в Kernel::sprintf.
"%05d" % 123 #=> "00123"
"%-5s: %08x" % [ "ID", self.object_id ] #=> "ID : 200e14d6"
"foo = %{foo}" % { :foo => 'bar' } #=> "foo = bar"
VALUE
rb_str_times(VALUE str, VALUE times)
{
VALUE str2;
long n, len;
char *ptr2;
int termlen;
len = NUM2LONG(times);
if (len < 0) {
rb_raise(rb_eArgError, "negative argument");
}
if (len && LONG_MAX/len < RSTRING_LEN(str)) {
rb_raise(rb_eArgError, "argument too big");
}
len *= RSTRING_LEN(str);
termlen = TERM_LEN(str);
str2 = rb_str_new_with_class(str, 0, (len + termlen - 1));
ptr2 = RSTRING_PTR(str2);
if (len) {
n = RSTRING_LEN(str);
memcpy(ptr2, RSTRING_PTR(str), n);
while (n <= len/2) {
memcpy(ptr2 + n, ptr2, n);
n *= 2;
}
memcpy(ptr2 + n, ptr2, len-n);
}
STR_SET_LEN(str2, len);
TERM_FILL(&ptr2[len], termlen);
OBJ_INFECT(str2, str);
rb_enc_cr_str_copy_for_substr(str2, str);
return str2;
} Копирование—Возвращает новую строку, содержащую integer копий исходной строки. integer должно быть больше или равно 0.
"Ho! " * 3 #=> "Ho! Ho! Ho! " "Ho! " * 0 #=> ""
VALUE
rb_str_plus(VALUE str1, VALUE str2)
{
VALUE str3;
rb_encoding *enc;
char *ptr1, *ptr2, *ptr3;
long len1, len2;
int termlen;
StringValue(str2);
enc = rb_enc_check(str1, str2);
RSTRING_GETMEM(str1, ptr1, len1);
RSTRING_GETMEM(str2, ptr2, len2);
termlen = rb_enc_mbminlen(enc);
str3 = str_new0(rb_cString, 0, len1+len2, termlen);
ptr3 = RSTRING_PTR(str3);
memcpy(ptr3, ptr1, len1);
memcpy(ptr3+len1, ptr2, len2);
TERM_FILL(&ptr3[len1+len2], termlen);
FL_SET_RAW(str3, OBJ_TAINTED_RAW(str1) | OBJ_TAINTED_RAW(str2));
ENCODING_CODERANGE_SET(str3, rb_enc_to_index(enc),
ENC_CODERANGE_AND(ENC_CODERANGE(str1), ENC_CODERANGE(str2)));
RB_GC_GUARD(str1);
RB_GC_GUARD(str2);
return str3;
} Конкатенация—Возвращает новую String, содержащую other_str, конкатенированную со строкой str.
"Hello from " + self.to_s #=> "Hello from main"
VALUE
rb_str_concat(VALUE str1, VALUE str2)
{
unsigned int code;
rb_encoding *enc = STR_ENC_GET(str1);
if (FIXNUM_P(str2) || RB_TYPE_P(str2, T_BIGNUM)) {
if (rb_num_to_uint(str2, &code) == 0) {
}
else if (FIXNUM_P(str2)) {
rb_raise(rb_eRangeError, "%ld out of char range", FIX2LONG(str2));
}
else {
rb_raise(rb_eRangeError, "bignum out of char range");
}
}
else {
return rb_str_append(str1, str2);
}
if (enc == rb_usascii_encoding()) {
/* US-ASCII automatically extended to ASCII-8BIT */
char buf[1];
buf[0] = (char)code;
if (code > 0xFF) {
rb_raise(rb_eRangeError, "%u out of char range", code);
}
rb_str_cat(str1, buf, 1);
if (code > 127) {
rb_enc_associate(str1, rb_ascii8bit_encoding());
ENC_CODERANGE_SET(str1, ENC_CODERANGE_VALID);
}
}
else {
long pos = RSTRING_LEN(str1);
int cr = ENC_CODERANGE(str1);
int len;
char *buf;
switch (len = rb_enc_codelen(code, enc)) {
case ONIGERR_INVALID_CODE_POINT_VALUE:
rb_raise(rb_eRangeError, "invalid codepoint 0x%X in %s", code, rb_enc_name(enc));
break;
case ONIGERR_TOO_BIG_WIDE_CHAR_VALUE:
case 0:
rb_raise(rb_eRangeError, "%u out of char range", code);
break;
}
buf = ALLOCA_N(char, len + 1);
rb_enc_mbcput(code, buf, enc);
if (rb_enc_precise_mbclen(buf, buf + len + 1, enc) != len) {
rb_raise(rb_eRangeError, "invalid codepoint 0x%X in %s", code, rb_enc_name(enc));
}
rb_str_resize(str1, pos+len);
memcpy(RSTRING_PTR(str1) + pos, buf, len);
if (cr == ENC_CODERANGE_7BIT && code > 127)
cr = ENC_CODERANGE_VALID;
ENC_CODERANGE_SET(str1, cr);
}
return str1;
} Добавление—Конкатенирует данный объект к str. Если объект является Integer, он рассматривается как кодовый символ и преобразуется в символ перед конкатенацией.
a = "hello " a << "world" #=> "hello world" a.concat(33) #=> "hello world!"
static VALUE
rb_str_cmp_m(VALUE str1, VALUE str2)
{
int result;
if (!RB_TYPE_P(str2, T_STRING)) {
VALUE tmp = rb_check_funcall(str2, rb_intern("to_str"), 0, 0);
if (RB_TYPE_P(tmp, T_STRING)) {
result = rb_str_cmp(str1, tmp);
}
else {
return rb_invcmp(str1, str2);
}
}
else {
result = rb_str_cmp(str1, str2);
}
return INT2FIX(result);
} Сравнение—Возвращает -1, 0, +1 или nil в зависимости от того, меньше ли string, равно ли ей, или больше ли она other_string.
Возвращает nil, если два значения несравнимы.
Если строки имеют разную длину и равны, когда сравниваются до наименьшей длины, то более длинная строка считается больше, чем более короткая.
<=> является основой для методов <, <=, >, >=, и between?, включённых из модуля Comparable. Метод String#== не использует Comparable#==.
"abcdef" <=> "abcde" #=> 1 "abcdef" <=> "abcdef" #=> 0 "abcdef" <=> "abcdefg" #=> -1 "abcdef" <=> "ABCDEF" #=> 1
VALUE
rb_str_equal(VALUE str1, VALUE str2)
{
if (str1 == str2) return Qtrue;
if (!RB_TYPE_P(str2, T_STRING)) {
if (!rb_respond_to(str2, rb_intern("to_str"))) {
return Qfalse;
}
return rb_equal(str2, str1);
}
return str_eql(str1, str2);
} Равенство
Возвращает, равна ли str obj, аналогично Object#==.
Если obj не является экземпляром String, но отвечает на to_str, то две строки сравниваются с использованием равенства по типу Object#===.
В противном случае возвращает аналогично #eql?, сравнивая длину и содержимое.
VALUE
rb_str_equal(VALUE str1, VALUE str2)
{
if (str1 == str2) return Qtrue;
if (!RB_TYPE_P(str2, T_STRING)) {
if (!rb_respond_to(str2, rb_intern("to_str"))) {
return Qfalse;
}
return rb_equal(str2, str1);
}
return str_eql(str1, str2);
} Равенство
Возвращает, равна ли str obj, аналогично Object#==.
Если obj не является экземпляром String, но отвечает на to_str, то две строки сравниваются с использованием равенства по типу Object#===.
В противном случае возвращает аналогично #eql?, сравнивая длину и содержимое.
static VALUE
rb_str_match(VALUE x, VALUE y)
{
if (SPECIAL_CONST_P(y)) goto generic;
switch (BUILTIN_TYPE(y)) {
case T_STRING:
rb_raise(rb_eTypeError, "type mismatch: String given");
case T_REGEXP:
return rb_reg_match(y, x);
generic:
default:
return rb_funcall(y, rb_intern("=~"), 1, x);
}
} Совпадение—Если obj является Regexp, используйте его в качестве шаблона для поиска в str и возвращает позицию начала совпадения, или nil если совпадения нет. В противном случае вызывает obj.=~, передавая str в качестве аргумента. По умолчанию =~ в Object возвращает nil.
Примечание: str =~ regexp не то же самое, что regexp =~
str. Строки, захваченные из именованных групп захвата, присваиваются локальным переменным только во втором случае.
"cat o' 9 tails" =~ /\d/ #=> 7 "cat o' 9 tails" =~ 9 #=> nil
static VALUE
rb_str_aref_m(int argc, VALUE *argv, VALUE str)
{
if (argc == 2) {
if (RB_TYPE_P(argv[0], T_REGEXP)) {
return rb_str_subpat(str, argv[0], argv[1]);
}
return rb_str_substr(str, NUM2LONG(argv[0]), NUM2LONG(argv[1]));
}
rb_check_arity(argc, 1, 2);
return rb_str_aref(str, argv[0]);
} Ссылка на элемент—Если передан один index, возвращает подстроку из одного символа по этому индексу. Если передан start индекс и length, возвращает подстроку, содержащую length символов, начиная с index. Если передан range, его начало и конец интерпретируются как смещения, ограничивающие подстроку, которая должна быть возвращена.
В этих трёх случаях, если индекс отрицательный, он считается с конца строки. Для start и range случаев начальный индекс находится перед символом, а индекс, совпадающий с размером строки. Кроме того, пустая строка возвращается, когда начальный индекс для диапазона символов находится в конце строки.
Возвращает nil если начальный индекс выходит за пределы строки или длина отрицательна.
Если передан Regexp, возвращается совпадающая часть строки. Если за регулярным выражением следует capture, который может быть индексом или именем группы захвата, возвращается соответствующий компонент MatchData.
Если передан match_str, возвращается эта строка, если она встречается в строке.
Возвращает nil если регулярное выражение не соответствует или строка совпадения не найдена.
a = "hello there" a[1] #=> "e" a[2, 3] #=> "llo" a[2..3] #=> "ll" a[-3, 2] #=> "er" a[7..-2] #=> "her" a[-4..-2] #=> "her" a[-2..-4] #=> "" a[11, 0] #=> "" a[11] #=> nil a[12, 0] #=> nil a[12..-1] #=> nil a[/[aeiou](.)\1/] #=> "ell" a[/[aeiou](.)\1/, 0] #=> "ell" a[/[aeiou](.)\1/, 1] #=> "l" a[/[aeiou](.)\1/, 2] #=> nil a[/(?<vowel>[aeiou])(?<non_vowel>[^aeiou])/, "non_vowel"] #=> "l" a[/(?<vowel>[aeiou])(?<non_vowel>[^aeiou])/, "vowel"] #=> "e" a["lo"] #=> "lo" a["bye"] #=> nil
static VALUE
rb_str_aset_m(int argc, VALUE *argv, VALUE str)
{
if (argc == 3) {
if (RB_TYPE_P(argv[0], T_REGEXP)) {
rb_str_subpat_set(str, argv[0], argv[1], argv[2]);
}
else {
rb_str_splice(str, NUM2LONG(argv[0]), NUM2LONG(argv[1]), argv[2]);
}
return argv[2];
}
rb_check_arity(argc, 2, 3);
return rb_str_aset(str, argv[0], argv[1]);
} Присваивание элемента—Заменяет часть или всё содержимое str. Влияющая на строку часть определяется по тем же критериям, что и String#[]. Если строка-замена не имеет такой же длины, как текст, который она заменяет, строка будет соответствующим образом скорректирована. Если регулярное выражение или строка используется в качестве индекса и не соответствует позиции в строке, IndexError возникает. Если используется форма регулярного выражения, необязательный второй Fixnum позволяет указать, какую часть совпадения заменить (эффективно используя правила индексирования MatchData). Формы, принимающие Fixnum, вызовут IndexError если значение находится за пределами диапазона; форма Range вызовет RangeError, а формы Regexp и String вызовут IndexError при отрицательном совпадении.
static VALUE
rb_str_is_ascii_only_p(VALUE str)
{
int cr = rb_enc_str_coderange(str);
return cr == ENC_CODERANGE_7BIT ? Qtrue : Qfalse;
} Возвращает true для строки, содержащей только символы ASCII.
"abc".force_encoding("UTF-8").ascii_only? #=> true
"abc\u{6666}".force_encoding("UTF-8").ascii_only? #=> false
static VALUE
rb_str_b(VALUE str)
{
VALUE str2 = str_alloc(rb_cString);
str_replace_shared_without_enc(str2, str);
OBJ_INFECT_RAW(str2, str);
ENC_CODERANGE_CLEAR(str2);
return str2;
} Возвращает скопированную строку с кодировкой ASCII-8BIT.
# File lib/scanf.rb, line 753
def block_scanf(fstr) #:yield: current_match
fs = Scanf::FormatString.new(fstr)
str = self.dup
final = []
begin
current = str.scanf(fs)
final.push(yield(current)) unless current.empty?
str = fs.string_left
end until current.empty? || str.empty?
return final
end Сканирует текущую строку до тех пор, пока совпадение не исчерпается, вызывая каждый совпадение, когда оно встречается в строке. Блок не нужен, так как результаты просто будут агрегированы в конечный массив.
"123 456".block_scanf("%d")
# => [123, 456]
Если блок предоставлен, значение, возвращаемое из yield, добавляется в выходной массив.
"123 456".block_scanf("%d) do |digit,| # the ',' unpacks the Array
digit + 100
end
# => [223, 556] См. Scanf для получения подробностей о создании строки формата.
Вам необходимо подключить 'scanf', чтобы использовать #block_scanf
static VALUE
rb_str_bytes(VALUE str)
{
return rb_str_enumerate_bytes(str, 1);
} Возвращает массив байтов в str. Это сокращение для str.each_byte.to_a.
Если предоставлен блок, что является устаревшей формой, он работает так же, как each_byte.
static VALUE
rb_str_bytesize(VALUE str)
{
return LONG2NUM(RSTRING_LEN(str));
} Возвращает длину str в байтах.
"\x80\u3042".bytesize #=> 4 "hello".bytesize #=> 5
static VALUE
rb_str_byteslice(int argc, VALUE *argv, VALUE str)
{
if (argc == 2) {
return str_byte_substr(str, NUM2LONG(argv[0]), NUM2LONG(argv[1]));
}
rb_check_arity(argc, 1, 2);
return str_byte_aref(str, argv[0]);
} Ссылка на байты—Если передано одно Fixnum, возвращает подстроку длиной в один байт в указанной позиции. Если переданы два объекта Fixnum, возвращает подстроку, начинающуюся с смещения, заданного первым, и длиной, заданной вторым. Если задан Range, возвращается подстрока, содержащая байты в смещениях, заданных диапазоном. Во всех трёх случаях, если смещение отрицательно, оно отсчитывается с конца str. Возвращает nil если начальное смещение выходит за пределы строки, длина отрицательна или начало диапазона больше конца. Кодировка результирующей строки сохраняет исходную кодировку.
"hello".byteslice(1) #=> "e" "hello".byteslice(-1) #=> "o" "hello".byteslice(1, 2) #=> "el" "\x80\u3042".byteslice(1, 3) #=> "\u3042" "\x03\u3042\xff".byteslice(1..3) #=> "\u3042"
static VALUE
rb_str_capitalize(VALUE str)
{
str = rb_str_dup(str);
rb_str_capitalize_bang(str);
return str;
} Возвращает копию str с первой буквой, преобразованной в верхний регистр, а остальной частью — в нижний. Примечание: преобразование регистра эффективно только в области ASCII.
"hello".capitalize #=> "Hello" "HELLO".capitalize #=> "Hello" "123ABC".capitalize #=> "123abc"
static VALUE
rb_str_capitalize_bang(VALUE str)
{
rb_encoding *enc;
char *s, *send;
int modify = 0;
unsigned int c;
int n;
str_modify_keep_cr(str);
enc = STR_ENC_GET(str);
rb_str_check_dummy_enc(enc);
if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return Qnil;
s = RSTRING_PTR(str); send = RSTRING_END(str);
c = rb_enc_codepoint_len(s, send, &n, enc);
if (rb_enc_islower(c, enc)) {
rb_enc_mbcput(rb_enc_toupper(c, enc), s, enc);
modify = 1;
}
s += n;
while (s < send) {
c = rb_enc_codepoint_len(s, send, &n, enc);
if (rb_enc_isupper(c, enc)) {
rb_enc_mbcput(rb_enc_tolower(c, enc), s, enc);
modify = 1;
}
s += n;
}
if (modify) return str;
return Qnil;
} Изменяет str, преобразуя первую букву в верхний регистр, а остальную часть — в нижний. Возвращает nil если изменений не произошло. Примечание: преобразование регистра эффективно только в области ASCII.
a = "hello" a.capitalize! #=> "Hello" a #=> "Hello" a.capitalize! #=> nil
static VALUE
rb_str_casecmp(VALUE str1, VALUE str2)
{
long len;
rb_encoding *enc;
char *p1, *p1end, *p2, *p2end;
StringValue(str2);
enc = rb_enc_compatible(str1, str2);
if (!enc) {
return Qnil;
}
p1 = RSTRING_PTR(str1); p1end = RSTRING_END(str1);
p2 = RSTRING_PTR(str2); p2end = RSTRING_END(str2);
if (single_byte_optimizable(str1) && single_byte_optimizable(str2)) {
while (p1 < p1end && p2 < p2end) {
if (*p1 != *p2) {
unsigned int c1 = TOUPPER(*p1 & 0xff);
unsigned int c2 = TOUPPER(*p2 & 0xff);
if (c1 != c2)
return INT2FIX(c1 < c2 ? -1 : 1);
}
p1++;
p2++;
}
}
else {
while (p1 < p1end && p2 < p2end) {
int l1, c1 = rb_enc_ascget(p1, p1end, &l1, enc);
int l2, c2 = rb_enc_ascget(p2, p2end, &l2, enc);
if (0 <= c1 && 0 <= c2) {
c1 = TOUPPER(c1);
c2 = TOUPPER(c2);
if (c1 != c2)
return INT2FIX(c1 < c2 ? -1 : 1);
}
else {
int r;
l1 = rb_enc_mbclen(p1, p1end, enc);
l2 = rb_enc_mbclen(p2, p2end, enc);
len = l1 < l2 ? l1 : l2;
r = memcmp(p1, p2, len);
if (r != 0)
return INT2FIX(r < 0 ? -1 : 1);
if (l1 != l2)
return INT2FIX(l1 < l2 ? -1 : 1);
}
p1 += l1;
p2 += l2;
}
}
if (RSTRING_LEN(str1) == RSTRING_LEN(str2)) return INT2FIX(0);
if (RSTRING_LEN(str1) > RSTRING_LEN(str2)) return INT2FIX(1);
return INT2FIX(-1);
} Регистронезависимая версия String#<=>.
"abcdef".casecmp("abcde") #=> 1
"aBcDeF".casecmp("abcdef") #=> 0
"abcdef".casecmp("abcdefg") #=> -1
"abcdef".casecmp("ABCDEF") #=> 0
static VALUE
rb_str_crypt(VALUE str, VALUE salt)
{
extern char *crypt(const char *, const char *);
VALUE result;
const char *s, *saltp;
char *res;
#ifdef BROKEN_CRYPT
char salt_8bit_clean[3];
#endif
StringValue(salt);
mustnot_wchar(str);
mustnot_wchar(salt);
if (RSTRING_LEN(salt) < 2) {
short_salt:
rb_raise(rb_eArgError, "salt too short (need >=2 bytes)");
}
s = RSTRING_PTR(str);
if (!s) s = "";
saltp = RSTRING_PTR(salt);
if (!saltp[0] || !saltp[1]) goto short_salt;
#ifdef BROKEN_CRYPT
if (!ISASCII((unsigned char)saltp[0]) || !ISASCII((unsigned char)saltp[1])) {
salt_8bit_clean[0] = saltp[0] & 0x7f;
salt_8bit_clean[1] = saltp[1] & 0x7f;
salt_8bit_clean[2] = '\0';
saltp = salt_8bit_clean;
}
#endif
res = crypt(s, saltp);
if (!res) {
rb_sys_fail("crypt");
}
result = rb_str_new_cstr(res);
FL_SET_RAW(result, OBJ_TAINTED_RAW(str) | OBJ_TAINTED_RAW(salt));
return result;
} Применяет одностороннее криптографическое хеширование к str, вызывая стандартную библиотечную функцию crypt(3) с заданной строкой соли. Хотя формат и результат зависят от системы и реализации, использование соли, соответствующей регулярному выражению \A[a-zA-Z0-9./]{2} должно быть допустимым и безопасным на любой платформе, при этом значимыми являются только первые два символа.
Этот метод предназначен для использования в скриптах, специфичных для системы, поэтому если вам нужна кроссплатформенная функция хеширования, рассмотрите использование Digest или OpenSSL вместо этого.
static VALUE
rb_str_delete(int argc, VALUE *argv, VALUE str)
{
str = rb_str_dup(str);
rb_str_delete_bang(argc, argv, str);
return str;
} Возвращает копию str, из которой удалены все символы, присутствующие в пересечении ее аргументов. Использует те же правила для построения набора символов, что и String#count.
"hello".delete "l","lo" #=> "heo" "hello".delete "lo" #=> "he" "hello".delete "aeiou", "^e" #=> "hell" "hello".delete "ej-m" #=> "ho"
static VALUE
rb_str_delete_bang(int argc, VALUE *argv, VALUE str)
{
char squeez[TR_TABLE_SIZE];
rb_encoding *enc = 0;
char *s, *send, *t;
VALUE del = 0, nodel = 0;
int modify = 0;
int i, ascompat, cr;
if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return Qnil;
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
for (i=0; i<argc; i++) {
VALUE s = argv[i];
StringValue(s);
enc = rb_enc_check(str, s);
tr_setup_table(s, squeez, i==0, &del, &nodel, enc);
}
str_modify_keep_cr(str);
ascompat = rb_enc_asciicompat(enc);
s = t = RSTRING_PTR(str);
send = RSTRING_END(str);
cr = ascompat ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID;
while (s < send) {
unsigned int c;
int clen;
if (ascompat && (c = *(unsigned char*)s) < 0x80) {
if (squeez[c]) {
modify = 1;
}
else {
if (t != s) *t = c;
t++;
}
s++;
}
else {
c = rb_enc_codepoint_len(s, send, &clen, enc);
if (tr_find(c, squeez, del, nodel)) {
modify = 1;
}
else {
if (t != s) rb_enc_mbcput(c, t, enc);
t += clen;
if (cr == ENC_CODERANGE_7BIT) cr = ENC_CODERANGE_VALID;
}
s += clen;
}
}
TERM_FILL(t, TERM_LEN(str));
STR_SET_LEN(str, t - RSTRING_PTR(str));
ENC_CODERANGE_SET(str, cr);
if (modify) return str;
return Qnil;
} Выполняет операцию delete на месте, возвращая str или nil, если str не был изменён.
static VALUE
rb_str_downcase(VALUE str)
{
str = rb_str_dup(str);
rb_str_downcase_bang(str);
return str;
} Возвращает копию str, в которой все заглавные буквы заменены соответствующими строчными. Операция нечувствительна к локали — затрагиваются только символы от “A” до “Z”. Примечание: замена регистра эффективна только в ASCII-области.
"hEllO".downcase #=> "hello"
static VALUE
rb_str_downcase_bang(VALUE str)
{
rb_encoding *enc;
char *s, *send;
int modify = 0;
str_modify_keep_cr(str);
enc = STR_ENC_GET(str);
rb_str_check_dummy_enc(enc);
s = RSTRING_PTR(str); send = RSTRING_END(str);
if (single_byte_optimizable(str)) {
while (s < send) {
unsigned int c = *(unsigned char*)s;
if (rb_enc_isascii(c, enc) && 'A' <= c && c <= 'Z') {
*s = 'a' + (c - 'A');
modify = 1;
}
s++;
}
}
else {
int ascompat = rb_enc_asciicompat(enc);
while (s < send) {
unsigned int c;
int n;
if (ascompat && (c = *(unsigned char*)s) < 0x80) {
if (rb_enc_isascii(c, enc) && 'A' <= c && c <= 'Z') {
*s = 'a' + (c - 'A');
modify = 1;
}
s++;
}
else {
c = rb_enc_codepoint_len(s, send, &n, enc);
if (rb_enc_isupper(c, enc)) {
/* assuming toupper returns codepoint with same size */
rb_enc_mbcput(rb_enc_tolower(c, enc), s, enc);
modify = 1;
}
s += n;
}
}
}
if (modify) return str;
return Qnil;
} Приводит содержимое str к нижнему регистру, возвращая nil в случае отсутствия изменений. Примечание: замена регистра эффективна только в ASCII-области.
VALUE
rb_str_dump(VALUE str)
{
rb_encoding *enc = rb_enc_get(str);
long len;
const char *p, *pend;
char *q, *qend;
VALUE result;
int u8 = (enc == rb_utf8_encoding());
len = 2; /* "" */
p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
while (p < pend) {
unsigned char c = *p++;
switch (c) {
case '"': case '\\':
case '\n': case '\r':
case '\t': case '\f':
case '\013': case '\010': case '\007': case '\033':
len += 2;
break;
case '#':
len += IS_EVSTR(p, pend) ? 2 : 1;
break;
default:
if (ISPRINT(c)) {
len++;
}
else {
if (u8 && c > 0x7F) { /* \u{NN} */
int n = rb_enc_precise_mbclen(p-1, pend, enc);
if (MBCLEN_CHARFOUND_P(n)) {
unsigned int cc = rb_enc_mbc_to_codepoint(p-1, pend, enc);
while (cc >>= 4) len++;
len += 5;
p += MBCLEN_CHARFOUND_LEN(n)-1;
break;
}
}
len += 4; /* \xNN */
}
break;
}
}
if (!rb_enc_asciicompat(enc)) {
len += 19; /* ".force_encoding('')" */
len += strlen(enc->name);
}
result = rb_str_new_with_class(str, 0, len);
p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
q = RSTRING_PTR(result); qend = q + len + 1;
*q++ = '"';
while (p < pend) {
unsigned char c = *p++;
if (c == '"' || c == '\\') {
*q++ = '\\';
*q++ = c;
}
else if (c == '#') {
if (IS_EVSTR(p, pend)) *q++ = '\\';
*q++ = '#';
}
else if (c == '\n') {
*q++ = '\\';
*q++ = 'n';
}
else if (c == '\r') {
*q++ = '\\';
*q++ = 'r';
}
else if (c == '\t') {
*q++ = '\\';
*q++ = 't';
}
else if (c == '\f') {
*q++ = '\\';
*q++ = 'f';
}
else if (c == '\013') {
*q++ = '\\';
*q++ = 'v';
}
else if (c == '\010') {
*q++ = '\\';
*q++ = 'b';
}
else if (c == '\007') {
*q++ = '\\';
*q++ = 'a';
}
else if (c == '\033') {
*q++ = '\\';
*q++ = 'e';
}
else if (ISPRINT(c)) {
*q++ = c;
}
else {
*q++ = '\\';
if (u8) {
int n = rb_enc_precise_mbclen(p-1, pend, enc) - 1;
if (MBCLEN_CHARFOUND_P(n)) {
int cc = rb_enc_mbc_to_codepoint(p-1, pend, enc);
p += n;
snprintf(q, qend-q, "u{%x}", cc);
q += strlen(q);
continue;
}
}
snprintf(q, qend-q, "x%02X", c);
q += 3;
}
}
*q++ = '"';
*q = '\0';
if (!rb_enc_asciicompat(enc)) {
snprintf(q, qend-q, ".force_encoding(\"%s\")", enc->name);
enc = rb_ascii8bit_encoding();
}
OBJ_INFECT_RAW(result, str);
/* result from dump is ASCII */
rb_enc_associate(result, enc);
ENC_CODERANGE_SET(result, ENC_CODERANGE_7BIT);
return result;
} Создаёт версию str, в которой все непечатаемые символы заменены на обозначение \nnn, а все специальные символы экранированы.
"hello \n ''".dump #=> "\"hello \\n ''\"
static VALUE
rb_str_each_byte(VALUE str)
{
return rb_str_enumerate_bytes(str, 0);
} Передаёт каждый байт в str в заданный блок или возвращает перечислитель, если блок не задан.
"hello".each_byte {|c| print c, ' ' }
Результат:
104 101 108 108 111
static VALUE
rb_str_each_char(VALUE str)
{
return rb_str_enumerate_chars(str, 0);
} Передаёт каждый символ в str в заданный блок или возвращает перечислитель, если блок не задан.
"hello".each_char {|c| print c, ' ' }
Результат:
h e l l o
static VALUE
rb_str_each_codepoint(VALUE str)
{
return rb_str_enumerate_codepoints(str, 0);
} Передаёт порядковый номер Integer каждого символа в str, также известный как кодовый точка при применении к строкам Unicode, в заданный блок.
Если блок не задан, возвращается перечислитель.
"hello\u0639".each_codepoint {|c| print c, ' ' }
Результат:
104 101 108 108 111 1593
static VALUE
rb_str_each_line(int argc, VALUE *argv, VALUE str)
{
return rb_str_enumerate_lines(argc, argv, str, 0);
} Разделяет str, используя заданный параметр в качестве разделителя записей (по умолчанию $/), и передаёт каждую подстроку в заданный блок. Если задан разделитель нулевой длины, строка разделяется на абзацы, ограниченные несколькими последовательными символами новой строки.
Если блок не задан, возвращается перечислитель.
print "Example one\n"
"hello\nworld".each_line {|s| p s}
print "Example two\n"
"hello\nworld".each_line('l') {|s| p s}
print "Example three\n"
"hello\n\n\nworld".each_line('') {|s| p s}
Результат:
Example one "hello\n" "world" Example two "hel" "l" "o\nworl" "d" Example three "hello\n\n\n" "world"
static VALUE
rb_str_empty(VALUE str)
{
if (RSTRING_LEN(str) == 0)
return Qtrue;
return Qfalse;
} Возвращает true, если у str длина равна нулю.
"hello".empty? #=> false " ".empty? #=> false "".empty? #=> true
static VALUE
str_encode(int argc, VALUE *argv, VALUE str)
{
VALUE newstr = str;
int encidx = str_transcode(argc, argv, &newstr);
return encoded_dup(newstr, str, encidx);
} Первый вариант возвращает копию str, преобразованную в кодировку encoding. Второй вариант возвращает копию str преобразованную из src_encoding в dst_encoding. Последний вариант возвращает копию str преобразованную в Encoding.default_internal.
По умолчанию первый и второй варианты генерируют Encoding::UndefinedConversionError для символов, не определённых в кодировке назначения, и Encoding::InvalidByteSequenceError для недопустимых последовательностей байтов в кодировке источника. Последний вариант по умолчанию не генерирует исключения, а использует строки замены.
Подробности преобразования содержит options Hash и может содержать следующие ключи:
- :invalid
-
Если значение равно
:replace, encode заменяет недопустимые последовательности байтов вstrсимволом замены. По умолчанию генерируется исключение Encoding::InvalidByteSequenceError - :undef
-
Если значение равно
:replace, encode заменяет символы, не определённые в кодировке назначения, символом замены. По умолчанию генерируется исключение Encoding::UndefinedConversionError. - :replace
-
Устанавливает строку замены на заданное значение. По умолчанию строкой замены является “uFFFD” для форм кодировки Unicode и “?” в противном случае.
- :fallback
-
Устанавливает строку замены заданным объектом для неопределённых символов. Объект должен быть Hash, Proc, Method или объектом, имеющим метод []. Его ключ — неопределённый символ, закодированный в кодировке источника текущего преобразователя. Его значение может быть любой кодировкой, пока его можно преобразовать в кодировку назначения преобразователя.
- :xml
-
Значение должно быть
:textили:attr. Если значение:text, encode заменяет неопределённые символы их (заглавными шестнадцатеричными) числовыми ссылками на символы. '&', '<', и '>' соответственно преобразуются в “&”, “<”, и “>”. Если значение:attr, encode также приводит результат замены в кавычки (используя '“') и заменяет '”' на “"”. - :cr_newline
-
Заменяет LF (“n”) на CR (“r”), если значение true.
- :crlf_newline
-
Заменяет LF (“n”) на CRLF (“rn”), если значение true.
- :universal_newline
-
Заменяет CRLF (“rn”) и CR (“r”) на LF (“n”), если значение true.
static VALUE
str_encode_bang(int argc, VALUE *argv, VALUE str)
{
VALUE newstr;
int encidx;
rb_check_frozen(str);
newstr = str;
encidx = str_transcode(argc, argv, &newstr);
if (encidx < 0) return str;
if (newstr == str) {
rb_enc_associate_index(str, encidx);
return str;
}
rb_str_shared_replace(str, newstr);
return str_encode_associate(str, encidx);
} Первый вариант преобразует содержимое str из str.encoding в encoding. Второй вариант преобразует содержимое str из src_encoding в dst_encoding. Опции Hash содержат подробности преобразования. См. #encode для получения дополнительных сведений. Возвращает строку даже если изменений не было.
VALUE
rb_obj_encoding(VALUE obj)
{
int idx = rb_enc_get_index(obj);
if (idx < 0) {
rb_raise(rb_eTypeError, "unknown encoding");
}
return rb_enc_from_encoding_index(idx & ENC_INDEX_MASK);
} Возвращает объект Encoding, представляющий кодировку obj.
static VALUE
rb_str_end_with(int argc, VALUE *argv, VALUE str)
{
int i;
char *p, *s, *e;
rb_encoding *enc;
for (i=0; i<argc; i++) {
VALUE tmp = argv[i];
StringValue(tmp);
enc = rb_enc_check(str, tmp);
if (RSTRING_LEN(str) < RSTRING_LEN(tmp)) continue;
p = RSTRING_PTR(str);
e = p + RSTRING_LEN(str);
s = e - RSTRING_LEN(tmp);
if (rb_enc_left_char_head(p, s, e, enc) != s)
continue;
if (memcmp(s, RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0)
return Qtrue;
}
return Qfalse;
} Возвращает true, если str оканчивается на один из suffixes указанных.
static VALUE
rb_str_eql(VALUE str1, VALUE str2)
{
if (str1 == str2) return Qtrue;
if (!RB_TYPE_P(str2, T_STRING)) return Qfalse;
return str_eql(str1, str2);
} Две строки равны, если они имеют одинаковую длину и содержимое.
static VALUE
rb_str_force_encoding(VALUE str, VALUE enc)
{
str_modifiable(str);
rb_enc_associate(str, rb_to_encoding(enc));
ENC_CODERANGE_CLEAR(str);
return str;
} Изменяет кодировку на encoding и возвращает self.
static VALUE
rb_str_getbyte(VALUE str, VALUE index)
{
long pos = NUM2LONG(index);
if (pos < 0)
pos += RSTRING_LEN(str);
if (pos < 0 || RSTRING_LEN(str) <= pos)
return Qnil;
return INT2FIX((unsigned char)RSTRING_PTR(str)[pos]);
} возвращает байт с индексом в виде целого числа.
static VALUE
rb_str_gsub(int argc, VALUE *argv, VALUE str)
{
return str_gsub(argc, argv, str, 0);
} Возвращает копию str со всеми вхождениями pattern, заменёнными на второй аргумент. pattern обычно представляет собой Regexp; если задано в виде String, все метасимволы регулярных выражений будут интерпретированы буквально, например, '\\d' будет соответствовать обратной косой черте, за которой идёт 'd', вместо цифры.
Если replacement — String, он будет заменён на совпавшее значение. Может содержать обратные ссылки на группы захвата шаблона вида \\d, где d — номер группы, или \\k<n>, где n — имя группы. Если это строка в двойных кавычках, обе обратные ссылки должны предваряться дополнительной обратной косой чертой. Однако внутри replacement специальные переменные сопоставления, такие как $&, не будут относиться к текущему совпадению.
Если второй аргумент — Hash, и совпавшее значение является одним из его ключей, соответствующее значение является строкой замены.
В блочном формате текущая строка совпадения передаётся в качестве параметра, и такие переменные, как $1, $2, $`, $&, и $', будут установлены соответствующим образом. Значение, возвращаемое блоком, будет заменено на совпадение при каждом вызове.
Результат наследует любые изменения в исходной строке или любой предоставленной строке замены.
Если не предоставлен ни блок, ни второй аргумент, возвращается Enumerator.
"hello".gsub(/[aeiou]/, '*') #=> "h*ll*"
"hello".gsub(/([aeiou])/, '<\1>') #=> "h<e>ll<o>"
"hello".gsub(/./) {|s| s.ord.to_s + ' '} #=> "104 101 108 108 111 "
"hello".gsub(/(?<foo>[aeiou])/, '{\k<foo>}') #=> "h{e}ll{o}"
'hello'.gsub(/[eo]/, 'e' => 3, 'o' => '*') #=> "h3ll*"
static VALUE
rb_str_gsub_bang(int argc, VALUE *argv, VALUE str)
{
str_modify_keep_cr(str);
return str_gsub(argc, argv, str, 1);
} Выполняет замены String#gsub на месте, возвращая str или nil, если замен не было. Если не задан ни блок, ни replacement, вместо этого возвращается итератор.
static VALUE
rb_str_lstrip_bang(VALUE str)
{
rb_encoding *enc;
char *start, *s;
long olen, loffset;
str_modify_keep_cr(str);
enc = STR_ENC_GET(str);
RSTRING_GETMEM(str, start, olen);
loffset = lstrip_offset(str, start, start+olen, enc);
if (loffset > 0) {
long len = olen-loffset;
s = start + loffset;
memmove(start, s, len);
STR_SET_LEN(str, len);
#if !SHARABLE_MIDDLE_SUBSTRING
TERM_FILL(start+len, rb_enc_mbminlen(enc));
#endif
return str;
}
return Qnil;
} Удаляет начальные пробелы из str, возвращая nil если изменений не было. См. также String#rstrip! и String#strip!.
" hello ".lstrip #=> "hello " "hello".lstrip! #=> nil
static VALUE
rb_str_match_m(int argc, VALUE *argv, VALUE str)
{
VALUE re, result;
if (argc < 1)
rb_check_arity(argc, 1, 2);
re = argv[0];
argv[0] = str;
result = rb_funcall2(get_pat(re), rb_intern("match"), argc, argv);
if (!NIL_P(result) && rb_block_given_p()) {
return rb_yield(result);
}
return result;
} Преобразует pattern в Regexp (если это не так), затем вызывает его метод match над str. Если второй параметр присутствует, он указывает позицию в строке, с которой начинается поиск.
'hello'.match('(.)\1') #=> #<MatchData "ll" 1:"l">
'hello'.match('(.)\1')[0] #=> "ll"
'hello'.match(/(.)\1/)[0] #=> "ll"
'hello'.match('xx') #=> nil
Если задан блок, вызовите блок с MatchData, если совпадение найдено, чтобы вы могли записать
str.match(pat) {|m| ...} вместо
if m = str.match(pat) ... end
В этом случае возвращаемое значение — значение из выполнения блока.
VALUE
rb_str_succ(VALUE orig)
{
VALUE str;
str = rb_str_new_with_class(orig, RSTRING_PTR(orig), RSTRING_LEN(orig));
rb_enc_cr_str_copy_for_substr(str, orig);
OBJ_INFECT(str, orig);
return str_succ(str);
} Возвращает преемника str. Преемник вычисляется путём инкрементации символов, начиная с самого правого алфавитно-цифрового символа (или последнего символа, если алфавитно-цифровых символов нет) в строке. Инкремент цифры всегда приводит к другой цифре, а инкремент буквы приводит к другой букве той же буквы. Инкремент неалфавитно-цифровых символов использует порядок сортировки набора символов.
Если инкремент приводит к «переносу», то инкрементируется символ слева от него. Этот процесс повторяется до тех пор, пока не будет переноса, добавляя дополнительный символ при необходимости.
"abcd".succ #=> "abce" "THX1138".succ #=> "THX1139" "<<koala>>".succ #=> "<<koalb>>" "1999zzz".succ #=> "2000aaa" "ZZZ9999".succ #=> "AAAA0000" "***".succ #=> "**+"
static VALUE
rb_str_succ_bang(VALUE str)
{
rb_str_modify(str);
str_succ(str);
return str;
} Эквивалентно String#succ, но изменяет получатель на месте.
static VALUE
rb_str_oct(VALUE str)
{
return rb_str_to_inum(str, -8, FALSE);
} Обрабатывает ведущие символы str как строку восьмеричных цифр (с необязательным знаком) и возвращает соответствующее число. Возвращает 0, если преобразование не выполняется.
"123".oct #=> 83 "-377".oct #=> -255 "bad".oct #=> 0 "0377bad".oct #=> 255
VALUE
rb_str_ord(VALUE s)
{
unsigned int c;
c = rb_enc_codepoint(RSTRING_PTR(s), RSTRING_END(s), STR_ENC_GET(s));
return UINT2NUM(c);
} Возвращает Integer порядковый номер строки из одного символа.
"a".ord #=> 97
static VALUE
rb_str_partition(VALUE str, VALUE sep)
{
long pos;
sep = get_pat_quoted(sep, 0);
if (RB_TYPE_P(sep, T_REGEXP)) {
pos = rb_reg_search(sep, str, 0, 0);
if (pos < 0) {
failed:
return rb_ary_new3(3, str, str_new_empty(str), str_new_empty(str));
}
sep = rb_str_subpat(str, sep, INT2FIX(0));
if (pos == 0 && RSTRING_LEN(sep) == 0) goto failed;
}
else {
pos = rb_str_index(str, sep, 0);
if (pos < 0) goto failed;
}
return rb_ary_new3(3, rb_str_subseq(str, 0, pos),
sep,
rb_str_subseq(str, pos+RSTRING_LEN(sep),
RSTRING_LEN(str)-pos-RSTRING_LEN(sep)));
} Ищет sep или шаблон (regexp) в строке и возвращает часть перед ним, совпадение и часть после него. Если не найдено, возвращает две пустые строки и str.
"hello".partition("l") #=> ["he", "l", "lo"]
"hello".partition("x") #=> ["hello", "", ""]
"hello".partition(/.l/) #=> ["h", "el", "lo"]
static VALUE
rb_str_prepend(VALUE str, VALUE str2)
{
StringValue(str2);
StringValue(str);
rb_str_update(str, 0L, 0L, str2);
return str;
} Добавляет—Добавляет заданную строку к str.
a = "world"
a.prepend("hello ") #=> "hello world"
a #=> "hello world"
static VALUE
str_scrub_bang(int argc, VALUE *argv, VALUE str)
{
VALUE repl = argc ? (rb_check_arity(argc, 0, 1), argv[0]) : Qnil;
VALUE new = rb_str_scrub(str, repl);
if (!NIL_P(new)) rb_str_replace(str, new);
return str;
} Если строка является некорректной последовательностью байтов, то заменяет некорректные байты заданным символом-заменителем, иначе возвращает само себя. Если задан блок, то заменяет некорректные байты результатом выполнения этого блока.
"abc\u3042\x81".scrub! #=> "abc\u3042\uFFFD"
"abc\u3042\x81".scrub!("*") #=> "abc\u3042*"
"abc\u3042\xE3\x80".scrub!{|bytes| '<'+bytes.unpack('H*')[0]+'>' } #=> "abc\u3042<e380>"
static VALUE
rb_str_setbyte(VALUE str, VALUE index, VALUE value)
{
long pos = NUM2LONG(index);
int byte = NUM2INT(value);
rb_str_modify(str);
if (pos < -RSTRING_LEN(str) || RSTRING_LEN(str) <= pos)
rb_raise(rb_eIndexError, "index %ld out of string", pos);
if (pos < 0)
pos += RSTRING_LEN(str);
RSTRING_PTR(str)[pos] = byte;
return value;
} модифицирует байт с индексом как целое число.
# File lib/shellwords.rb, line 199 def shellescape Shellwords.escape(self) end
Экранирует str так, чтобы его безопасно можно было использовать в командной строке оболочки Bourne.
См. Shellwords#shellescape для получения подробной информации.
# File lib/shellwords.rb, line 188 def shellsplit Shellwords.split(self) end
Разделяет str на массив токенов так же, как это делает оболочка Bourne UNIX.
См. Shellwords#shellsplit для получения подробной информации.
VALUE
rb_str_length(VALUE str)
{
return LONG2NUM(str_strlen(str, NULL));
} Возвращает длину строки str в символах.
static VALUE
rb_str_aref_m(int argc, VALUE *argv, VALUE str)
{
if (argc == 2) {
if (RB_TYPE_P(argv[0], T_REGEXP)) {
return rb_str_subpat(str, argv[0], argv[1]);
}
return rb_str_substr(str, NUM2LONG(argv[0]), NUM2LONG(argv[1]));
}
rb_check_arity(argc, 1, 2);
return rb_str_aref(str, argv[0]);
} Ссылка на элемент — Если передан один index, возвращает подстроку длиной в один символ по этому индексу. Если передан start индекс и length, возвращает подстроку, содержащую length символов, начиная с index. Если передан range, его начало и конец интерпретируются как смещения, определяющие подстроку, которая должна быть возвращена.
В этих трёх случаях, если индекс отрицательный, он отсчитывается от конца строки. Для случаев start и range начальный индекс находится непосредственно перед символом, а индекс, соответствующий размеру строки. Кроме того, пустая строка возвращается, когда начальный индекс для диапазона символов находится в конце строки.
Возвращает nil если начальный индекс выходит за пределы строки или длина отрицательная.
Если передан Regexp, возвращается соответствующая часть строки. Если за регулярным выражением следует capture, который может быть индексом или именем группы захвата, вместо этого возвращается соответствующая часть объекта MatchData.
Если передан match_str, возвращается эта строка, если она встречается в строке.
Возвращает nil если регулярное выражение не соответствует или строка совпадения не найдена.
a = "hello there" a[1] #=> "e" a[2, 3] #=> "llo" a[2..3] #=> "ll" a[-3, 2] #=> "er" a[7..-2] #=> "her" a[-4..-2] #=> "her" a[-2..-4] #=> "" a[11, 0] #=> "" a[11] #=> nil a[12, 0] #=> nil a[12..-1] #=> nil a[/[aeiou](.)\1/] #=> "ell" a[/[aeiou](.)\1/, 0] #=> "ell" a[/[aeiou](.)\1/, 1] #=> "l" a[/[aeiou](.)\1/, 2] #=> nil a[/(?<vowel>[aeiou])(?<non_vowel>[^aeiou])/, "non_vowel"] #=> "l" a[/(?<vowel>[aeiou])(?<non_vowel>[^aeiou])/, "vowel"] #=> "e" a["lo"] #=> "lo" a["bye"] #=> nil
static VALUE
rb_str_slice_bang(int argc, VALUE *argv, VALUE str)
{
VALUE result;
VALUE buf[3];
int i;
rb_check_arity(argc, 1, 2);
for (i=0; i<argc; i++) {
buf[i] = argv[i];
}
str_modify_keep_cr(str);
result = rb_str_aref_m(argc, buf, str);
if (!NIL_P(result)) {
buf[i] = rb_str_new(0,0);
rb_str_aset_m(argc+1, buf, str);
}
return result;
} Удаляет указанную часть из str и возвращает удаленную часть.
string = "this is a string"
string.slice!(2) #=> "i"
string.slice!(3..6) #=> " is "
string.slice!(/s.*t/) #=> "sa st"
string.slice!("r") #=> "r"
string #=> "thing"
static VALUE
rb_str_split_m(int argc, VALUE *argv, VALUE str)
{
rb_encoding *enc;
VALUE spat;
VALUE limit;
enum {awk, string, regexp} split_type;
long beg, end, i = 0;
int lim = 0;
VALUE result, tmp;
if (rb_scan_args(argc, argv, "02", &spat, &limit) == 2) {
lim = NUM2INT(limit);
if (lim <= 0) limit = Qnil;
else if (lim == 1) {
if (RSTRING_LEN(str) == 0)
return rb_ary_new2(0);
return rb_ary_new3(1, str);
}
i = 1;
}
enc = STR_ENC_GET(str);
if (NIL_P(spat) && NIL_P(spat = rb_fs)) {
split_type = awk;
}
else {
spat = get_pat_quoted(spat, 0);
if (BUILTIN_TYPE(spat) == T_STRING) {
rb_encoding *enc2 = STR_ENC_GET(spat);
mustnot_broken(spat);
split_type = string;
if (RSTRING_LEN(spat) == 0) {
/* Special case - split into chars */
spat = rb_reg_regcomp(spat);
split_type = regexp;
}
else if (rb_enc_asciicompat(enc2) == 1) {
if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' '){
split_type = awk;
}
}
else {
int l;
if (rb_enc_ascget(RSTRING_PTR(spat), RSTRING_END(spat), &l, enc2) == ' ' &&
RSTRING_LEN(spat) == l) {
split_type = awk;
}
}
}
else {
split_type = regexp;
}
}
result = rb_ary_new();
beg = 0;
if (split_type == awk) {
char *ptr = RSTRING_PTR(str);
char *eptr = RSTRING_END(str);
char *bptr = ptr;
int skip = 1;
unsigned int c;
end = beg;
if (is_ascii_string(str)) {
while (ptr < eptr) {
c = (unsigned char)*ptr++;
if (skip) {
if (ascii_isspace(c)) {
beg = ptr - bptr;
}
else {
end = ptr - bptr;
skip = 0;
if (!NIL_P(limit) && lim <= i) break;
}
}
else if (ascii_isspace(c)) {
rb_ary_push(result, rb_str_subseq(str, beg, end-beg));
skip = 1;
beg = ptr - bptr;
if (!NIL_P(limit)) ++i;
}
else {
end = ptr - bptr;
}
}
}
else {
while (ptr < eptr) {
int n;
c = rb_enc_codepoint_len(ptr, eptr, &n, enc);
ptr += n;
if (skip) {
if (rb_isspace(c)) {
beg = ptr - bptr;
}
else {
end = ptr - bptr;
skip = 0;
if (!NIL_P(limit) && lim <= i) break;
}
}
else if (rb_isspace(c)) {
rb_ary_push(result, rb_str_subseq(str, beg, end-beg));
skip = 1;
beg = ptr - bptr;
if (!NIL_P(limit)) ++i;
}
else {
end = ptr - bptr;
}
}
}
}
else if (split_type == string) {
char *ptr = RSTRING_PTR(str);
char *temp = ptr;
char *eptr = RSTRING_END(str);
char *sptr = RSTRING_PTR(spat);
long slen = RSTRING_LEN(spat);
mustnot_broken(str);
enc = rb_enc_check(str, spat);
while (ptr < eptr &&
(end = rb_memsearch(sptr, slen, ptr, eptr - ptr, enc)) >= 0) {
/* Check we are at the start of a char */
char *t = rb_enc_right_char_head(ptr, ptr + end, eptr, enc);
if (t != ptr + end) {
ptr = t;
continue;
}
rb_ary_push(result, rb_str_subseq(str, ptr - temp, end));
ptr += end + slen;
if (!NIL_P(limit) && lim <= ++i) break;
}
beg = ptr - temp;
}
else {
char *ptr = RSTRING_PTR(str);
long len = RSTRING_LEN(str);
long start = beg;
long idx;
int last_null = 0;
struct re_registers *regs;
while ((end = rb_reg_search(spat, str, start, 0)) >= 0) {
regs = RMATCH_REGS(rb_backref_get());
if (start == end && BEG(0) == END(0)) {
if (!ptr) {
rb_ary_push(result, str_new_empty(str));
break;
}
else if (last_null == 1) {
rb_ary_push(result, rb_str_subseq(str, beg,
rb_enc_fast_mbclen(ptr+beg,
ptr+len,
enc)));
beg = start;
}
else {
if (start == len)
start++;
else
start += rb_enc_fast_mbclen(ptr+start,ptr+len,enc);
last_null = 1;
continue;
}
}
else {
rb_ary_push(result, rb_str_subseq(str, beg, end-beg));
beg = start = END(0);
}
last_null = 0;
for (idx=1; idx < regs->num_regs; idx++) {
if (BEG(idx) == -1) continue;
if (BEG(idx) == END(idx))
tmp = str_new_empty(str);
else
tmp = rb_str_subseq(str, BEG(idx), END(idx)-BEG(idx));
rb_ary_push(result, tmp);
}
if (!NIL_P(limit) && lim <= ++i) break;
}
}
if (RSTRING_LEN(str) > 0 && (!NIL_P(limit) || RSTRING_LEN(str) > beg || lim < 0)) {
if (RSTRING_LEN(str) == beg)
tmp = str_new_empty(str);
else
tmp = rb_str_subseq(str, beg, RSTRING_LEN(str)-beg);
rb_ary_push(result, tmp);
}
if (NIL_P(limit) && lim == 0) {
long len;
while ((len = RARRAY_LEN(result)) > 0 &&
(tmp = RARRAY_AREF(result, len-1), RSTRING_LEN(tmp) == 0))
rb_ary_pop(result);
}
return result;
} Разделяет str на подстроки на основе разделителя, возвращая массив этих подстрок.
Если pattern — это String, тогда его содержимое используется в качестве разделителя при разделении str. Если pattern — это пробел, str разделяется по пробелам, с игнорированием начальных пробелов и последовательностей пробелов.
Если pattern — это Regexp, str делится там, где совпадает шаблон. Всякий раз, когда шаблон соответствует пустой строке, str разделяется на отдельные символы. Если pattern содержит группы, соответствующие совпадения будут возвращены в массиве также.
Если pattern опущено, используется значение $;. Если $; — это nil (что является значением по умолчанию), str разделяется по пробелам, как если бы был указан ` '.
Если параметр limit опущен, хвостовые нулевые поля подавляются. Если limit — положительное число, возвращается не более этого числа полей (если limit — 1, вся строка возвращается как единственная запись в массиве). Если отрицательное, нет ограничения на количество возвращенных полей, и хвостовые нулевые поля не подавляются.
Когда входное str пустое, возвращается пустой массив, так как строка считается не имеющей полей для разделения.
" now's the time".split #=> ["now's", "the", "time"]
" now's the time".split(' ') #=> ["now's", "the", "time"]
" now's the time".split(/ /) #=> ["", "now's", "", "the", "time"]
"1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"]
"hello".split(//) #=> ["h", "e", "l", "l", "o"]
"hello".split(//, 3) #=> ["h", "e", "llo"]
"hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"]
"mellow yellow".split("ello") #=> ["m", "w y", "w"]
"1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"]
"1,2,,3,4,,".split(',', 4) #=> ["1", "2", "", "3,4,,"]
"1,2,,3,4,,".split(',', -4) #=> ["1", "2", "", "3", "4", "", ""]
"".split(',', -1) #=> []
static VALUE
rb_str_squeeze(int argc, VALUE *argv, VALUE str)
{
str = rb_str_dup(str);
rb_str_squeeze_bang(argc, argv, str);
return str;
} Создаёт множество символов из параметра(ов) other_str, используя процедуру, описанную для String#count. Возвращает новую строку, где последовательности одинаковых символов, присутствующих в этом множестве, заменяются одним символом. Если аргументы не указаны, все последовательности одинаковых символов заменяются одним символом.
"yellow moon".squeeze #=> "yelow mon"
" now is the".squeeze(" ") #=> " now is the"
"putters shoot balls".squeeze("m-z") #=> "puters shot balls"
static VALUE
rb_str_squeeze_bang(int argc, VALUE *argv, VALUE str)
{
char squeez[TR_TABLE_SIZE];
rb_encoding *enc = 0;
VALUE del = 0, nodel = 0;
char *s, *send, *t;
int i, modify = 0;
int ascompat, singlebyte = single_byte_optimizable(str);
unsigned int save;
if (argc == 0) {
enc = STR_ENC_GET(str);
}
else {
for (i=0; i<argc; i++) {
VALUE s = argv[i];
StringValue(s);
enc = rb_enc_check(str, s);
if (singlebyte && !single_byte_optimizable(s))
singlebyte = 0;
tr_setup_table(s, squeez, i==0, &del, &nodel, enc);
}
}
str_modify_keep_cr(str);
s = t = RSTRING_PTR(str);
if (!s || RSTRING_LEN(str) == 0) return Qnil;
send = RSTRING_END(str);
save = -1;
ascompat = rb_enc_asciicompat(enc);
if (singlebyte) {
while (s < send) {
unsigned int c = *(unsigned char*)s++;
if (c != save || (argc > 0 && !squeez[c])) {
*t++ = save = c;
}
}
}
else {
while (s < send) {
unsigned int c;
int clen;
if (ascompat && (c = *(unsigned char*)s) < 0x80) {
if (c != save || (argc > 0 && !squeez[c])) {
*t++ = save = c;
}
s++;
}
else {
c = rb_enc_codepoint_len(s, send, &clen, enc);
if (c != save || (argc > 0 && !tr_find(c, squeez, del, nodel))) {
if (t != s) rb_enc_mbcput(c, t, enc);
save = c;
t += clen;
}
s += clen;
}
}
}
TERM_FILL(t, TERM_LEN(str));
if (t - RSTRING_PTR(str) != RSTRING_LEN(str)) {
STR_SET_LEN(str, t - RSTRING_PTR(str));
modify = 1;
}
if (modify) return str;
return Qnil;
} Сжимает str на месте, возвращая либо str, либо nil если изменений не было.
static VALUE
rb_str_start_with(int argc, VALUE *argv, VALUE str)
{
int i;
for (i=0; i<argc; i++) {
VALUE tmp = argv[i];
StringValue(tmp);
rb_enc_check(str, tmp);
if (RSTRING_LEN(str) < RSTRING_LEN(tmp)) continue;
if (memcmp(RSTRING_PTR(str), RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0)
return Qtrue;
}
return Qfalse;
} Возвращает true, если str начинается с одного из prefixes заданных.
"hello".start_with?("hell") #=> true
# returns true if one of the prefixes matches.
"hello".start_with?("heaven", "hell") #=> true
"hello".start_with?("heaven", "paradise") #=> false
static VALUE
rb_str_strip(VALUE str)
{
char *start;
long olen, loffset, roffset;
rb_encoding *enc = STR_ENC_GET(str);
RSTRING_GETMEM(str, start, olen);
loffset = lstrip_offset(str, start, start+olen, enc);
roffset = rstrip_offset(str, start+loffset, start+olen, enc);
if (loffset <= 0 && roffset <= 0) return rb_str_dup(str);
return rb_str_subseq(str, loffset, olen-loffset-roffset);
} Возвращает копию str с удалёнными начальными и конечными пробелами.
" hello ".strip #=> "hello" "\tgoodbye\r\n".strip #=> "goodbye"
static VALUE
rb_str_strip_bang(VALUE str)
{
char *start;
long olen, loffset, roffset;
rb_encoding *enc;
str_modify_keep_cr(str);
enc = STR_ENC_GET(str);
RSTRING_GETMEM(str, start, olen);
loffset = lstrip_offset(str, start, start+olen, enc);
roffset = rstrip_offset(str, start+loffset, start+olen, enc);
if (loffset > 0 || roffset > 0) {
long len = olen-roffset;
if (loffset > 0) {
len -= loffset;
memmove(start, start + loffset, len);
}
STR_SET_LEN(str, len);
#if !SHARABLE_MIDDLE_SUBSTRING
TERM_FILL(start+len, rb_enc_mbminlen(enc));
#endif
return str;
}
return Qnil;
} Удаляет начальные и конечные пробелы из str. Возвращает nil если str не был изменён.
static VALUE
rb_str_sub(int argc, VALUE *argv, VALUE str)
{
str = rb_str_dup(str);
rb_str_sub_bang(argc, argv, str);
return str;
} Возвращает копию str с первым вхождением pattern заменённым на второй аргумент. Второй аргумент обычно является Regexp; если он задан как строка, все метасимволы регулярных выражений в нём будут интерпретированы буквально, например, '\\d' будет соответствовать обратной косой черте, за которой следует 'd', а не цифре.
Если второй аргумент — строка, она подставится вместо совпавшего текста. Она может содержать обратные ссылки на группы захвата шаблона в формате "\d", где d — номер группы, или "\k<n>", где n — имя группы. Если это строка в двойных кавычках, обе обратные ссылки должны предваряться дополнительной обратной косой чертой. Однако внутри replacement специальные переменные соответствия, такие как &$, не будут ссылаться на текущее соответствие. Если replacement — строка, которая выглядит как группа захвата шаблона, но на самом деле ею не является, например, "\'", то перед ней должны стоять две обратные косые черты, как в "\\'".
Если второй аргумент — Hash, и совпавший текст является одним из его ключей, соответствующее значение является строкой замены.
В форме с блоком текущая строка соответствия передаётся в качестве параметра, а переменные, такие как $1, $2, $`, $&, и $', будут установлены должным образом. Значение, возвращаемое блоком, будет подставлено вместо соответствия при каждом вызове.
Результат наследует любые изменения состояния исходной строки или любой предоставленной строки замены.
"hello".sub(/[aeiou]/, '*') #=> "h*llo"
"hello".sub(/([aeiou])/, '<\1>') #=> "h<e>llo"
"hello".sub(/./) {|s| s.ord.to_s + ' ' } #=> "104 ello"
"hello".sub(/(?<foo>[aeiou])/, '*\k<foo>*') #=> "h*e*llo"
'Is SHELL your preferred shell?'.sub(/[[:upper:]]{2,}/, ENV)
#=> "Is /bin/bash your preferred shell?"
static VALUE
rb_str_sub_bang(int argc, VALUE *argv, VALUE str)
{
VALUE pat, repl, hash = Qnil;
int iter = 0;
int tainted = 0;
long plen;
int min_arity = rb_block_given_p() ? 1 : 2;
long beg;
rb_check_arity(argc, min_arity, 2);
if (argc == 1) {
iter = 1;
}
else {
repl = argv[1];
hash = rb_check_hash_type(argv[1]);
if (NIL_P(hash)) {
StringValue(repl);
}
tainted = OBJ_TAINTED_RAW(repl);
}
pat = get_pat_quoted(argv[0], 1);
str_modifiable(str);
beg = rb_pat_search(pat, str, 0, 1);
if (beg >= 0) {
rb_encoding *enc;
int cr = ENC_CODERANGE(str);
long beg0, end0;
VALUE match, match0 = Qnil;
struct re_registers *regs;
char *p, *rp;
long len, rlen;
match = rb_backref_get();
regs = RMATCH_REGS(match);
if (RB_TYPE_P(pat, T_STRING)) {
beg0 = beg;
end0 = beg0 + RSTRING_LEN(pat);
match0 = pat;
}
else {
beg0 = BEG(0);
end0 = END(0);
if (iter) match0 = rb_reg_nth_match(0, match);
}
if (iter || !NIL_P(hash)) {
p = RSTRING_PTR(str); len = RSTRING_LEN(str);
if (iter) {
repl = rb_obj_as_string(rb_yield(match0));
}
else {
repl = rb_hash_aref(hash, rb_str_subseq(str, beg0, end0 - beg0));
repl = rb_obj_as_string(repl);
}
str_mod_check(str, p, len);
rb_check_frozen(str);
}
else {
repl = rb_reg_regsub(repl, str, regs, RB_TYPE_P(pat, T_STRING) ? Qnil : pat);
}
enc = rb_enc_compatible(str, repl);
if (!enc) {
rb_encoding *str_enc = STR_ENC_GET(str);
p = RSTRING_PTR(str); len = RSTRING_LEN(str);
if (coderange_scan(p, beg0, str_enc) != ENC_CODERANGE_7BIT ||
coderange_scan(p+end0, len-end0, str_enc) != ENC_CODERANGE_7BIT) {
rb_raise(rb_eEncCompatError, "incompatible character encodings: %s and %s",
rb_enc_name(str_enc),
rb_enc_name(STR_ENC_GET(repl)));
}
enc = STR_ENC_GET(repl);
}
rb_str_modify(str);
rb_enc_associate(str, enc);
tainted |= OBJ_TAINTED_RAW(repl);
if (ENC_CODERANGE_UNKNOWN < cr && cr < ENC_CODERANGE_BROKEN) {
int cr2 = ENC_CODERANGE(repl);
if (cr2 == ENC_CODERANGE_BROKEN ||
(cr == ENC_CODERANGE_VALID && cr2 == ENC_CODERANGE_7BIT))
cr = ENC_CODERANGE_UNKNOWN;
else
cr = cr2;
}
plen = end0 - beg0;
rp = RSTRING_PTR(repl); rlen = RSTRING_LEN(repl);
len = RSTRING_LEN(str);
if (rlen > plen) {
RESIZE_CAPA(str, len + rlen - plen);
}
p = RSTRING_PTR(str);
if (rlen != plen) {
memmove(p + beg0 + rlen, p + beg0 + plen, len - beg0 - plen);
}
memcpy(p + beg0, rp, rlen);
len += rlen - plen;
STR_SET_LEN(str, len);
TERM_FILL(&RSTRING_PTR(str)[len], TERM_LEN(str));
ENC_CODERANGE_SET(str, cr);
FL_SET_RAW(str, tainted);
return str;
}
return Qnil;
} Выполняет ту же замену, что и #sub, но в том же месте.
Возвращает str если замена была выполнена или nil если замена не была выполнена.
VALUE
rb_str_succ(VALUE orig)
{
VALUE str;
str = rb_str_new_with_class(orig, RSTRING_PTR(orig), RSTRING_LEN(orig));
rb_enc_cr_str_copy_for_substr(str, orig);
OBJ_INFECT(str, orig);
return str_succ(str);
} Возвращает преемник str. Преемник вычисляется путём инкрементации символов, начиная с самого правого буквенно-цифрового символа (или последнего символа, если буквенно-цифровых символов нет) в строке. Инкремент цифры всегда приводит к другой цифре, а инкремент буквы приводит к другой букве той же прописности. Инкремент не буквенно-цифровых символов использует порядок сортировки базового набора символов.
Если инкремент генерирует «перенос», символ слева от него увеличивается. Этот процесс повторяется до тех пор, пока не произойдёт перенос, добавляя дополнительный символ при необходимости.
"abcd".succ #=> "abce" "THX1138".succ #=> "THX1139" "<<koala>>".succ #=> "<<koalb>>" "1999zzz".succ #=> "2000aaa" "ZZZ9999".succ #=> "AAAA0000" "***".succ #=> "**+"
static VALUE
rb_str_succ_bang(VALUE str)
{
rb_str_modify(str);
str_succ(str);
return str;
} Эквивалентно String#succ, но изменяет получатель на месте.
static VALUE
rb_str_sum(int argc, VALUE *argv, VALUE str)
{
VALUE vbits;
int bits;
char *ptr, *p, *pend;
long len;
VALUE sum = INT2FIX(0);
unsigned long sum0 = 0;
if (argc == 0) {
bits = 16;
}
else {
rb_scan_args(argc, argv, "01", &vbits);
bits = NUM2INT(vbits);
if (bits < 0)
bits = 0;
}
ptr = p = RSTRING_PTR(str);
len = RSTRING_LEN(str);
pend = p + len;
while (p < pend) {
if (FIXNUM_MAX - UCHAR_MAX < sum0) {
sum = rb_funcall(sum, '+', 1, LONG2FIX(sum0));
str_mod_check(str, ptr, len);
sum0 = 0;
}
sum0 += (unsigned char)*p;
p++;
}
if (bits == 0) {
if (sum0) {
sum = rb_funcall(sum, '+', 1, LONG2FIX(sum0));
}
}
else {
if (sum == INT2FIX(0)) {
if (bits < (int)sizeof(long)*CHAR_BIT) {
sum0 &= (((unsigned long)1)<<bits)-1;
}
sum = LONG2FIX(sum0);
}
else {
VALUE mod;
if (sum0) {
sum = rb_funcall(sum, '+', 1, LONG2FIX(sum0));
}
mod = rb_funcall(INT2FIX(1), rb_intern("<<"), 1, INT2FIX(bits));
mod = rb_funcall(mod, '-', 1, INT2FIX(1));
sum = rb_funcall(sum, '&', 1, mod);
}
}
return sum;
} Возвращает барический контрольный код длиной n бит символов в str, где n — необязательный параметр, по умолчанию равный 16. Результат — просто сумма двоичного значения каждого байта в str по модулю 2**n - 1. Это не очень хороший контрольный код.
static VALUE
rb_str_swapcase(VALUE str)
{
str = rb_str_dup(str);
rb_str_swapcase_bang(str);
return str;
} Возвращает копию str с прописными буквенными символами, преобразованными в строчные, и строчными буквенными символами, преобразованными в прописные. Примечание: преобразование регистра эффективно только в ASCII области.
"Hello".swapcase #=> "hELLO" "cYbEr_PuNk11".swapcase #=> "CyBeR_pUnK11"
static VALUE
rb_str_swapcase_bang(VALUE str)
{
rb_encoding *enc;
char *s, *send;
int modify = 0;
int n;
str_modify_keep_cr(str);
enc = STR_ENC_GET(str);
rb_str_check_dummy_enc(enc);
s = RSTRING_PTR(str); send = RSTRING_END(str);
while (s < send) {
unsigned int c = rb_enc_codepoint_len(s, send, &n, enc);
if (rb_enc_isupper(c, enc)) {
/* assuming toupper returns codepoint with same size */
rb_enc_mbcput(rb_enc_tolower(c, enc), s, enc);
modify = 1;
}
else if (rb_enc_islower(c, enc)) {
/* assuming tolower returns codepoint with same size */
rb_enc_mbcput(rb_enc_toupper(c, enc), s, enc);
modify = 1;
}
s += n;
}
if (modify) return str;
return Qnil;
} Эквивалентно String#swapcase, но изменяет получатель на месте, возвращая str или nil если изменений не было. Примечание: преобразование регистра эффективно только в ASCII области.
static VALUE
rb_str_tr_s(VALUE str, VALUE src, VALUE repl)
{
str = rb_str_dup(str);
tr_trans(str, src, repl, 1);
return str;
} Обрабатывает копию str, как описано в String#tr, затем удаляет повторяющиеся символы в регионах, которые были затронуты переводом.
"hello".tr_s('l', 'r') #=> "hero"
"hello".tr_s('el', '*') #=> "h*o"
"hello".tr_s('el', 'hx') #=> "hhxo"
static VALUE
rb_str_tr_s_bang(VALUE str, VALUE src, VALUE repl)
{
return tr_trans(str, src, repl, 1);
} Выполняет String#tr_s обработку str на месте, возвращая str или nil, если изменений не было.
# File lib/unicode_normalize.rb, line 30
def unicode_normalize(form = :nfc)
require 'unicode_normalize/normalize.rb' unless defined? UnicodeNormalize
## The following line can be uncommented to avoid repeated checking for
## UnicodeNormalize. However, tests didn't show any noticeable speedup
## when doing this. This comment also applies to the commented out lines
## in String#unicode_normalize! and String#unicode_normalized?.
# String.send(:define_method, :unicode_normalize, ->(form = :nfc) { UnicodeNormalize.normalize(self, form) } )
UnicodeNormalize.normalize(self, form)
end Нормализация Unicode
Возвращает нормализованную форму str, используя нормализации Unicode NFC, NFD, NFKC или NFKD. Используемая форма нормализации определяется form, которая может быть одним из четырёх значений: :nfc, :nfd, :nfkc или :nfkd. По умолчанию используется :nfc.
Если строка не в кодировке Unicode Кодировка, то генерируется Исключение. В данном контексте «кодировка Unicode» означает UTF-8, UTF-16BE/LE и UTF-32BE/LE, а также GB18030, UCS_2BE и UCS_4BE. Всё, что не UTF-8, реализуется путём преобразования в UTF-8, что делает его медленнее, чем UTF-8.
Примеры
"a\u0300".unicode_normalize #=> 'à' (same as "\u00E0")
"a\u0300".unicode_normalize(:nfc) #=> 'à' (same as "\u00E0")
"\u00E0".unicode_normalize(:nfd) #=> 'à' (same as "a\u0300")
"\xE0".force_encoding('ISO-8859-1').unicode_normalize(:nfd)
#=> Encoding::CompatibilityError raised
# File lib/unicode_normalize.rb, line 46
def unicode_normalize!(form = :nfc)
require 'unicode_normalize/normalize.rb' unless defined? UnicodeNormalize
# String.send(:define_method, :unicode_normalize!, ->(form = :nfc) { replace(unicode_normalize(form)) } )
replace(unicode_normalize(form))
end Деструктивная версия #unicode_normalize, выполняет нормализацию Unicode на месте.
# File lib/unicode_normalize.rb, line 71
def unicode_normalized?(form = :nfc)
require 'unicode_normalize/normalize.rb' unless defined? UnicodeNormalize
# String.send(:define_method, :unicode_normalized?, ->(form = :nfc) { UnicodeNormalize.normalized?(self, form) } )
UnicodeNormalize.normalized?(self, form)
end Проверяет, находится ли str в форме нормализации Unicode form, которая может быть одним из четырёх значений: :nfc, :nfd, :nfkc или :nfkd. По умолчанию используется :nfc.
Если строка не в кодировке Unicode Кодировка, то генерируется Исключение. Для подробностей см. #unicode_normalize.
Примеры
"a\u0300".unicode_normalized? #=> false
"a\u0300".unicode_normalized?(:nfd) #=> true
"\u00E0".unicode_normalized? #=> true
"\u00E0".unicode_normalized?(:nfd) #=> false
"\xE0".force_encoding('ISO-8859-1').unicode_normalized?
#=> Encoding::CompatibilityError raised
static VALUE
pack_unpack(VALUE str, VALUE fmt)
{
static const char hexdigits[] = "0123456789abcdef";
char *s, *send;
char *p, *pend;
VALUE ary;
char type;
long len, tmp_len;
int star;
#ifdef NATINT_PACK
int natint; /* native integer */
#endif
int block_p = rb_block_given_p();
int signed_p, integer_size, bigendian_p;
#define UNPACK_PUSH(item) do {\
VALUE item_val = (item);\
if (block_p) {\
rb_yield(item_val);\
}\
else {\
rb_ary_push(ary, item_val);\
}\
} while (0)
StringValue(str);
StringValue(fmt);
s = RSTRING_PTR(str);
send = s + RSTRING_LEN(str);
p = RSTRING_PTR(fmt);
pend = p + RSTRING_LEN(fmt);
ary = block_p ? Qnil : rb_ary_new();
while (p < pend) {
int explicit_endian = 0;
type = *p++;
#ifdef NATINT_PACK
natint = 0;
#endif
if (ISSPACE(type)) continue;
if (type == '#') {
while ((p < pend) && (*p != '\n')) {
p++;
}
continue;
}
star = 0;
{
modifiers:
switch (*p) {
case '_':
case '!':
if (strchr(natstr, type)) {
#ifdef NATINT_PACK
natint = 1;
#endif
p++;
}
else {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
}
goto modifiers;
case '<':
case '>':
if (!strchr(endstr, type)) {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, endstr);
}
if (explicit_endian) {
rb_raise(rb_eRangeError, "Can't use both '<' and '>'");
}
explicit_endian = *p++;
goto modifiers;
}
}
if (p >= pend)
len = 1;
else if (*p == '*') {
star = 1;
len = send - s;
p++;
}
else if (ISDIGIT(*p)) {
errno = 0;
len = STRTOUL(p, (char**)&p, 10);
if (len < 0 || errno) {
rb_raise(rb_eRangeError, "pack length too big");
}
}
else {
len = (type != '@');
}
switch (type) {
case '%':
rb_raise(rb_eArgError, "%% is not supported");
break;
case 'A':
if (len > send - s) len = send - s;
{
long end = len;
char *t = s + len - 1;
while (t >= s) {
if (*t != ' ' && *t != '\0') break;
t--; len--;
}
UNPACK_PUSH(infected_str_new(s, len, str));
s += end;
}
break;
case 'Z':
{
char *t = s;
if (len > send-s) len = send-s;
while (t < s+len && *t) t++;
UNPACK_PUSH(infected_str_new(s, t-s, str));
if (t < send) t++;
s = star ? t : s+len;
}
break;
case 'a':
if (len > send - s) len = send - s;
UNPACK_PUSH(infected_str_new(s, len, str));
s += len;
break;
case 'b':
{
VALUE bitstr;
char *t;
int bits;
long i;
if (p[-1] == '*' || len > (send - s) * 8)
len = (send - s) * 8;
bits = 0;
bitstr = rb_usascii_str_new(0, len);
t = RSTRING_PTR(bitstr);
for (i=0; i<len; i++) {
if (i & 7) bits >>= 1;
else bits = (unsigned char)*s++;
*t++ = (bits & 1) ? '1' : '0';
}
UNPACK_PUSH(bitstr);
}
break;
case 'B':
{
VALUE bitstr;
char *t;
int bits;
long i;
if (p[-1] == '*' || len > (send - s) * 8)
len = (send - s) * 8;
bits = 0;
bitstr = rb_usascii_str_new(0, len);
t = RSTRING_PTR(bitstr);
for (i=0; i<len; i++) {
if (i & 7) bits <<= 1;
else bits = (unsigned char)*s++;
*t++ = (bits & 128) ? '1' : '0';
}
UNPACK_PUSH(bitstr);
}
break;
case 'h':
{
VALUE bitstr;
char *t;
int bits;
long i;
if (p[-1] == '*' || len > (send - s) * 2)
len = (send - s) * 2;
bits = 0;
bitstr = rb_usascii_str_new(0, len);
t = RSTRING_PTR(bitstr);
for (i=0; i<len; i++) {
if (i & 1)
bits >>= 4;
else
bits = (unsigned char)*s++;
*t++ = hexdigits[bits & 15];
}
UNPACK_PUSH(bitstr);
}
break;
case 'H':
{
VALUE bitstr;
char *t;
int bits;
long i;
if (p[-1] == '*' || len > (send - s) * 2)
len = (send - s) * 2;
bits = 0;
bitstr = rb_usascii_str_new(0, len);
t = RSTRING_PTR(bitstr);
for (i=0; i<len; i++) {
if (i & 1)
bits <<= 4;
else
bits = (unsigned char)*s++;
*t++ = hexdigits[(bits >> 4) & 15];
}
UNPACK_PUSH(bitstr);
}
break;
case 'c':
signed_p = 1;
integer_size = 1;
bigendian_p = BIGENDIAN_P(); /* not effective */
goto unpack_integer;
case 'C':
signed_p = 0;
integer_size = 1;
bigendian_p = BIGENDIAN_P(); /* not effective */
goto unpack_integer;
case 's':
signed_p = 1;
integer_size = NATINT_LEN(short, 2);
bigendian_p = BIGENDIAN_P();
goto unpack_integer;
case 'S':
signed_p = 0;
integer_size = NATINT_LEN(short, 2);
bigendian_p = BIGENDIAN_P();
goto unpack_integer;
case 'i':
signed_p = 1;
integer_size = (int)sizeof(int);
bigendian_p = BIGENDIAN_P();
goto unpack_integer;
case 'I':
signed_p = 0;
integer_size = (int)sizeof(int);
bigendian_p = BIGENDIAN_P();
goto unpack_integer;
case 'l':
signed_p = 1;
integer_size = NATINT_LEN(long, 4);
bigendian_p = BIGENDIAN_P();
goto unpack_integer;
case 'L':
signed_p = 0;
integer_size = NATINT_LEN(long, 4);
bigendian_p = BIGENDIAN_P();
goto unpack_integer;
case 'q':
signed_p = 1;
integer_size = NATINT_LEN_Q;
bigendian_p = BIGENDIAN_P();
goto unpack_integer;
case 'Q':
signed_p = 0;
integer_size = NATINT_LEN_Q;
bigendian_p = BIGENDIAN_P();
goto unpack_integer;
case 'n':
signed_p = 0;
integer_size = 2;
bigendian_p = 1;
goto unpack_integer;
case 'N':
signed_p = 0;
integer_size = 4;
bigendian_p = 1;
goto unpack_integer;
case 'v':
signed_p = 0;
integer_size = 2;
bigendian_p = 0;
goto unpack_integer;
case 'V':
signed_p = 0;
integer_size = 4;
bigendian_p = 0;
goto unpack_integer;
unpack_integer:
if (explicit_endian) {
bigendian_p = explicit_endian == '>';
}
PACK_LENGTH_ADJUST_SIZE(integer_size);
while (len-- > 0) {
int flags = bigendian_p ? INTEGER_PACK_BIG_ENDIAN : INTEGER_PACK_LITTLE_ENDIAN;
VALUE val;
if (signed_p)
flags |= INTEGER_PACK_2COMP;
val = rb_integer_unpack(s, integer_size, 1, 0, flags);
UNPACK_PUSH(val);
s += integer_size;
}
PACK_ITEM_ADJUST();
break;
case 'f':
case 'F':
PACK_LENGTH_ADJUST_SIZE(sizeof(float));
while (len-- > 0) {
float tmp;
memcpy(&tmp, s, sizeof(float));
s += sizeof(float);
UNPACK_PUSH(DBL2NUM((double)tmp));
}
PACK_ITEM_ADJUST();
break;
case 'e':
PACK_LENGTH_ADJUST_SIZE(sizeof(float));
while (len-- > 0) {
FLOAT_CONVWITH(tmp);
memcpy(tmp.buf, s, sizeof(float));
s += sizeof(float);
VTOHF(tmp);
UNPACK_PUSH(DBL2NUM(tmp.f));
}
PACK_ITEM_ADJUST();
break;
case 'E':
PACK_LENGTH_ADJUST_SIZE(sizeof(double));
while (len-- > 0) {
DOUBLE_CONVWITH(tmp);
memcpy(tmp.buf, s, sizeof(double));
s += sizeof(double);
VTOHD(tmp);
UNPACK_PUSH(DBL2NUM(tmp.d));
}
PACK_ITEM_ADJUST();
break;
case 'D':
case 'd':
PACK_LENGTH_ADJUST_SIZE(sizeof(double));
while (len-- > 0) {
double tmp;
memcpy(&tmp, s, sizeof(double));
s += sizeof(double);
UNPACK_PUSH(DBL2NUM(tmp));
}
PACK_ITEM_ADJUST();
break;
case 'g':
PACK_LENGTH_ADJUST_SIZE(sizeof(float));
while (len-- > 0) {
FLOAT_CONVWITH(tmp);
memcpy(tmp.buf, s, sizeof(float));
s += sizeof(float);
NTOHF(tmp);
UNPACK_PUSH(DBL2NUM(tmp.f));
}
PACK_ITEM_ADJUST();
break;
case 'G':
PACK_LENGTH_ADJUST_SIZE(sizeof(double));
while (len-- > 0) {
DOUBLE_CONVWITH(tmp);
memcpy(tmp.buf, s, sizeof(double));
s += sizeof(double);
NTOHD(tmp);
UNPACK_PUSH(DBL2NUM(tmp.d));
}
PACK_ITEM_ADJUST();
break;
case 'U':
if (len > send - s) len = send - s;
while (len > 0 && s < send) {
long alen = send - s;
unsigned long l;
l = utf8_to_uv(s, &alen);
s += alen; len--;
UNPACK_PUSH(ULONG2NUM(l));
}
break;
case 'u':
{
VALUE buf = infected_str_new(0, (send - s)*3/4, str);
char *ptr = RSTRING_PTR(buf);
long total = 0;
while (s < send && (unsigned char)*s > ' ' && (unsigned char)*s < 'a') {
long a,b,c,d;
char hunk[3];
len = ((unsigned char)*s++ - ' ') & 077;
total += len;
if (total > RSTRING_LEN(buf)) {
len -= total - RSTRING_LEN(buf);
total = RSTRING_LEN(buf);
}
while (len > 0) {
long mlen = len > 3 ? 3 : len;
if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
a = ((unsigned char)*s++ - ' ') & 077;
else
a = 0;
if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
b = ((unsigned char)*s++ - ' ') & 077;
else
b = 0;
if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
c = ((unsigned char)*s++ - ' ') & 077;
else
c = 0;
if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
d = ((unsigned char)*s++ - ' ') & 077;
else
d = 0;
hunk[0] = (char)(a << 2 | b >> 4);
hunk[1] = (char)(b << 4 | c >> 2);
hunk[2] = (char)(c << 6 | d);
memcpy(ptr, hunk, mlen);
ptr += mlen;
len -= mlen;
}
if (s < send && (unsigned char)*s != '\r' && *s != '\n')
s++; /* possible checksum byte */
if (s < send && *s == '\r') s++;
if (s < send && *s == '\n') s++;
}
rb_str_set_len(buf, total);
UNPACK_PUSH(buf);
}
break;
case 'm':
{
VALUE buf = infected_str_new(0, (send - s + 3)*3/4, str); /* +3 is for skipping paddings */
char *ptr = RSTRING_PTR(buf);
int a = -1,b = -1,c = 0,d = 0;
static signed char b64_xtable[256];
if (b64_xtable['/'] <= 0) {
int i;
for (i = 0; i < 256; i++) {
b64_xtable[i] = -1;
}
for (i = 0; i < 64; i++) {
b64_xtable[(unsigned char)b64_table[i]] = (char)i;
}
}
if (len == 0) {
while (s < send) {
a = b = c = d = -1;
a = b64_xtable[(unsigned char)*s++];
if (s >= send || a == -1) rb_raise(rb_eArgError, "invalid base64");
b = b64_xtable[(unsigned char)*s++];
if (s >= send || b == -1) rb_raise(rb_eArgError, "invalid base64");
if (*s == '=') {
if (s + 2 == send && *(s + 1) == '=') break;
rb_raise(rb_eArgError, "invalid base64");
}
c = b64_xtable[(unsigned char)*s++];
if (s >= send || c == -1) rb_raise(rb_eArgError, "invalid base64");
if (s + 1 == send && *s == '=') break;
d = b64_xtable[(unsigned char)*s++];
if (d == -1) rb_raise(rb_eArgError, "invalid base64");
*ptr++ = castchar(a << 2 | b >> 4);
*ptr++ = castchar(b << 4 | c >> 2);
*ptr++ = castchar(c << 6 | d);
}
if (c == -1) {
*ptr++ = castchar(a << 2 | b >> 4);
if (b & 0xf) rb_raise(rb_eArgError, "invalid base64");
}
else if (d == -1) {
*ptr++ = castchar(a << 2 | b >> 4);
*ptr++ = castchar(b << 4 | c >> 2);
if (c & 0x3) rb_raise(rb_eArgError, "invalid base64");
}
}
else {
while (s < send) {
a = b = c = d = -1;
while ((a = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
if (s >= send) break;
s++;
while ((b = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
if (s >= send) break;
s++;
while ((c = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
if (*s == '=' || s >= send) break;
s++;
while ((d = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
if (*s == '=' || s >= send) break;
s++;
*ptr++ = castchar(a << 2 | b >> 4);
*ptr++ = castchar(b << 4 | c >> 2);
*ptr++ = castchar(c << 6 | d);
a = -1;
}
if (a != -1 && b != -1) {
if (c == -1)
*ptr++ = castchar(a << 2 | b >> 4);
else {
*ptr++ = castchar(a << 2 | b >> 4);
*ptr++ = castchar(b << 4 | c >> 2);
}
}
}
rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
UNPACK_PUSH(buf);
}
break;
case 'M':
{
VALUE buf = infected_str_new(0, send - s, str);
char *ptr = RSTRING_PTR(buf), *ss = s;
int c1, c2;
while (s < send) {
if (*s == '=') {
if (++s == send) break;
if (s+1 < send && *s == '\r' && *(s+1) == '\n')
s++;
if (*s != '\n') {
if ((c1 = hex2num(*s)) == -1) break;
if (++s == send) break;
if ((c2 = hex2num(*s)) == -1) break;
*ptr++ = castchar(c1 << 4 | c2);
}
}
else {
*ptr++ = *s;
}
s++;
ss = s;
}
rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
rb_str_buf_cat(buf, ss, send-ss);
ENCODING_CODERANGE_SET(buf, rb_ascii8bit_encindex(), ENC_CODERANGE_VALID);
UNPACK_PUSH(buf);
}
break;
case '@':
if (len > RSTRING_LEN(str))
rb_raise(rb_eArgError, "@ outside of string");
s = RSTRING_PTR(str) + len;
break;
case 'X':
if (len > s - RSTRING_PTR(str))
rb_raise(rb_eArgError, "X outside of string");
s -= len;
break;
case 'x':
if (len > send - s)
rb_raise(rb_eArgError, "x outside of string");
s += len;
break;
case 'P':
if (sizeof(char *) <= (size_t)(send - s)) {
VALUE tmp = Qnil;
char *t;
memcpy(&t, s, sizeof(char *));
s += sizeof(char *);
if (t) {
VALUE a;
const VALUE *p, *pend;
if (!(a = str_associated(str))) {
rb_raise(rb_eArgError, "no associated pointer");
}
p = RARRAY_CONST_PTR(a);
pend = p + RARRAY_LEN(a);
while (p < pend) {
if (RB_TYPE_P(*p, T_STRING) && RSTRING_PTR(*p) == t) {
if (len < RSTRING_LEN(*p)) {
tmp = rb_tainted_str_new(t, len);
str_associate(tmp, a);
}
else {
tmp = *p;
}
break;
}
p++;
}
if (p == pend) {
rb_raise(rb_eArgError, "non associated pointer");
}
}
UNPACK_PUSH(tmp);
}
break;
case 'p':
if (len > (long)((send - s) / sizeof(char *)))
len = (send - s) / sizeof(char *);
while (len-- > 0) {
if ((size_t)(send - s) < sizeof(char *))
break;
else {
VALUE tmp = Qnil;
char *t;
memcpy(&t, s, sizeof(char *));
s += sizeof(char *);
if (t) {
VALUE a;
const VALUE *p, *pend;
if (!(a = str_associated(str))) {
rb_raise(rb_eArgError, "no associated pointer");
}
p = RARRAY_CONST_PTR(a);
pend = p + RARRAY_LEN(a);
while (p < pend) {
if (RB_TYPE_P(*p, T_STRING) && RSTRING_PTR(*p) == t) {
tmp = *p;
break;
}
p++;
}
if (p == pend) {
rb_raise(rb_eArgError, "non associated pointer");
}
}
UNPACK_PUSH(tmp);
}
}
break;
case 'w':
{
char *s0 = s;
while (len > 0 && s < send) {
if (*s & 0x80) {
s++;
}
else {
s++;
UNPACK_PUSH(rb_integer_unpack(s0, s-s0, 1, 1, INTEGER_PACK_BIG_ENDIAN));
len--;
s0 = s;
}
}
}
break;
default:
rb_warning("unknown unpack directive '%c' in '%s'",
type, RSTRING_PTR(fmt));
break;
}
}
return ary;
} Декодирует str (который может содержать двоичные данные) в соответствии со строкой формата, возвращая массив из каждого извлечённого значения. Строка формата состоит из последовательности односимвольных директив, суммированных в таблице в конце этой записи. Каждая директива может быть дополнена числом, указывающим количество повторений этой директивы. Звёздочка («*») использует все оставшиеся элементы. Директивы sSiIlL могут быть дополнены нижним подчёркиванием («_») или знаком восклицания («!») для использования родного размера платформы для указанного типа; в противном случае используется независимый от платформы согласованный размер. Пробелы в строке формата игнорируются. См. также Array#pack.
"abc \0\0abc \0\0".unpack('A6Z6') #=> ["abc", "abc "]
"abc \0\0".unpack('a3a3') #=> ["abc", " \000\000"]
"abc \0abc \0".unpack('Z*Z*') #=> ["abc ", "abc "]
"aa".unpack('b8B8') #=> ["10000110", "01100001"]
"aaa".unpack('h2H2c') #=> ["16", "61", 97]
"\xfe\xff\xfe\xff".unpack('sS') #=> [-2, 65534]
"now=20is".unpack('M*') #=> ["now is"]
"whole".unpack('xax2aX2aX1aX2a') #=> ["h", "e", "l", "l", "o"]
В этой таблице суммируются различные форматы и классы Ruby, возвращаемые каждым из них.
Integer | |
Directive | Returns | Meaning
-----------------------------------------------------------------
C | Integer | 8-bit unsigned (unsigned char)
S | Integer | 16-bit unsigned, native endian (uint16_t)
L | Integer | 32-bit unsigned, native endian (uint32_t)
Q | Integer | 64-bit unsigned, native endian (uint64_t)
| |
c | Integer | 8-bit signed (signed char)
s | Integer | 16-bit signed, native endian (int16_t)
l | Integer | 32-bit signed, native endian (int32_t)
q | Integer | 64-bit signed, native endian (int64_t)
| |
S_, S! | Integer | unsigned short, native endian
I, I_, I! | Integer | unsigned int, native endian
L_, L! | Integer | unsigned long, native endian
Q_, Q! | Integer | unsigned long long, native endian (ArgumentError
| | if the platform has no long long type.)
| | (Q_ and Q! is available since Ruby 2.1.)
| |
s_, s! | Integer | signed short, native endian
i, i_, i! | Integer | signed int, native endian
l_, l! | Integer | signed long, native endian
q_, q! | Integer | signed long long, native endian (ArgumentError
| | if the platform has no long long type.)
| | (q_ and q! is available since Ruby 2.1.)
| |
S> L> Q> | Integer | same as the directives without ">" except
s> l> q> | | big endian
S!> I!> | | (available since Ruby 1.9.3)
L!> Q!> | | "S>" is same as "n"
s!> i!> | | "L>" is same as "N"
l!> q!> | |
| |
S< L< Q< | Integer | same as the directives without "<" except
s< l< q< | | little endian
S!< I!< | | (available since Ruby 1.9.3)
L!< Q!< | | "S<" is same as "v"
s!< i!< | | "L<" is same as "V"
l!< q!< | |
| |
n | Integer | 16-bit unsigned, network (big-endian) byte order
N | Integer | 32-bit unsigned, network (big-endian) byte order
v | Integer | 16-bit unsigned, VAX (little-endian) byte order
V | Integer | 32-bit unsigned, VAX (little-endian) byte order
| |
U | Integer | UTF-8 character
w | Integer | BER-compressed integer (see Array.pack)
Float | |
Directive | Returns | Meaning
-----------------------------------------------------------------
D, d | Float | double-precision, native format
F, f | Float | single-precision, native format
E | Float | double-precision, little-endian byte order
e | Float | single-precision, little-endian byte order
G | Float | double-precision, network (big-endian) byte order
g | Float | single-precision, network (big-endian) byte order
String | |
Directive | Returns | Meaning
-----------------------------------------------------------------
A | String | arbitrary binary string (remove trailing nulls and ASCII spaces)
a | String | arbitrary binary string
Z | String | null-terminated string
B | String | bit string (MSB first)
b | String | bit string (LSB first)
H | String | hex string (high nibble first)
h | String | hex string (low nibble first)
u | String | UU-encoded string
M | String | quoted-printable, MIME encoding (see RFC2045)
m | String | base64 encoded string (RFC 2045) (default)
| | base64 encoded string (RFC 4648) if followed by 0
P | String | pointer to a structure (fixed-length string)
p | String | pointer to a null-terminated string
Misc. | |
Directive | Returns | Meaning
-----------------------------------------------------------------
@ | --- | skip to the offset given by the length argument
X | --- | skip backward one byte
x | --- | skip forward one byte static VALUE
rb_str_upcase(VALUE str)
{
str = rb_str_dup(str);
rb_str_upcase_bang(str);
return str;
} Возвращает копию str со всеми строчными буквами, заменёнными их заглавными аналогами. Операция нечувствительна к региону — только символы от «a» до «z» затрагиваются. Примечание: замена регистров эффективна только в ASCII области.
"hEllO".upcase #=> "HELLO"
static VALUE
rb_str_upcase_bang(VALUE str)
{
rb_encoding *enc;
char *s, *send;
int modify = 0;
int n;
str_modify_keep_cr(str);
enc = STR_ENC_GET(str);
rb_str_check_dummy_enc(enc);
s = RSTRING_PTR(str); send = RSTRING_END(str);
if (single_byte_optimizable(str)) {
while (s < send) {
unsigned int c = *(unsigned char*)s;
if (rb_enc_isascii(c, enc) && 'a' <= c && c <= 'z') {
*s = 'A' + (c - 'a');
modify = 1;
}
s++;
}
}
else {
int ascompat = rb_enc_asciicompat(enc);
while (s < send) {
unsigned int c;
if (ascompat && (c = *(unsigned char*)s) < 0x80) {
if (rb_enc_isascii(c, enc) && 'a' <= c && c <= 'z') {
*s = 'A' + (c - 'a');
modify = 1;
}
s++;
}
else {
c = rb_enc_codepoint_len(s, send, &n, enc);
if (rb_enc_islower(c, enc)) {
/* assuming toupper returns codepoint with same size */
rb_enc_mbcput(rb_enc_toupper(c, enc), s, enc);
modify = 1;
}
s += n;
}
}
}
if (modify) return str;
return Qnil;
} Преобразует содержимое str в верхний регистр, возвращая nil, если изменений не было. Примечание: замена регистров эффективна только в ASCII области.
static VALUE
rb_str_upto(int argc, VALUE *argv, VALUE beg)
{
VALUE end, exclusive;
VALUE current, after_end;
ID succ;
int n, excl, ascii;
rb_encoding *enc;
rb_scan_args(argc, argv, "11", &end, &exclusive);
RETURN_ENUMERATOR(beg, argc, argv);
excl = RTEST(exclusive);
CONST_ID(succ, "succ");
StringValue(end);
enc = rb_enc_check(beg, end);
ascii = (is_ascii_string(beg) && is_ascii_string(end));
/* single character */
if (RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 && ascii) {
char c = RSTRING_PTR(beg)[0];
char e = RSTRING_PTR(end)[0];
if (c > e || (excl && c == e)) return beg;
for (;;) {
rb_yield(rb_enc_str_new(&c, 1, enc));
if (!excl && c == e) break;
c++;
if (excl && c == e) break;
}
return beg;
}
/* both edges are all digits */
if (ascii && ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0])) {
char *s, *send;
VALUE b, e;
int width;
s = RSTRING_PTR(beg); send = RSTRING_END(beg);
width = rb_long2int(send - s);
while (s < send) {
if (!ISDIGIT(*s)) goto no_digits;
s++;
}
s = RSTRING_PTR(end); send = RSTRING_END(end);
while (s < send) {
if (!ISDIGIT(*s)) goto no_digits;
s++;
}
b = rb_str_to_inum(beg, 10, FALSE);
e = rb_str_to_inum(end, 10, FALSE);
if (FIXNUM_P(b) && FIXNUM_P(e)) {
long bi = FIX2LONG(b);
long ei = FIX2LONG(e);
rb_encoding *usascii = rb_usascii_encoding();
while (bi <= ei) {
if (excl && bi == ei) break;
rb_yield(rb_enc_sprintf(usascii, "%.*ld", width, bi));
bi++;
}
}
else {
ID op = excl ? '<' : rb_intern("<=");
VALUE args[2], fmt = rb_obj_freeze(rb_usascii_str_new_cstr("%.*d"));
args[0] = INT2FIX(width);
while (rb_funcall(b, op, 1, e)) {
args[1] = b;
rb_yield(rb_str_format(numberof(args), args, fmt));
b = rb_funcall(b, succ, 0, 0);
}
}
return beg;
}
/* normal case */
no_digits:
n = rb_str_cmp(beg, end);
if (n > 0 || (excl && n == 0)) return beg;
after_end = rb_funcall(end, succ, 0, 0);
current = rb_str_dup(beg);
while (!rb_str_equal(current, after_end)) {
VALUE next = Qnil;
if (excl || !rb_str_equal(current, end))
next = rb_funcall(current, succ, 0, 0);
rb_yield(current);
if (NIL_P(next)) break;
current = next;
StringValue(current);
if (excl && rb_str_equal(current, end)) break;
if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current) == 0)
break;
}
return beg;
} Итерируется по последовательным значениям, начиная со значения str и заканчивая other_str включительно, передавая каждое значение в блок по очереди. Метод String#succ используется для генерации каждого значения. Если необязательный второй аргумент exclusive опущен или равен false, последнее значение включается; в противном случае оно исключается.
Если блок не задан, возвращается перечислитель.
"a8".upto("b6") {|s| print s, ' ' }
for s in "a8".."b6"
print s, ' '
end
выводит:
a8 a9 b0 b1 b2 b3 b4 b5 b6 a8 a9 b0 b1 b2 b3 b4 b5 b6
Если str и other_str содержат только символы ASCII чисел, оба распознаются как десятичные числа. Кроме того, ширина строки (например, ведущие нули) обрабатывается должным образом.
"9".upto("11").to_a #=> ["9", "10", "11"]
"25".upto("5").to_a #=> []
"07".upto("11").to_a #=> ["07", "08", "09", "10", "11"]
static VALUE
rb_str_valid_encoding_p(VALUE str)
{
int cr = rb_enc_str_coderange(str);
return cr == ENC_CODERANGE_BROKEN ? Qfalse : Qtrue;
} Возвращает true для строки, которая была закодирована правильно.
"\xc2\xa1".force_encoding("UTF-8").valid_encoding? #=> true
"\xc2".force_encoding("UTF-8").valid_encoding? #=> false
"\x80".force_encoding("UTF-8").valid_encoding? #=> false
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.