Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: разработка для Web > perl изменить регистр букв


Автор: burakov 20.2.2007, 10:06
Добрый день.
Подскажите пожалуйста как преобразовать слово 
чтобы первая буква оказалась заглавной, а остальные строчными.

типа 
иВаН - Иван.

Пожалуйста кусочек кода smile, если можно. Справку читать нет времени... 

Огромное спасибо.


Автор: Shaggie 20.2.2007, 10:38
Код

$name='iVaN';
$name=ucfirst lc $name;

Автор: chaos 20.2.2007, 10:39
давно не писал на перле. что то типо в этом роде
Код

$stroka[0] = uc($stroka[0]);
for ($n = 1; n < length($stroka); $n++)
    $stroka[$n] = lc($stroka[$n]);


Добавлено @ 10:41 
вот только думаю что возникнут проблемы с кирилицей

Автор: Shaggie 20.2.2007, 10:51
chaos, точно давно не писал.
Код

for ($n = 1; n < length($stroka); $n++) # ошибка, в коде нет переменной $stroka
# тогда уж
for ($n = 1; n < @stroka; $n++)

Автор: amg 20.2.2007, 10:54
Shaggie привел совершенно правильный код.
Чтобы избежать проблем с кодировкой, можно попробовать
Код

use locale;
use POSIX;
# setlocale(LC_CTYPE,"ru_RU.KOI8-R");
setlocale(LC_CTYPE,"ru_RU.CP1251");

Если в виндах это не сработает, то все равно легко сделать.

Автор: chaos 20.2.2007, 11:00
Цитата

# ошибка, в коде нет переменной $stroka

кому как smile

Цитата

# тогда уж

тогда уж дело вкуса ;)

Автор: burakov 20.2.2007, 11:30
Друзья !!!
классные коды, но с кириллицей - ничего не получается (работаю я под windows).

Автор: amg 20.2.2007, 11:36
chaos, Ваш код - это правильный код Си (только доллары убрать) smile Чтобы он годился для Перла, надо завести две переменные (об этом, видимо, говорил Shaggie, строка не является одновременно массивом) и добавить {} вокруг тела цикла (даже если там всего одно выражение)
Код

$stroka = 'iVaN';
@stroka = split '', $stroka;
$stroka[0] = uc($stroka[0]);
for ($n = 1; n < length($stroka); $n++) {
    $stroka[$n] = lc($stroka[$n]);
}

 

Автор: Shaggie 20.2.2007, 11:47
amg, все уже пропустили... smile 
в цикле for в фазе проверки цикла перед переменной n значок доллара пропущен. Так, к слову.

А у меня, кстати, тоже под виндой русские буквы не хотят работать. Кто нибудь знает, как это лечится smile 

Автор: amg 20.2.2007, 11:48
burakov, попробуй так:
Код

$name='иВаН';
# Приводим в порядок англ. буквы
$name=ucfirst lc $name;
# И русские (за искл. ёЁ)
$name =~ s/([А-Я])/chr(ord($1-32))/eg;
$name =~ s/^[а-я]/chr(ord($1+32))/e;

Shaggie, попробуй тоже, а то мне проверить негде

Автор: chaos 20.2.2007, 11:51
amg, а для юникода?

Автор: burakov 20.2.2007, 11:54
Вот я сделал это криво но зато работает - проверил для винды

Код


$name='иВан';
print "$name\n";


$name =~ tr/(А-Я)(A-Z)/(а-я)(a-z)/;
@NAME = split ('', $name);
$NAME[0] =~ tr/а-я/А-Я/;
$name=join ('', @NAME);

print "$name\n";



теперь хочу вывыести это дело в sub &name (подпрограмму) и чтобы она брала переменную, какую подставишь типа
&name($name), но опыта не имею....
подскажите нужно както объявить переменную $name в подпрограмме ЛОКАЛЬНО и вообщем то массив тоже, чтобы они не помешали основному коду программы... 


