2014-02-19 3 views
0

Update перефразировать мой вопрос:Длина строки умляутов исходящих из файловой системы

У меня есть небольшой скрипт, который создает резюме для каждого каталога в данной папке:

def processDir(dir) 
    title = "Project #{dir}" 
<<EOF 
#{title} 
#{'-' * title.length} 
...  


EOF 
end 

Так что теперь с папками, которые содержат умляуты длину заголовка не соответствует, так что я в конечном итоге с дополнительным тире в некоторых случаях:

phone 
----- 

Propadeutikum 
------------- 

Propädeutikum 
-------------- 
      ^extra dash! 

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

Обновление:: Это действительно странно, в зависимости от того, где я его копирую, я получаю разные результаты в своей консоли.

1.9.3-p448 :012 > "Propädeutikum".length 
=> 13 
1.9.3-p448 :013 > "Propädeutikum".length 
=> 14 

ответ

4

Рубин строку не Supprot только ASCII chracters до сих пор. Таким образом, вы можете использовать драгоценный камень - unicode для этого, когда у вас будет non-ascii charcaters. Смотри также - width.

require "unicode" 

s1 = "Propädeutikum" 
s2 = "Propadeutikum" 
Unicode::width(s1) # => 13 
Unicode::width(s2) # => 13 

прочитать этот пост Re: how to capitalize nonascii characters ?

Привет,

Да, использование Юникода камень на данный момент. Строковые операции над символами не ASCII являются одной из тем для предстоящего Ruby 2.2.

  matz. 
+0

Теперь это сделал трюк – Besi

+0

@Besi Он должен, ждать, пока * 2.2 * as * matz * said. :-) –

+1

@ArupRakshit +1 за информацию, я думаю, что вы должны работать на выборах SO :) Вы много делаете за тег ruby ​​на SO, спасибо за ваши усилия – bjhaid

1

Подобно Мэтта, но может быть немного более эффективным.

"Propädeutikum".each_char.size 
# => 13 

t = Time.now 
500000.times{ 
"Propädeutikum".each_char.size 
} 
puts Time.now - t 
# => 0.364056992 

t = Time.now 
500000.times{ 
"Propädeutikum".chars.count 
} 
puts Time.now - t 
# => 1.462392185 
+0

Такая же проблема, как и версия Мэтта. – Besi

+0

@Besi Это ваша проблема, а не наша. – sawa

+0

OK +1 для вашей статистики времени (хотя это не проблема в моем случае, но полезно знать). Я получаю строки из имен файлов, поэтому, вероятно, это связано с файловой системой. И, конечно, это моя проблема, поэтому я задаю вопрос :-) – Besi

1

Возможно, у вас есть проблема с эквивалентностью Unicode и составленными символами?

См. Следующий пример. Оба текста выглядят одинаково, но кодируются разными способами:

#encoding: utf-8 
text = "Myl\u00E8ne.png" #"Mylène.png" 
text2 = "Myle\u0300ne.png" #"Mylène.png" 

puts text #Mylène.png 
puts text2 #Mylène.png 

puts text.size #10 
puts text2.size #11 

puts text.chars.count #10 
puts text2.chars.count #11 

Некоторые подробности в my answer for Weird Characters encoding.

Вы можете проверить его, если сравнить кодовые страницы ваших текстов с text.codepoints.to_a. В моем примере я получаю:

p text.codepoints.to_a #[77, 121, 108, 232, 110, 101, 46, 112, 110, 103] 
p text2.codepoints.to_a #[77, 121, 108, 101, 768, 110, 101, 46, 112, 110, 103] 
+0

Я думаю, вы правы. 'Unicode :: width (string)' исправил проблему сейчас. – Besi

3

В юникодом некоторые символы, такие как ä, могут быть представлены двумя способами. Они могут быть одиночными кодовыми точками, такими как U + 00E4, в случае ä, или они могут быть сформированы из «базового» символа, за которым следует combining character, например a, за которым следует U + 0308 (КОМБИНИРОВАННАЯ ОПЕРАЦИЯ). В последнем случае комбинированный символ состоит из двух кодовых точек, а метод Ruby String#length возвращает только общее количество кодовых точек, чтобы вы могли получать разные значения для длин кажущихся одних и тех же строк.

s1 = "ä"  # single codepoint 
s2 = "a"  # 'base' letter 
s3 = "a\u0308" # base letter + combining character 

[s1, s2, s3].each do |s| 
    puts "Letter:  #{s}" 
    puts "Bytes:  #{s.bytes}" 
    puts "Codepoints: #{s.codepoints}" 
    puts "Length:  #{s.length}" 
    puts 
end 

Выход:

Letter:  ä 
Bytes:  [195, 164] 
Codepoints: [228] 
Length:  1 

Letter:  a 
Bytes:  [97] 
Codepoints: [97] 
Length:  1 

Letter:  ä 
Bytes:  [97, 204, 136] 
Codepoints: [97, 776] 
Length:  2 

(bytes это кодировка UTF-8 символов в UTF-8 некоторые символы кодируются в виде нескольких байт - это отдельный вопрос от улавливающих символов. .)

Ruby сам по себе (в настоящее время) не имеет большой поддержки для решения таких проблем в Юникоде, поэтому вам нужно использовать внешнюю библиотеку, такую ​​как UnicodeUtils. Идея length может стать довольно неясной при разговоре о разных языках (что считается «единственным символом». Вы можете использовать метод display_width, который, вероятно, даст то, что вы хотите для латинских скриптов. Другая возможность - использовать normalized form, который убеждается все символы представлены точно так же, как все разложено на conbining символы, или все (что у них есть), используя один символ:

require 'unicode_utils' 

combined = "a\u0308" 
single = "ä" 

# nfc - normalized form composed - use a single code point if possible 
puts UnicodeUtils.nfc(combined).length # => 1 
puts UnicodeUtils.nfc(single).length # => 1 

# nfd - normalized form decomposed - always use combining characters 
puts UnicodeUtils.nfd(combined).length # => 2 
puts UnicodeUtils.nfd(single).length # => 2 
+0

Хорошо, это очень интересно. Фактически на моей клавиатуре я могу нажать клавишу '¨', а затем' a', чтобы получить 'ä' это. Я не уверен, что это приводит к другому персонажу, но это та же концепция. – Besi

+1

@Besi Вы правы, это такая же идея, но отдельно от фактических символов. На моей машине (Mac) я могу нажать 'alt + u', а затем' a', чтобы получить 'ä', но результат - единственная версия с кодовым названием. – matt

+0

@Besi, смотря на ваш отредактированный вопрос, 'display_width' (или просто' width' из gem unicode), вероятно, то, что вам нужно в этом случае. – matt