Zamiana pojedynczych znaków

Witam,

Wiem, że problem jest pewnie lamerski, ale brakuje mi trochę obycia z Rubym :wink:

Potrzebuję zamienić wszystkie polskie znaki (ąęłóńżźć) na ich odpowiedniki w ASCII (aelonzzc). W php mogłem sobie zrobić tak:

[code=php]$polish = array(‘ą’, ‘ę’, ‘ś’, ‘ć’, ‘ż’, ‘ź’, ‘ó’, ‘ł’, ‘ń’, ‘Ą’, ‘Ę’, ‘Ś’, ‘Ć’, ‘Ż’, ‘Ź’, ‘Ó’, ‘Ł’, ‘Ń’);
$replace = array(‘a’, ‘e’, ‘s’, ‘c’, ‘z’, ‘z’, ‘o’, ‘l’, ‘n’, ‘A’, ‘E’, ‘S’, ‘C’, ‘Z’, ‘Z’, ‘O’, ‘L’, ‘N’);

$url = str_replace($polish, $replace, $url);[/code]
Jak podobną operację wykonać w Rubym (łańcuchy mam zapisane w UTF-8)? Próbowałem kombinować z tr(), ale kod:

slug = "ęą ąę dupa" slug = slug.tr('ą', 'a') slug = slug.tr('ę', 'e')
daje mi jakieś dziwne wyniki :expressionless: (dostaję: aeaa aaae dupa)

http://wiki.rubyonrails.com/rails/pages/HowToUseUnicodeStrings lub zaczekaj około dwóch lat na wersję drugą Rubiego.

Bez przesady, powinno jakoś dać się to rozwiązać :wink: PHP ma jeszcze gorszą obsługę unikodu, a jakoś sobie można z tym poradzić.

"jakieś słowo z ś".gsub(/ś/, "s")

Z tr() nie masz co kombinować bo to działa na stringach 8 bitowych. W PHP też ci by ta funkcja nie zadziałała. Aby użyć tr() musisz wpierw string przekonwertować do jakiegoś formatu 8 bitowego. Np.:

[code=ruby]require ‘iconv’

def pl2ascii(s)
ascii = “acelnoszzACELNOSZZ”
cep = “\271\346\352\263\361\363\234\277\237\245\306\312\243\321\323\214\257\217”
s = Iconv.new(“cp1250”, “UTF-8”).iconv(s)
s.tr!(cep,ascii)
end

puts pl2ascii(‘zażółć gęślą jaźń ZAŻÓŁĆ GĘŚLĄ JAŹŃ’) # => zazolc gesla jazn ZAZOLC GESLA JAZN

=begin

Do generacji stringa z kodami cep1250 użyłem Pythona:

pl = [oct(ord©) for c in unicode(‘ąćęłńóśżźĄĆĘŁŃÓŚŻŹ’, ‘utf8’).encode(‘cp1250’)]
print ‘’.join(["\%s" % c[1:] for c in pl])

=> \271\346\352\263\361\363\234\277\237\245\306\312\243\321\323\214\257\217

=end[/code]
Oczywiście, powyższy kod się wysypie jak masz w tekście jakieś niekonwertowalne znaki z utf8 do cp1250.

OK. Dzięki za odpowiedzi.

Mam jeszcze jedną wątpliwość. Przeglądam sobie kod Typo, żeby zobaczyć jak zrealizowana jest tam zamiana tytułu na permalink. Jeśli tytuł brzmi: “Zażółć gęślą jaźń”, to permalink będzie taki: “zażółć-gęślą-jaźń”. Typo korzysta z takiego kodu:

[code=ruby]class String

Converts a post title to its-title-using-dashes

All special chars are stripped in the process

def to_url
return if self.nil?

result = self.downcase