Автор: amg 20.2.2007, 11:55
Цитата(chaos @  20.2.2007,  11:51 Найти цитируемый пост)
amg, а для юникода?

Не знаю, никогда не сталкивался. use utf8 ? Вскоре появятся опытные люди и скажут.

Автор: chaos 20.2.2007, 11:56
Цитата(amg @ 20.2.2007,  13:36)
1. надо завести две переменные (об этом, видимо, говорил Shaggie)

2. строка не является одновременно массивом

3. и добавить {} вокруг тела цикла (даже если там всего одно выражение

теперь мои замечания smile
1. я могу ни чего не заводить, ибо язык позволяет. а то что кто-то там может забыть это сделать это его проблемы. я показал то что требовалось(хоть и не совсем правельно smile ), а не готовый скрипт

2. запамятовал, бывает

3. тоже запамятовл(в данном примере)
а вот пример где не надо
Код

print for(0..10);

Автор: amg 20.2.2007, 13:32
Цитата(burakov @  20.2.2007,  11:54 Найти цитируемый пост)
теперь хочу вывыести это дело в sub &name (подпрограмму) и чтобы она брала переменную, какую подставишь типа&name($name), но опыта не имею....подскажите нужно както объявить переменную $name в подпрограмме ЛОКАЛЬНО и вообщем то массив тоже, чтобы они не помешали основному коду программы...

Код
$name='иВан';
print "$name\n";
$name = Ucfirst($name);
print "$name\n";

sub Ucfirst { # Кроме ёЁ и только для win
  my $name = $_[0];
  $name =~ tr/А-ЯA-Z/а-яa-z/;
  my @NAME = split ('', $name);
  $NAME[0] =~ tr/а-яa-z/А-ЯA-Z/;
  $name=join ('', @NAME);
  return $name;
}


Автор: burakov 20.2.2007, 14:37
Всем, спасибо.
конструкция в которой используется подпрограмма, пока меня вполне устраивает
ибо работать я буду пока только в cp1251., а 

ёЁ
я заменил принудительно отдельным 
$name =~ tr/ё/e/;
$name =~ tr/Ё/e/;

вроде бы сработало (буква "ё" вообще не нужна заменил ее на "е" да и все)



Автор: amg 20.2.2007, 14:55
Цитата(burakov @  20.2.2007,  14:37 Найти цитируемый пост)
$name =~ tr/ё/e/;
$name =~ tr/Ё/e/;

Можно одним выражением:
$name =~ tr/ёЁ/eе/;
либо
$name =~ s/[ёЁ]/е/g;
или даже без него
$name =~ tr/А-ЯA-ZёЁ/а-яa-zeе/;

Автор: burakov 20.2.2007, 15:21

оставвил  $name =~ tr/А-ЯA-ZёЁ/а-яa-zeе/;
действительно работает. спасибо

еще как то коряво реализован у меня поиск первого символа слова
через split массив и т.д.

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

Автор: amg 20.2.2007, 15:56
Цитата(burakov @  20.2.2007,  15:21 Найти цитируемый пост)
еще как то коряво реализован у меня поиск первого символа слова
Что-то не приходит в голову элегантное решение. Варианты:
Код

$name = ucfirst lc $name;
$name =~ tr/А-ЯёЁ/а-яeе/;
$name =~ s/^([а-я])/chr(ord($1+32))/e;

Код

$name =~ tr/А-ЯA-ZёЁ/а-яa-zeе/;
$name =~ s/^(.)/$_=$1;tr{а-яa-z}{А-ЯA-Z};$_/e;

Автор: Shaggie 20.2.2007, 16:00
amg, элегантность во вред скорости?

По хорошему надо бы локаль настроить, но она мне так и не поддалась smile 

Автор: amg 20.2.2007, 16:55
Цитата(Shaggie @ 20.2.2007,  16:00)
amg, элегантность во вред скорости?

По хорошему надо бы локаль настроить, но она мне так и не поддалась smile

Ну, часто элегантное решение бывает и самым быстрым. А локаль конечно хорошо было бы настроить. Конечно, это самый элегантный (и быстрый) вариант.

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

sub Ucfirst {
  my $name = ucfirst lc $_[0];
  $name =~ tr/ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ/ёйцукенгшщзхъфывапролджэячсмитьбю/;
  (my $first) = $name=~/^(.)/;
  $first =~ tr/ёйцукенгшщзхъфывапролджэячсмитьбю/ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ/;
  substr($name,0,1) = $first;
  return $name;
}


Автор: Shaggie 6.3.2007, 12:35
Нашел интересный подход, спасибо Nitr!
В стандартную поставку Перл входит модуль Encode, позволяющий производить кодирование-декодирование информации.
Код

use Encode qw(encode decode);

# строка в кодировке cp1251
my $content='иВаН';

# декодируем в стандарт
$content=decode('cp1251', $content);

# производим необходимую конвертацию
$content=ucfirst lc $content;

# кодируем для вывода в виндовую консоль
# (иначе можно применить обратно cp1251 или еще что душе угодно)
$content=encode('cp866', $content);

print $content;

или если коротко:
Код

use Encode qw(encode decode);
my $content='иВаН';
print encode('cp866', ucfirst lc decode('cp1251', $content));

У меня работает, выводит "Иван".

Вполне изящное решение smile 

Автор: GoDleSS 27.3.2007, 23:36
Как вариант.

Код

#!/usr/bin/perl

print "Content-type:text/plain;Charset:windows-1251;\n\n";

# С локалью
use locale;
my $name='аБрА';
$name=~s/(\w+)/\u\L$1/gs;
print $name, "\n";

# Без нее
$name='аБрА';
print Ucfirst($name);

sub Ucfirst {
    my $string=shift;
    $string=~s/(\w+)/\u\L$1/gs;
    $string=~s/^(.)//s;
    my $first=$1;
    $first=~tr/[а-я]/[А-Я]/;
    $string=~tr/[А-Я]/[а-я]/;
    $string=$first.$string;
    undef $first;
    return $string;
}

Автор: nitr 28.3.2007, 00:15
GoDleSS, см. комментарии amg, он доходчиво написал, почему ты неправ smile (вариант без локали) 

Автор: GoDleSS 28.3.2007, 09:42
nitr, не говорю, что это полностью грамотный вариант, просто он имеет право на существование.
ёЁ естественно стоит обработать отдельно, если есть необходимость и гораздо лучше будет вариант с транслитерацией не диапазоном, а прямы списком ("tr/ЁЙЦУКЕНГШ..."), - быстрее и точнее.
+ конечно это вариант только для win-1251, либо переделав для любой другой 1 кодировки.

В данном случае, имхо, лучшим решением будет не перевод текста  N-ую кодировку и т.д.(т.к. это лишняя библиотека, если код короткий, либо лишняя ручная обработка), а создание нескольких обработок в одной или нескольких же функциях, либо универсализированной функции.
Что именно я этим хочу сказать:
- пусть есть некая система, в которой используется 1 или неск. функций строкового приведения + может изменяться кодировка вывода.
- пусть кодировка настраивается в неких конфигах(таблица базы, как правило)

И того имеем решение проблемы:
Код

%Chars=SetCharMap($Config{'ENCODE'});

$String=Ucfirst($String, %Chars);

sub SetCharMap {
 my $encode=shift;
 my %Chars;
 if ($encode=~m/win[a-z]*\-1251/i) {
  $Chars{'UC'}='...';
  $Chars{'LC'}='...';
 } elsif ($encode=~m/koi8/i) {
  $Chars{'UC'}='...';
  $Chars{'LC'}='...';
 }
 ...
}

sub Ucfirst {    
    my $string=shift;
    my %Chars=shift;    
    ...
    $string=~tr/$Chars{'UC'}/$Chars{'LC'}/;
    ...    
    return $string;    
}

или подобное, за конкретную работоспособность не уверен, но логика, думаю, понятна.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)