# replace quotes by nothing
result.gsub!(/['"]/, '')

# strip all non word chars
result.gsub!(/\W/, ' ')

# replace all white space sections with a dash
result.gsub!(/\ +/, '-')

# trim dashes
result.gsub!(/(-)$/, '')
result.gsub!(/^(-)/, '')

result

end
end[/code]
Gdy próbuję ten sam kod zastosować u siebie:

[code=ruby]slug = link.title.downcase
slug.gsub!(/[’"]/, ‘’)

strip all non word chars

slug.gsub!(/\W/, ’ ')

replace all white space sections with a dash

slug.gsub!(/\ +/, ‘-’)

trim dashes

slug.gsub!(/(-)$/, ‘’)
slug.gsub!(/^(-)/, ‘’)[/code]
to otrzymuję: “za-g-l-ja”. Od czego to zależy?

To z CodeSnippets: National characters to ascii letters. Moze sie przyda.

Tu jest inna wersja, bardziej w Ruby-way. Wzbogaca wszystkie stringi o dodatkową metodę.

[code=ruby]require ‘iconv’

class String
def to_ascii
ascii = “acelnoszzACELNOSZZ”
cep = “\271\346\352\263\361\363\234\277\237”
s = Iconv.new(“cp1250”, “UTF-8”).iconv(self)
s.tr!(cep,ascii)
end
end

puts ‘zażółć gęślą jaźń’.to_ascii # => zazolc gesla jazn[/code]

Ladnie, bo tyle gsubow mnie przerazalo :slight_smile: Dodatkowo mozna latwo rozszerzyc o inne jezyki.

Dla przykladu podam jave. Uzywalem ibm-owskiej bibl icu4j.

import com.ibm.icu.text.Transliterator;

public class TextUtils {
	
	static final String suplement = "ł>l;Ł>L;ß>ss";
	
	static final String accentID = 
			"NFD; Suplement; [:Nonspacing Mark:] Remove; NFC";
	
	static final String loginID = 
			"NFD; Suplement; [\\P{ASCII}\\P{Alpha}] Remove; Lower; NFC";
	
	//	"Greek-Latin; Cyrillic-Latin; ru-en;"
	
	static TextUtils singleton = null;
	
	private TextUtils() {	
		Transliterator.registerInstance(
				Transliterator.createFromRules("Any-Suplement",
				suplement, Transliterator.FORWARD));
	}
	
	public static TextUtils getInstance() {
		if (singleton == null) {
			singleton = new TextUtils();
		}
		return singleton;
	}
	
	
	public String transliterate(String customID, String text) {
		Transliterator acc = Transliterator.getInstance(customID);
		System.out.println(acc.getID());
		return acc.transliterate(text);
	}
	
	
	public String removeAccents(String text) {	
		return transliterate(accentID, text);
	}
	
	
	public String prepare4LoginName(String text) {	
		return transliterate(loginID, text);
	}
	
	
	public String prepare4LoginName(String text, int maxChars) {
		String s = transliterate(loginID, text);
		return s.length() > maxChars ? s.substring(0, maxChars) : s;
	}
}

Jak widac nie do konca sobie transliterator radzil - stad suplement … ale takie rozw. jest b. wydajne bo nie operuje na regexp.

Kolejne lamerskie pytanie: jak podpiąć ten skrypt do mojej aplikacji? :slight_smile:

Zrobiłem tak: utworzyłem plik string_utils.rb w katalogu lib, z kodem podanym przez jzabiello. W environments.rb dodałem linię: require ‘lib/string_utils’

No i gdy próbuję wywołać to_ascii to albo się pluje, że nie mógł wczytać iconv, albo że metoda nie istnieje. Webrick chce się uruchomić, gdy w environments.rb jest dodana przeze mnie linia.

Wiem, że to pewnie jedna z najprostszych rzeczy i najgłupszych błędów, ale Ruby jest ciągle dla mnie czymś nowym. Szukałem w dokumentacji, ale nie znalazłem. Będę wdzięczny za pomoc.

Dodaj samo require ‘string_utlis’ (bez lib).

To samo. Może po prostu nie mam biblioteki iconv?

Pod eclipse (RDT) dziala. Bibl. iconv jest standardowo w ruby. Prawdop. masz inny encoding pliku niz UTF-8.

Ja dostawalem Iconv::IllegalSequence dla cp1250 i iso8859-2.

Tego typu sprawy zniechecaja do Rubiego vs. Java czy Python.

Pod windowsami standardowo nie ma ani iconv ani gettext. Trzeba to doinstalowac, np. stąd: http://sourceforge.net/project/showfiles.php?group_id=25167.

Uruchom irb i wpisz require 'iconv'
Jak zwróci true to masz zainstalowane.

LoadError: No such file to load.

Czyli lipa.

W pełni zadowoliłoby mnie rozwiązanie używane w typo do tworzenia permalinków (kod, który podałem kilka postów wyżej). Nie mam jednak pojęcia, dlaczego w Typo działa on normalnie, a u mnie usuwa wszystkie polskie znaki.

Mysle, ze chodzi o wlaczenie multibyte support (tzn. dokladnie utf8) - typo ma to globalnie ustawione w envionment.rb

$KCODE = 'u'
require 'jcode'

Opcjonalnie mozesz wlaczyc dla danego wyr. regularnego

expr = /[Ąą]/u  # to 'u' na koncu
puts expr.kcode # => utf8 
# strip all non word chars
result.gsub!(/\W/, ' ')

Powyzszy fragm. z typo pozbawial cie polskich znakow bo nie sa one “word char” w rozumieniu ASCII. /\W/u powinno zalatwic sprawe, ale i tak musisz pozniej podmienic diakrytyki - sprytna metoda zaproponowana przez Jarka Zabiello albo gsub-y z Code Snippets.

PS. Ja mam pod winda standardowo iconv (One-Click Installer, 1.8.4-16 release candidate 1)

Dzięki, o to mi chodziło :slight_smile:

Teraz już śmiga jak trzeba.

[quote=jzabiello][code=ruby]require ‘iconv’

def pl2ascii(s)
ascii = “acelnoszzACELNOSZZ”
cep = “\271\346\352\263\361\363\234\277\237”
s = Iconv.new(“cp1250”, “UTF-8”).iconv(s)
s.tr!(cep,ascii)
end

puts pl2ascii(‘zażółć gęślą jaźń’) # => zazolc gesla jazn[/code]
[/quote]
jak unowocześnić ten kod, żeby przerabiał też duże polskie litery?

[quote=bober0][quote=jzabiello][code=ruby]require ‘iconv’

def pl2ascii(s)
ascii = “acelnoszzACELNOSZZ”
cep = “\271\346\352\263\361\363\234\277\237”
s = Iconv.new(“cp1250”, “UTF-8”).iconv(s)
s.tr!(cep,ascii)
end

puts pl2ascii(‘zażółć gęślą jaźń’) # => zazolc gesla jazn[/code]
[/quote]
jak unowocześnić ten kod, żeby przerabiał też duże polskie litery?[/quote]
Trzeba dodać brakujące kody, zobacz poprawioną wersję