Синхронизация потоков М н о г о п о т о ч н ы й р е ж и м р а б о т ы о т к р ы в а е т новые в о з м о ж н о с т и для п р о г р а м м и с т о в , од нако за эти в о з м о ж н о с т и п р и х о д и т с я р а с п л а ч и в а т ь с я у с л о ж н е н и е м п р о ц е с с а п р о е к т и р о в а н и я п р и л о ж е н и я и о т л а д к и . О с н о в н а я т р у д н о с т ь , с которой с т а л к и в а ю т с я про г р а м м и с т ы , р а н е е н и к о г д а не с о з д а в а в ш и е м н о г о п о т о ч н ы е п р о г р а м м ы , это синхронизация одновременно работающих потоков. Для чего и к о г д а она н у ж н а ? О д н о п о т о ч н а я п р о г р а м м а , т а к а я , н а п р и м е р , как п р о г р а м м а M S - D O S , при запуске п о л у ч а е т в м о н о п о л ь н о е р а с п о р я ж е н и е все р е с у р с ы к о м п ь ю т е р а . Так как в о д н о п о т о ч ной ОС с у щ е с т в у е т т о л ь к о один п р о ц е с с , он и с п о л ь з у е т эти р е с у р с ы в той п о с л е д о в а т е л ь н о с т и , к о т о р а я с о о т в е т с т в у е т л о г и к е р а б о т ы п р о г р а м м ы . П р о ц е с с ы и з а д а ч и , рабо¬ тающие одновременно в многопоточной системе, обращаются одновременно к одним и тем же р е с у р с а м . Если при р а з р а б о т к е п р о г р а м м не у ч и т ы в а т ь это о б с т о я т е л ь с т в о , программы будут работать с ошибками. П о я с н и м это на п р о с т о м Пусть мы с о з д а е м п р о г р а м м у , в ы п о л н я ю щ у ю о п е р а ц и и с б а н к о в с к и м с ч е т о м . Опе¬ рация снятия н е к о т о р о й с у м м ы д е н е г со счета м о ж е т п р о и с х о д и т ь в такой последова¬ тельности: •
на п е р в о м ш а г е п р о в е р я е т с я о б щ а я с у м м а д е н е г , к о т о р а я х р а н и т с я на счету;
•
если эта с у м м а р а в н а или п р е в ы ш а е т размер с н и м а е м о й с у м м ы д е н е г , о б щ а я сумма уменьшается на необходимую величину;
•
з н а ч е н и е о с т а т к а з а п и с ы в а е т с я на т е к у щ и й счет.
Если о п е р а ц и я у м е н ь ш е н и я т е к у щ е г о счета в ы п о л н я е т с я в о д н о п о т о ч н о й то н и к а к и х п р о б л е м не в о з н и к н е т . О д н а к о п р е д с т а в и м себе, что два п р о ц е с с а тока) п ы т а ю т с я о д н о в р е м е н н о в ы п о л н и т ь т о л ь к о что о п и с а н н у ю о п е р а ц и ю и тем же с ч е т о м . П у с т ь при этом на счету н а х о д и т с я 5 млн. д о л л а р о в , а оба п ы т а ю т с я снять с него по 3 млн. д о л л а р о в . Допустим, события разворачиваются следующим образом:
системе, (или по¬ с одним процесса
•
п е р в ы й п р о ц е с с п р о в е р я е т с о с т о я н и е т е к у щ е г о счета и у б е ж д а е т с я , что на нем хра¬ нится 5
•
второй процесс проверяет состояние что на нем х р а н и т с я 5 млн. д о л л а р о в ;
•
п е р в ы й п р о ц е с с у м е н ь ш а е т счет на 3 млн. д о л л а р о в и з а п и с ы в а е т о с т а т о к (2 млн. д о л л а р о в ) на счет;
•
второй п р о ц е с с в ы п о л н я е т ту же с а м у ю о п е р а ц и ю , так как п о с л е п р о в е р к и с ч и т а е т , что на счету п о - п р е ж н е м у х р а н и т с я 5 млн. д о л л а р о в .
текущего
счета
и
также
убеждается,
В р е з у л ь т а т е п о л у ч и л о с ь , что со счета, на к о т о р о м н а х о д и л о с ь 5 м л н . д о л л а р о в , было снято 6 м л н . д о л л а р о в и при этом там осталось еще 2 млн. д о л л а р о в ! И т о г о банку нанесен у щ е р б в 3 млн. д о л л а р о в . Как же с о с т а в и т ь п р о г р а м м у у м е н ь ш е н и я счета, чтобы она не п о з в о л я л а в ы т в о р я т ь подобное?
340
А. В. Фролов, Г В.
Язык СМ. Самоучитель
просто на время в ы п о л н е н и я о п е р а ц и й над счетом о д н и м п р о ц е с с о м не о б х о д и м о з а п р е т и т ь доступ к этому счету со с т о р о н ы д р у г и х п р о ц е с с о в . В этом случае с ц е н а р и й р а б о т ы п р о г р а м м ы д о л ж е н быть с л е д у ю щ и м : •
процесс б л о к и р у е т счет для в ы п о л н е н и я о п е р а ц и й д р у г и м и его в м о н о п о л ь н о е в л а д е н и е ;
процессами, получая
•
п р о ц е с с п р о в о д и т п р о ц е д у р у у м е н ь ш е н и я счета и з а п и с ы в а е т на т е к у щ и й счет н о вое з н а ч е н и е остатка;
•
п р о ц е с с р а з б л о к и р у е т счет, р а з р е ш а я д р у г и м п р о ц е с с а м в ы п о л н е н и е о п е р а ц и й .
К о г д а п е р в ы й п р о ц е с с б л о к и р у е т счет, о н с т а н о в и т с я н е д о с т у п е н д р у г и м п р о ц е с сам. Если в т о р о й процесс т а к ж е п о п ы т а е т с я з а б л о к и р о в а т ь этот же счет, он будет пе¬ реведен в с о с т о я н и е о ж и д а н и я . К о г д а п е р в ы й п р о ц е с с у м е н ь ш и т счет и на нем оста¬ нется 2 м л н . д о л л а р о в , второй п р о ц е с с будет р а з б л о к и р о в а н . Он п р о в е р и т о с т а т о к , у б е д и т с я , что сумма н е д о с т а т о ч н а и не будет п р о в о д и т ь о п е р а ц и ю . Таким о б р а з о м , в м н о г о п о т о ч н о й среде н е о б х о д и м а с и н х р о н и з а ц и я п р о ц е с с о в и п о т о к о в при о б р а щ е н и и к к р и т и ч е с к и м р е с у р с а м . Если н а д такими р е с у р с а м и будут в ы п о л н я т ь с я о п е р а ц и и в н е п р а в и л ь н о й п о с л е д о в а т е л ь н о с т и , это п р и в е д е т к возникно¬ вению трудно обнаруживаемых ошибок. В я з ы к е п р о г р а м м и р о в а н и я С# п р е д у с м о т р е н о н е с к о л ь к о средств с и н х р о н и з а ц и и п о т о к о в , к о т о р ы е мы р а с с м о т р и м в этом р а з д е л е .
Ожидание завершения потока С п о м о щ ь ю метода J o i n , о п р е д е л е н н о г о в классе T h r e a d , п о т о к м о ж е т о ж и д а т ь за¬ в е р ш е н и я р а б о т ы д р у г о г о п о т о к а , для к о т о р о й этот м е т о д в ы з в а н . Предусмотрено 3 перегруженных определения метода public public public
void Join(int); bool
П е р в ы й из них в ы п о л н я е т о ж и д а н и е о г р а н и ч е н и я во в р е м е н и . Для в т о р о г о о ж и д а н и е будет п р е р в а н о п р и н у д и т е л ь н о ч е р е з интервал в р е м е н и , з а д а н н ы й в милли¬ с е к у н д а х . Третий м е т о д п о з в о л я е т у к а з ы в а т ь более д л и т е л ь н ы е и н т е р в а л ы в р е м е н и . М ы р а с с к а з ы в а л и о б этом р а н е е при о п и с а н и и метода S l e e p . У ч т и т е , что р е а л ь н о вы не с м о ж е т е у к а з ы в а т ь в р е м я с т о ч н о с т ь ю до н а н о с е к у н д , так как д и с к р е т н о с т ь с и с т е м н о г о т а й м е р а к о м п ь ю т е р а н а м н о г о б о л ь ш е . Метод может возвращать значение или и з них возвращает¬ ся в том случае, если поток завершил свою работу через заданный интервал времени, а второе — если указанный период времени истек, но поток так и не завершился. А н а л и з и р у я з н а ч е н и е , п о л у ч е н н о е о т м е т о д а J o i n , ваша п р о г р а м м а м о ж е т опреде¬ л и т ь , сумел ли поток з а в е р ш и т ь с я в о т в е д е н н о е для этого время или нет. В зависимо¬ сти от логики работы п р о г р а м м ы в п о с л е д н е м случае м о ж н о п р и б е г н у т ь , н а п р и м е р , к а в а р и й н о м у з а в е р ш е н и ю р а б о т ы « у п р я м о г о » потока. В п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е 10.8, мы п р и в е л и п р и мер и с п о л ь з о в а н и я метода Глава
341
Листинг using using
10.8.
Файл
System;
namespace
JoinDemo
class public
static
string
void
s;
для
завершения
любую работы
строку
или
\"exit\"
s ввели
static
void
Thread
строку
s);
args) new
ReadThread
завершил
свою
М е т о д M a i n этой п р о г р а м м ы с о з д а е т н о в ы й п о т о к R e a d T h r e a d и з а п у с к а е т его н а в ы п о л н е н и е м е т о д о м S t a r t . З а т е м г л а в н ы й п о т о к п р о г р а м м ы (т. е . тот поток, в р а м к а х к о т о р о г о р а б о т а е т м е т о д M a i n ) п е р е в о д и т с я в с о с т о я н и е о ж и д а н и я до тех п о р , пока н е з а в е р ш и т с в о ю р а б о т у п о т о к R e a d T h r e a d : Thread thr new thr.
342
ReadThread
завершил
А.
Фролов, Г.
свою
Фролов. Язык С#. Самоучитель
Когда ж е это, н а к о н е ц , п р о и с х о д и т , главный поток в ы в о д и т н а к о н с о л ь с о о б щ е н и е о завершении р а б о т ы п о т о к а Что ж е п р е д с т а в л я е т собой поток R e a d T h r e a d ? Он в ы п о л н я е т п р о с т у ю работу: в б е с к о н е ч н о м цикле он вводит с к л а в и а т у р ы тек¬ с т о в ы е строки и затем в ы в о д и т их на к о н с о л ь : public
static
string or(
void
s;
"Введите
любую
строку
или
\"exit\"
для
завершения
работы
s ввели
строку
У с л о в и е м з а в е р ш е н и я цикла является в в о д строки R e a d T h r e a d выходит из цикла и завершает свою работу.
s)
exit.
При
этом
поток
Д а н н о е с о б ы т и е о ж и д а е т с я г л а в н ы м п о т о к о м п р и л о ж е н и я при п о м о щ и м е т о д а J o i n . Д о тех п о р , пока р а б о т а е т п о т о к R e a d T h r e a d , г л а в н ы й п о т о к н а ш е й програм¬ мы н а х о д и т с я в с о с т о я н и и о ж и д а н и я .
Критические секции Критические секции
(critical
sections)
являются
наиболее простым
с р е д с т в о м синхро¬
н и з а ц и и п о т о к о в для о б е с п е ч е н и я п о с л е д о в а т е л ь н о г о д о с т у п а к р е с у р с а м . Они рабо¬ т а ю т б ы с т р о и не с н и ж а ю т п р о и з в о д и т е л ь н о с т и с и с т е м ы Как работают критические секции? Если один п о т о к в о ш е л в к р и т и ч е с к у ю с е к ц и ю , но еще не в ы ш е л из н е е , то при по¬ п ы т к е д р у г и х п о т о к о в войти в ту же с а м у ю к р и т и ч е с к у ю с е к ц и ю они будут переведе¬ ны в с о с т о я н и е о ж и д а н и я . П о т о к и п р о б у д у т в этом с о с т о я н и и до тех п о р , пока п о т о к , к о т о р ы й в о ш е л в к р и т и ч е с к у ю с е к ц и ю п е р в ы м , не в ы й д е т из нее. Т а к и м с п о с о б о м г а р а н т и р у е т с я , что ф р а г м е н т кода, з а к л ю ч е н н ы й в н у т р и к р и т и ч е ской
секции,
будет в ы п о л н я т ь с я
потоками
последовательно, если
все они
работают
с одной н той же к р и т и ч е с к о й с е к ц и е й . Если поток р а б о т а е т с н е с к о л ь к и м и р е с у р с а м и , д о с т у п к к о т о р ы м д о л ж е н выпол¬ няться п о с л е д о в а т е л ь н о , о н м о ж е т создать н е с к о л ь к о к р и т и ч е с к и х с е к ц и й . З а м е т и м , ч т о , когда потоки р а б о т а ю т с н е с к о л ь к и м и к р и т и ч е с к и м и с е к ц и я м и , все они д о л ж н ы и с п о л ь з о в а т ь о д и н а к о в у ю п о с л е д о в а т е л ь н о с т ь входа в эти к р и т и ч е с к и е секции и вы¬ х о д а и з них, иначе в о з м о ж н ы в з а и м н ы е б л о к и р о в к и п о т о к о в .
Глава
343
П р и м е н е н и е к л ю ч е в о г о слова lock П р о с т е й ш и й с п о с о б с и н х р о н и з а ц и и п о т о к о в с п о м о щ ь ю к р и т и ч е с к и х секций о с н о в а н н а и с п о л ь з о в а н и и к л ю ч е в о г о слова l o c k , с п е ц и а л ь н о п р е д у с м о т р е н н о г о для этой задачи в я з ы к е п р о г р а м м и р о в а н и я С#. Для того ч т о б ы о ф о р м и т ь ф р а г м е н т кода в виде
секции, достаточно
з а к л ю ч и т ь его в блок l o c k , как это п о к а з а н о н и ж е :
К л ю ч е в о е с л о в о t h i s и с п о л ь з у е т с я здесь для т о г о , ч т о б ы у к а з а т ь , что з а щ и щ а е т с я к о н к р е т н ы й о б ъ е к т класса. В о б щ е м с л у ч а е вы д о л ж н ы задавать в с к о б к а х после к л ю чевого слова l o c k ссылку на о б ъ е к т , и с п о л ь з у е м ы й для с и н х р о н и з а ц и и (т. е. объект, за д о с т у п к к о т о р о м у будут к о н к у р и р о в а т ь н е с к о л ь к о п о т о к о в ) . Рассмотрим следующий пример программы (листинг Листинг using using
10.9.
10.9).
Файл
System;
namespace
L oc k De m o
public
void
i
0;
i
10;
i+ + )
class
[STAThread] static void
args)
NumberWriter Thread Thread
344
thr2
new new new
Thread(new Thread(new А В Фролов, Г. В. Фролов. Язык С#. Самоучитель
Эта п р о г р а м м а создает д в а п о т о к а , к а ж д ы й из к о т о р ы х в ы в о д и т в цикле ч и с л а от О до 9, п о с л е в ы в о д а с о з д а е т с я небольшая з а д е р ж к а : public
void
i
0;
i
10;
i+ + )
Если бы мы не з а к л ю ч и л и тело о п е р а т о р а цикла в блок l o c k , потоки к о н к у р и р о в а ¬ ли бы м е ж д у собой за к о н с о л ь , о т о б р а ж а я числа в х а о т и ч е с к о м п о р я д к е :
Н а л и ч и е блока l o c k г а р а н т и р у е т , ч т о , пока один п о т о к н е з а к о н ч и т в ы в о д , д р у г о й будет н а х о д и т ь с я в с о с т о я н и и о ж и д а н и я . В о т что мы у в и д и м на консоли в р е з у л ь т а т е и с п о л ь з о в а н и я такой с и н х р о н и з а ц и и :
Теперь числа у п о р я д о ч е н ы , так как в н а ч а л е их в ы в о д и л на к о н с о л ь один п о т о к , а затем в т о р о й .
Использование класса Если н е о б х о д и м о о б е с п е ч и т ь п о с л е д о в а т е л ь н о е и с п о л ь з о в а н и е р е с у р с о в п о т о к а м и , с о з д а н н ы м и в р а м к а х р а з н ы х п р о ц е с с о в , в м е с т о к р и т и ч е с к и х секций н е о б х о д и м о ис¬ п о л ь з о в а т ь о б ъ е к т ы с и н х р о н и з а ц и и M u t e x . Свое н а з в а н и е они п о л у ч и л и о т в ы р а ж е н и я e x c l u s i v e , что о з н а ч а е т « в з а и м н о и с к л ю ч а ю щ и й » . О б ъ е к т M u t e x м о ж е т н а х о д и т ь с я в о т м е ч е н н о м или н е о т м е ч е н н о м с о с т о я н и и . Ко¬ гда к а к о й - л и б о поток, п р и н а д л е ж а щ и й л ю б о м у п р о ц е с с у , с т а н о в и т с я в л а д е л ь ц е м объ¬ екта M u t e x , п о с л е д н и й п е р е к л ю ч а е т с я в н е о т м е ч е н н о е с о с т о я н и е . Если же п о т о к «от¬ к а з ы в а е т с я » о т в л а д е н и я о б ъ е к т о м M u t e x , его с о с т о я н и е с т а н о в и т с я о т м е ч е н н ы м . Организация последовательного доступа к ресурсам с использованием объектов M u t e x в о з м о ж н а п о т о м у , что в к а ж д ы й м о м е н т т о л ь к о один п о т о к м о ж е т в л а д е т ь э т и м о б ъ е к т о м . Для того чтобы з а в л а д е т ь о б ъ е к т о м , к о т о р ы й у ж е з а х в а ч е н , все о с т а л ь н ы е потоки в ы н у ж д е н ы ж д а т ь . Д л я того ч т о б ы о б ъ е к т M u t e x был д о с т у п е н п о т о к а м , при¬ н а д л е ж а щ и м р а з л и ч н ы м п р о ц е с с а м , при создании вы д о л ж н ы п р и с в о и т ь ему имя. Глава 10.
345
З а м е т и м , что о б ъ е к т ы в я з ы к е С# р е а л и з у ю т м е х а н и з м с и н х р о н и з а ц и и п о т о ков ОС Microsoft W i n d o w s , д о с т у п н ы й на у р о в н е п р о г р а м м н о г о Win32 этой О С . С р е д с т в а м н о г о п о т о ч н о с т и этой ОС мы п о д р о б н о р а с с м о т р е л и в [10]. Применение класса M u t e x в п р о г р а м м е С# д е м о н с т р и р у е т с я в л и с т и н г е Листинг using using
10.10.
10.10.
Файл
System;
namespace class
NumberWriter
Mutex
mutex;
public mutex
public
new M u t e x ( f a l s e ,
void
for(int i
0;
i
10;
i+ + ) i);
class
static
void
NumberWriter Thread Thread
346
thr2
args) nw
new
new T h r e a d ( n e w new T h r e a d ( n e w
А В. Фролов, Г. В, Фролов. Язык С#. Самоучитель
Эта п р о г р а м м а очень н а п о м и н а е т п р е д ы д у щ у ю п р о г р а м м у (см. л и с т и н г 9.10), од¬ нако для р е а л и з а ц и и к р и т и ч е с к о й секции мы и с п о л ь з о в а л и в ней не к л ю ч е в о е слово а класс M u t e x . И з м е н е н и я затрагивают только класс
отображающий на консоли
в о т д е л ь н о м п о т о к е числа от 0 до 9: class
NumberWriter
Mutex
mutex;
public mutex
public
new M u t e x ( f a l s e ,
void
i
0;
i
10;
i+ + )
Как в и д и т е , теперь в этом классе и м е е т с я к о н с т р у к т о р , с о з д а ю щ и й о б ъ е к т класса Mutex: mutex
new M u t e x ( f a l s e ,
С у щ е с т в у е т н е с к о л ь к о п е р е г р у ж е н н ы х к о н с т р у к т о р о в в классе M u t e x . И х прото¬ т и п ы мы п р и в е л и н и ж е : public public
Mutex Mutex(bool); Mutex(bool,
П е р в ы й и з них с о з д а е т о б ъ е к т класса M u t e x , о б л а д а ю щ и й с в о й с т в а м и , п р и н я т ы м и по
умолчанию.
Второй
конструктор
позволяет указать,
должен
ли
объект
класса
M u t e x с о з д а в а т ь с я в н е о т м е ч е н н о м с о с т о я н и и , т. е. д о л ж е н ли им владеть вызываю¬ щий поток. Третий к о н с т р у к т о р а н а л о г и ч е н п р е д ы д у щ е м у , о д н а к о д о п о л н и т е л ь н о по¬ зволяет указать имя о б ъ е к т а M u t e x , д о с т у п н о е з а п р е д е л а м и т е к у щ е г о процесса. В нашей п р о г р а м м е мы создали о б ъ е к т класса M u t e x в о т м е ч е н н о м с о с т о я н и и . Та¬ ким о б р а з о м , п о т о к , с о з д а в ш и й M u t e x , н е с т а н о в и т с я его в л а д е л ь ц е м . Глава
В х о д в к р и т и ч е с к у ю с е к ц и ю в ы п о л н я е т с я в нашей п р о г р а м м е
образом:
Вызов метода без п а р а м е т р о в п р и в е д е т к т о м у , что п р о г р а м м а будет на¬ х о д и т ь с я в к р и т и ч е с к о й секции н е о п р е д е л е н н о д о л г о , пока она не п о к и н е т его, закрыв о б ъ е к т класса M u t e x я в н ы м о б р а з о м . В нашей п р о г р а м м е в ы х о д из к р и т и ч е с к о й секции в ы п о л н я е т с я т а к :
Здесь м ы п р о с т о в ы з ы в а е м метод C l o s e , о п р е д е л е н н ы й в классе M u t e x . В классе M u t e x и м е е т с я еще два метода позволяющих задать время о ж и д а н и я , а также д е й с т в и я , п р е д п р и н и м а е м ы е после его и с т е ч е н и я :
Они о т л и ч а ю т с я л и ш ь с п о с о б о м о п р е д е л е н и я в р е м е н н о г о и н т е р в а л а (в первом слу¬ чае время у к а з ы в а е т с я в м и л л и с е к у н д а х , а во в т о р о м — задается при п о м о щ и класса
Использование класса Monitor И н а к о н е ц , еще о д н о с р е д с т в о с и н х р о н и з а ц и и п о т о к о в , к о т о р о е мы р а с с м о т р и м в на¬ шей к н и г е , это класс M o n i t o r . Он н е с к о л ь к о п р о щ е в и с п о л ь з о в а н и и по с р а в н е н и ю с только что р а с с м о т р е н н ы м к л а с с о м M u t e x , о д н а к о он не п о з в о л я е т с и н х р о н и з и р о ¬ вать п о т о к и , в ы п о л н я е м ы е в р а м к а х р а з н ы х п р о ц е с с о в . В п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е класс M o n i t o r для с о з д а н и я к р и т и ч е с к о й с е к ц и и . 10.11.
Листинг using using
мы п р и м е н и л и
Файл
System;
namespace class
NumberWriter
public
void
(int i
0; i
10; i+ + ) i);
348
В. Фролов, Г, В, Фролов. Язык С#. Самоучитель
static
void
args)
NumberWriter Thread Thread
thr2
new new new
Thread(new Thread(new
Все с а м о е и н т е р е с н о е н а х о д и т с я в н у т р и о б ъ я в л е н и я м е т о д а W r i t e T h r e a d , п о л н я ю щ е г о с я в р а м к а х о т д е л ь н о г о потока: public
вы¬
void
for(int i
0;
i
10;
i+ + ) i)
Как в и д и т е , в х о д в к р и т и ч е с к у ю с е к ц и ю в ы п о л н я е т с я при п о м о щ и м е т о д а E n t e r , а в ы х о д из нее — при п о м о щ и м е т о д а E x i t . О б о и м э т и м м е т о д а м в к а ч е с т в е парамет¬ ра н е о б х о д и м о п е р е д а в а т ь ссылку на о б ъ е к т с и н х р о н и з а ц и и . П о м и м о методов E n t e r и E x i t , в классе M o n i t o r есть еще ряд полезных методов. Н а п р и м е р , с п о м о щ ь ю м е т о д а T r y E n t e r п р о г р а м м а м о ж е т о п р е д е л и т ь , заблокирова¬ на ли к р и т и ч е с к а я секция. Э т о п о з в о л и т п о т о к у , н а п р и м е р , в х о д и т ь в к р и т и ч е с к у ю с е к ц и ю т о л ь к о в т о м с л у ч а е , если она с в о б о д н а , избегая б л о к и р о в к и . Более подробную и н ф о р м а ц и ю о средствах синхронизации потоков, доступных п р и л о ж е н и я м С#, вы найдете в с п р а в о ч н о й с и с т е м е M S D N , в х о д я щ е й в состав систе¬ мы р а з р а б о т к и Microsoft Visual Studio
Глава 10.
349
Глава 11. Делегаты и события В п р е д ы д у щ е й главе мы уже р а с с к а з ы в а л и вам о д е л е г а т а х , с л у ж а щ и х в качестве не¬ п л о х о й з а м е н ы у к а з а т е л я м на ф у н к ц и и . В этой главе мы р а с с м о т р и м д е л е г а т ы более детально. Ч т о б ы вам б ы л о п о н я т н е е н а з н а ч е н и е д е л е г а т о в , р а с с к а ж е м п о д р о б н е е о ф у н к ц и я х о б р а т н о г о в ы з о в а и их п р и м е н е н и и . Вы з н а е т е , что п р о г р а м м ы в качестве а р г у м е н т о в п р о г р а м м ы м о г у т п е р е д а в а т ь ме¬ т о д а м ссылки на п а р а м е т р ы — п е р е м е н н ы е , числа, м а с с и в ы и т. п. С у щ е с т в у е т и еще одна в о з м о ж н о с т ь : в качестве а р г у м е н т о в м о ж н о п е р е д а в а т ь м е т о д а м с с ы л к и на дру¬
гие методы. П р е д с т а в и м с е б е , что нам н у ж н о р а с п е ч а т ы в а т ь массив с т р о к на р а з н ы х п р и н т е р а х , п р и ч е м для к а ж д о г о п р и н т е р а п р е д у с м о т р е н а с о б с т в е н н а я п р о ц е д у р а печати. Эта про¬ ц е д у р а м о ж е т быть о ф о р м л е н а , н а п р и м е р , в виде м е т о д а к а к о г о - л и б о класса. В з а в и с и м о с т и от тех или и н ы х условий п р о г р а м м а п е ч а т и могла бы п е р е д а в а т ь м е т о д у , о т в е т с т в е н н о м у за п е ч а т ь , ссылку на тот или иной м е т о д , р е а л и з у ю щ и й осо¬ бенности конкретного принтера. Д р у г о й п р и м е р , когда т р е б у ю т с я ф у н к ц и и о б р а т н о г о в ы з о в а , — с о р т и р о в к а произ¬ в о л ь н ы х о б ъ е к т о в . Э т о т п р и м е р в с т р е ч а е т с я п р а к т и ч е с к и во всех у ч е б н и к а х по про¬ г р а м м и р о в а н и ю . С о з д а е т с я о б щ и й к о д , п р и г о д н ы й для с о р т и р о в к и р а з л и ч н ы х объек¬ т о в , а п р о ц е д у р ы с р а в н е н и я о б ъ е к т о в , к о т о р ы е зависят от типа с р а в н и в а е м ы х объек¬ т о в , о ф о р м л я ю т с я в виде ф у н к ц и й о б р а т н о г о вызова. П р и с о з д а н и и м н о г о п о т о ч н ы х п р о г р а м м , р а с с м о т р е н н ы х в п р е д ы д у щ е й главе, так¬ же н е о б х о д и м м е х а н и з м о б р а т н о г о вызова. Н а п о м н и м , что для з а п у с к а метода в от¬ д е л ь н о м п о т о к е нам н у ж н о создать д л я этого м е т о д а д е л е г а т , т. е. ссылку на метод. П р и этом п р о г р а м м а н и к о г д а не в ы з ы в а е т н а п р я м у ю м е т о д , п р е д н а з н а ч е н н ы й для ра¬ боты в о т д е л ь н о м п о т о к е . В м е с т о э т о г о ссылка на д а н н ы й м е т о д п е р е д а е т с я конструк¬ тору класса T h r e a d , ф о р м и р у ю щ е м у новый поток. Ф у н к ц и и о б р а т н о г о вызова часто и с п о л ь з у ю т с я для о б р а б о т к и р е д к о в о з н и к а ю щ и х с о б ы т и й . Т и п и ч н ы й п р и м е р — п е р е д а ч а д а н н ы х по сети. П р и этом п р о г р а м м а ини¬ ц и и р у е т п р о ц е с с п е р е д а ч и или п р и е м а блока д а н н ы х , но не д о ж и д а е т с я его заверше¬ ния. К о г д а ж е д а н н ы е будут п о л у ч е н ы , д р а й в е р с е т е в о г о п р о т о к о л а , о т в е ч а ю щ и й за передачу данных, вызовет функцию программы, предназначенную для обработки д а н н ы х . С с ы л к а н а эту ф у н к ц и ю п е р е д а е т с я д р а й в е р у при и н и ц и и р о в а н и и п р о ц е с с а передачи данных. Механизм функций обратного вызова реализован во многих языках программиро¬ в а н и я . В я з ы к а х С и С++ для этого п р и м е н я ю т с я у к а з а т е л и на ф у н к ц и и , т. е. перемен¬ н ы е , с о д е р ж а щ и е адрес ф у н к ц и й . О д н а к о , как мы н е о д н о к р а т н о з а м е ч а л и , у к а з а т е л и С и о б л а д а ю т в е с ь м а с у щ е с т в е н н ы м н е д о с т а т к о м — они н е б е з о п а с н ы в и с п о л ь з о в а п р о г р а м м а м о ж е т по о ш и б к е л е г к о и з м е н и т ь с о д е р ж и м о е у к а з а т е л я т а к и м обра¬ зом, что он у к а з ы в а т ь не туда, куда н у ж н о . В р е з у л ь т а т е п о в е д е н и е п р о г р а м м ы станет н е п р е д с к а з у е м ы м .
350
В этом с м ы с л е д е л е г а т ы С# п о л н о с т ь ю б е з о п а с н ы . Д е л е г а т м о ж е т с о д е р ж а т ь толь¬ ко ссылку на м е т о д , о б ъ я в л е н н ы й в п р о г р а м м е , и н и ч е г о б о л ь ш е . Вы не с м о ж е т е изме¬ нить с о д е р ж и м о е д е л е г а т а н и к а к и м н е п р е д у с м о т р е н н ы м с п о с о б о м , что с у щ е с т в е н н о уменьшает вероятность возникновения ошибки.
Использование делегатов Р а с с м о т р и м п р о с т е й ш и й п р и м е р делегата для х р а н е н и я ссылки н а м е т о д ы , р еал и з у ю ¬ щий в о з м о ж н о с т и р а з н ы х п р и н т е р о в . Наша программа (листинг д в а ж д ы р а с п е ч а т а е т на в и р т у а л ь н о м п р и н т е р е (вы¬ п о л н я ю щ е м вместо н а с т о я щ е й печати в ы в о д строк н а к о н с о л ь ) с о д е р ж и м о е м а с с и в а строк. 11.1. using
Файл
System;
namespace
public
delegate
public
void
void str,
StringPrinter
printer)
class public
static
void
public
static
void
str)
str)
J
static
void
string[]
Глава
args) Lines
Делегаты и события
351
new
new на foreach
(string
первом
CurrentString
in
Lines)
StringPrinter new на foreach
(string
mp
PrintString
втором
CurrentString
in
Lines)
В первый раз программа распечатает массив строк на принтере, который просто выведет все строки массива на консоль, разделив их пробелами. Второй, принтер добавит после каждой отображаемой строки дополнительную точку: Печать Печать Для
на на
первом принтере: втором принтере:
реализации
функции
This is С# s t r i n g T h i s . i s . С#. печати
в
нашей
array array.
программе
предусмотрен
класс
M y P r i n t e r , объявленный с л е д у ю щ и м образом: class
MyPrinter
public
delegate
public
void
void str,
StringPrinter
printer)
Обратите внимание, что в качестве члена этого класса мы объявили поле-делегат
public
delegate
void
Получив управление, м е т о д M a i n создает объект класса M y P r i n t e r , при п о м о щ и которого наша программа б у д е т распечатывать строки массива: M y P r i n t e r mp
352
new A
Г. В. Фролов. Язык
Самоучитель
Д а л е е она и н и ц и а л и з и р у е т д е л е г а т S t r i n g P r i n t e r , н а «простой» п р и н т е р S i m p l e P r i n t e r :
записывая
в
него
ссылку
StringPrinter new П о с л е такой и н и ц и а л и з а ц и и м о ж н о п р и с т у п а т ь к печати: на п е р в о м п р и н т е р е : foreach
(string
in
Lines)
В процессе п е ч а т и , в ы п о л н я е м о м м е т о д о м P r i n t S t r i n g и з класса M y P r i n t e r , м ы п е р е д а е м этому методу д е л е г а т S t r i n g P r i n t e r . Таким с п о с о б о м м ы с о о б щ а е м методу P r i n t S t r i n g , какой м е т о д д о л ж е н и с п о л ь з о в а т ь с я д л я печати. К о г д а с о д е р ж и м о е массива п е ч а т а е т с я в п е р в ы й раз, в роли в и р т у а л ь н о г о п р и н т е р а будет и с п о л ь з о в а н м е т о д public
static
void
str)
П е р е д тем как п е ч а т а т ь с о д е р ж и м о е м а с с и в а во второй раз, п р о г р а м м а з а п и с ы в а е т в делегат S t r i n g P r i n t e r ссылку на другой виртуальный принтер, реализованный методом SmartPrinter: StringPrinter new Д а л е е мы п о в т о р я е м цикл печати в н е и з м е н н о м виде: на foreach
(string
CurrentString
втором in
принтере:
Lines)
о д н а к о , печать будет в ы п о л н я т ь с я п о - д р у г о м у , так как за нее б у д е т отве¬ чать м е т о д S r a a r t P r i n t e r : public
static
void
str)
Console. Write
str)
О т у п о м я н у т о г о в ы ш е м е т о д а S i r a p l e P r i n t e r метод S r a a r t P r i n t e r о т л и ч а е т с я т е м , что он д о б а в л я е т точку к к а ж д о й р а с п е ч а т ы в а е м о й с т р о к е .
Статические делегаты В п р е д ы д у щ е м п р и м е р е п р о г р а м м ы мы создали один д е л е г а т и з а т е м , в п р о ц е с с е рабо¬ ты п р о г р а м м ы , з а п и с ы в а л и в него ссылки на р а з н ы е м е т о д ы . С у щ е с т в у е т и д р у г а я в о з м о ж н о с т ь , о с н о в а н н а я н а о б ъ я в л е н и е д е л е г а т о в как с т а т и ч е с к и х ч л е н о в к л а с с а .
Глава 12 Язык С#
Делегаты и события
Такие д е л е г а т ы и н и ц и а л и з и р у ю т с я вместе с д р у г и м и с т а т и ч е с к и м и ч л е н а м и п ер ед ис пользованием класса и проще в употреблении. Р а с с м о т р и м п р и м е р п р о г р а м м ы , и с х о д н ы й текст к о т о р о й приведен в 1 Листинг using
Файл System;
namespace class
DelegateStatic MyPrinter
public
delegate
public
void
void str,
StringPrinter
printer)
printer
class
DelegateStaticApp
public
static
void
str) str)
public
static
void
str) str)
public static readonly stringSimplePrinter new public
static
readonly
new static
void
args) Lines
354
А В Фролов,
В Фролов.
Самоучитель
M y P r i n t e r mp
new на
foreach
(string
первом
CurrentString
на foreach
(string
CurrentString
принтере:
in
Lines)
втором in
Lines)
От п р е д ы д у щ е й п р о г р а м м ы она о т л и ч а е т с я т е м , что теперь мы о б ъ я в и л и два стати¬ ческих делегата, первый из которых содержит ссылку на виртуальный принтер S i r a p l e P r i n t e r , а второй на виртуальный принтер S m a r t P r i n t e r : public new
static
readonly
public
static
readonly
StringPrinter
stringSimplePrinter
new Эти д е л е г а т ы о б ъ я в л е н ы т о л ь к о ч и т а е м ы м и с п о м о щ ь ю к л ю ч е в о г о слова r e a d o n l y , п о э т о м у наша п р о г р а м м а п о о ш и б к е н е с м о ж е т и з м е н и т ь х р а н я щ и е с я в Как м е т о д M a i n п о л ь з у е т с я с т а т и ч е с к и м и д е л е г а т а м и ? П р и печати н а « п р о с т о м » п р и н т е р е д е л е г а т передается методу P r i n t S t r i n g ( в ы п о л н я ю щ е м у п е ч а т ь ) в качестве п а р а м е т р а : на foreach
(string
первом
CurrentString
in
принтере: Lines)
Этот д е л е г а т был на этапе и н и ц и а л и з а ц и и п р о г р а м м ы — он с о д е р ж и т с с ы л к у н а метод S i m p l e P r i n t e r . П е ч а т ь на в т о р о м п р и н т е р е а н а л о г и ч н о , но с и с п о л ь з о в а н и е м д р у г о г о а именно делегата на foreach
(string
CurrentString
втором in
Lines)
Так как этот д е л е г а т был п р о и н и ц и а л и з и р о в а н с с ы л к о й н а м е т о д S m a r t P r i n t e r , во второй раз печать будет в ы п о л н я т ь с я « п о - у м н о м у » , т. е. с д о б а в л е н и е м т о ч к и . Глава
Делегаты и события
355
Массивы делегатов При н е о б х о д и м о с т и п р и л о ж е н и е м о ж е т с о з д а в а т ь м а с с и в ы д е л е г а т о в , их с с ы л к а м и на Это п о з в о л я е т в ы б и р а т ь для о б р а б о т к и ф у н к ц и ю об¬ р а т н о г о в ы з о в а д и н а м и ч е с к и , в о время р а б о т ы п р о г р а м м ы . Этот п р и е м м ы д е м о н с т р и р у е м н а п р и м е р е приведен в листинге Листинг using
п р о г р а м м ы , и с х о д н ы й текст к о т о р о й
Файл System;
namespace class
DelegateArray MyPrinter
public
delegate
public
void
class
void str,
StringPrinter
printer)
DelegateArrayApp
public
static
void
str)
public
static
void
str)
public
static
void
str)
Console.WriteC
static
void
args) Lines
356
А В
Г. В. Фролов. Язык
Самоучитель
MyPrinter
new M y P r i n t e r StringPrinter
new new new new на foreach
(string
первом
CurrentString
на foreach
(string
(string
Lines)
втором
CurrentString
на foreach
in
принтере:
in
Lines)
третьем
CurrentString
in
принтере:
Lines)
В о с н о в н о м классе п р о г р а м м ы D e l e g a t e A r r a y A p p мы о п р е д е л и л и ждый
3
м е т о д а , ка
из которых играет роль виртуального принтера:
public
static
void
str)
public
static
void
str)
public
static
void
str)
Эти методы аналогичны тем, что мы использовали в нашей п р е д ы д у щ е й програм ме.
П е р в ы й из них при выводе строки добавляет к ее концу символ пробела, второй
точку, а третий
Делегаты
символ подчеркивания.
357
В теле метода M a i n м ы объявили массив делегатов M y P r i n t e r new Далее н а ш а п р о г р а м м а и н и ц и а л и з и р у е т я ч е й к и м а с с и в а , з а п и с ы в а я в них д е л е г а т ы , с о з д а н н ы е на соответствующих методов: new new new Т е п е р ь , ч т о б ы в ы б р а т ь н у ж н ы й нам п р и н т е р , м ы м о ж е м у к а з а т ь и н д е к с соответст¬ вующего делегата в массиве: на foreach
(string
первом
CurrentString
на foreach
(string
CurrentString
на foreach
(string
CurrentString
in
Lines)
втором принтере: in
Lines)
третьем in
принтере:
Lines)
Так как в п р о ц е с с е своей р а б о т ы п р о г р а м м а может д и н а м и ч е с к и о б р а щ а т ь с я к лю¬ бым н у ж н ы м е й я ч е й к а м м а с с и в а , д а н н ы й с п о с о б о б е с п е ч и в а е т д и н а м и ч е с к и й в ы б о р н е о б х о д и м о й ф у н к ц и и о б р а т н о г о в ы з о в а ( т о ч н е е г о в о р я , м е т о д а о б р а т н о г о вызова, хо¬ тя э т о т т е р м и н и не я в л я е т с я о б щ е п р и н я т ы м ) .
Обработка событий Самые первые п р о г р а м м ы , создаваемые для к о м п ь ю т е р о в , представляли собой потоки м а ш и н н ы х команд, и с п о л н я е м ы х л и н е й н о . Л и н е й н ы й ход программы нарушали лишь ус¬ ловные или циклические операторы. О б ы ч н о такие программы получали в начале своей работы все н е о б х о д и м ы е д а н н ы е ( н а п р и м е р , через перфокарты или перфоленты), а после завершения выдавали результат на устройство печати, перфокарты и перфоленты. О п и с а н н ы й р е ж и м обработки д а н н ы х называется пакетным, так как к о м п ь ю т е р загру¬ жает по очереди пакеты программ с д а н н ы м и и обрабатывает их, выдавая результат. С о в р е м е н н ы е д и а л о г о в ы е п р о г р а м м ы , н а п и с а н н ы е для п е р с о н а л ь н ы х компьюте¬ р о в , р а б о т а ю т с о в е р ш е н н о п о - д р у г о м у . П о с л е запуска они в о с п р и н и м а ю т д е й с т в и я п о л ь з о в а т е л е й над м ы ш ь ю и к л а в и а т у р о й как события (events), в ы п о л н я я при в о з н и к н о в е н и и р а з л и ч н ы х с о б ы т и й т с или и н ы е д е й с т в и я . Г о в о р я т , что работа с о в р е м е н н ы х д и а л о г о в ы х п р о г р а м м с г р а ф и ч е с к и м и н т е р ф е й с о м управляется событиями (events program).
358
А В. Фролов, Г. В, Фролов. Язык С#. Самоучитель
Щ е л к а я м ы ш ь ю , н а п р и м е р , кнопки и н с т р у м е н т а л ь н о й л и н е й к и , п о л ь з о в а т е л ь м о в ы з ы в а т ь на экран те или иные д и а л о г о в ы е окна. При к а ж д о м т а к о м щ е л ч к е ОС с о б ы т и я (т. с. щелчки м ы ш ь ю ) , п е р е д а в а я у п р а в л е н и е р а з л и ч н ы м фраг¬ ментам программы.
Эти
фрагменты
программы
выполняют асинхронную обработку
с о б ы т и й , так как д е й с т в и я п о л ь з о в а т е л е й над п р о г р а м м о й а с и н х р о н н ы п о о т н о ш е н и ю к р а б о т е самой п р о г р а м м ы и с о в е р ш е н н о н е п р е д с к а з у е м ы . Если п р о г р а м м а р а б о т а е т в м н о г о п о т о ч н о м р е ж и м е , то в ней м о г у т в о з н и к а т ь со¬ б ы т и я , с в я з а н н ы е не только с д е й с т в и я м и п о л ь з о в а т е л е й , но и с р а б о т о й д о п о л н и т е л ь ¬ ных п о т о к о в . Е с л и , н а п р и м е р , п р о г р а м м а з а п у с к а е т п о т о к , в ы п о л н я ю щ и й к а к у ю - л и б о д л и т е л ь н у ю работу ( н а п р и м е р , п о и с к и н ф о р м а ц и и в базе д а н н ы х ) , то о к о н ч а н и е этой р а б о т ы м о ж е т р а с с м а т р и в а т ь с я в качестве п р и м е р а т а к о г о с о б ы т и я .
Публикация событий В о т л и ч и е от м н о г и х д р у г и х я з ы к о в п р о г р а м м и р о в а н и я , в к о т о р ы х о б р а б о т к а с о б ы т и й в ы п о л н я е т с я при п о м о щ и ф у н к ц и й о б р а т н о г о в ы з о в а , в я з ы к е С# п р и м е н я е т с я более развитая
технология
публикации событий
(events
publishing)
и
подписки
на
события
(events s u b s c r i b i n g ) . И д е я п у б л и к а ц и и с о б ы т и й и п о д п и с к и на них д о с т а т о ч н о п о н я т н а . П р о в е д е м ана¬ логию с интернетовскими сайтами новостей. В д а н н о м случае п у б л и к а ц и я н о в о с т и на т а к о м сайте будет играть роль с о б ы т и я . О н о п о х о ж е на с о б ы т и е , в о з н и к а ю щ е е в п р о г р а м м е , в том с м ы с л е , что п о я в л я е т с я не¬ п р е д с к а з у е м ы м о б р а з о м и с о д е р ж и м о е его т а к ж е н е п р е д с к а з у е м о . М н о г и е сайты новостей с о д е р ж а т раздел п о д п и с к и , где к а ж д ы й ж е л а ю щ и й м о ж е т о с т а в и т ь свой адрес э л е к т р о н н о й п о ч т ы д л я о т п р а в к и т е к с т о в н о в о с т е й . П о л ь з о в а т е л и И н т е р н е т а могут сами п о д п и с ы в а т ь с я на и н т е р е с у ю щ и е их н о в о с т и на тех или и н ы х К о г д а на сайте п о я в л я е т с я н о в о с т ь , она р а с с ы л а е т с я всем п о д п и с ч и к а м по элек¬ тронной почте. Получение и чтение новости в данном контексте можно рассматривать как о б р а б о т к у с о б ы т и я . Создавая программу на языке С#, вы можете разрабатывать объекты классов, ини ц и и р у ю щ и е п о я в л е н и е с о б ы т и й , а т а к ж е о б ъ е к т ы к л а с с о в , о б р а б а т ы в а ю щ и х эти собы¬ тия. П е р в ы е из них д о л ж н ы о п у б л и к о в а т ь с о б ы т и я , а в т о р ы е — п о д п и с а т ь с я на них. Для р е а л и з а ц и и о б р а б о т ч и к о в с о б ы т и й в я з ы к е С# и с п о л ь з у ю т с я у ж е з н а к о м ы е вам д е л е г а т ы . К л а с с , п у б л и к у ю щ и й с о б ы т и я (т. е. к л а с с , в к о т о р о м эти с о б ы т и я возника¬ ю т ) , д о л ж е н о п р е д е л и т ь делегат. Э т о т д е л е г а т р е а л и з у е т с я к л а с с о м , п о д п и с ы в а ю щ и м ¬ ся на с о б ы т и е , т. е. о т в е т с т в е н н ы м за о б р а б о т к у с о б ы т и я . К а к т о л ь к о с о б ы т и е возника¬ ет, п р о и с х о д и т вызов м е т о д а о б р а б а т ы в а ю щ е г о класса, з а д а н н о г о д е л е г а т о м . Э т о т ме¬ тод
обычно
называется
обработчиком
события
(event h a n d l e r ) .
Приведем пример программы. П у с т ь м ы создаем м н о г о п о т о ч н у ю п р о г р а м м у , о т о б р а ж а ю щ у ю н а к о н с о л и симво¬ лы
п р и ч е м это о т о б р а ж е н и е в ы п о л н я е т с я в рамках о т д е л ь н о г о потока. И с х о д н ы й
текст с о о т в е т с т в у ю щ е г о класса п р е д с т а в л е н н и ж е : Глава
и
359
public
class
private private
StringWriter
Thread int count;
public count
public
0;
void
count++
public
void
thr
go
new
Как видите, пока здесь для нас нет ничего нового. М е т о д W r i t e T h r e a d содержит цикл отображения символа а метод запускает процесс отображения в отдельном по¬ токе. В переменной c o u n t ведется подсчет общего количества выведенных символов. Д а в а й т е д о п о л н и м этот класс к о д о м , с о з д а ю щ и м и п у б л и к у ю щ и м с о б ы т и е , связан¬ ное с о т о б р а ж е н и е м о ч е р е д н о г о с и м в о л а на к о н с о л и . С этой ц е л ь ю мы д о б а в и м в класс S t r i n g W r i t e r д в а п о л я , п е р в о е и з к о т о р ы х , с и м е н е м E v e n t H a n d l e r , я в л я е т с я де¬ л е г а т о м , а в т о р о е , с и м е н е м O n L o o p D o n e , и г р а е т роль так н а з ы в а е м о й п е р е м е н н о й события: public
delegate
public
event
void
EventHandler (object writer,
EventHandler
OnLoopDone;
Д е л е г а т , п р е д н а з н а ч е н н ы й д л я о б р а б о т к и с о б ы т и й , д о л ж е н всегда о б ъ я в л я т ь с я с д в у м я п а р а м е т р а м и . П е р в ы й из них задает о б ъ е к т , с о з д а ю щ и й с о б ы т и е , а в т о р о й и н ф о р м а ц и ю о событии (например, время возникновения события). П е р е м е н н а я с о б ы т и я O n L o o p D o n e будет х р а н и т ь ссылку н а м е т о д о б р а б о т ч и к а события. Остановимся подробнее на параметрах делегата. Для т о г о ч т о б ы п р о г р а м м а м о г л а п е р е д а в а т ь и н ф о р м а ц и ю о с о б ы т и и о б р а б о т ч и к у э т о г о с о б ы т и я , н е о б х о д и м о о б ъ я в и т ь п р о и з в о д н ы й к л а с с , создав его на базе класса EventArgs:
360
В. Фролов, Г. В Фролов Язык
Самоучитель
public
class
public
count) count;
dt
public public
readonly readonly
int count; System. DateTime
dt;
В этом классе следует определить п о л я , х р а н я щ и е в с ю н е о б х о д и м у ю и н ф о р м а ц и ю о с о б ы т и и , а т а к ж е к о н с т р у к т о р , и н и ц и а л и з и р у ю щ и й эти поля. В д а н н о м случае мы о п р е д е л и л и поле c o u n t для х р а н е н и я п о р я д к о в о г о н о м е р а о ч е р е д н о г о о т о б р а ж а е м о г о на к о н с о л и с и м в о л а , а т а к ж е поле dt класса в котором будет х р а н и т ь с я д а т а и время в о з н и к н о в е н и я с о б ы т и я ( с в о й с т в о х р а н и т и н ф о р м а ц и ю о т е к у щ е й дате и в р е м е н и ) . В своей п р о г р а м м е вы будете о п р е д е л я т ь д р у г и е п о л я , в с о о т в е т с т в и и с л о г и к о й работы программы. З а м е т и м , что оба поля д о с т у п н ы т о л ь к о д л я ч т е н и я , так как о б ъ я в л е н ы при п о м о щ и к л ю ч е в о г о слова r e a d o n l y . И х с о д е р ж и м о е и з м е н я е т с я т о л ь к о к о н с т р у к т о р о м в м о с о з д а н и я объекта.
Создание события Р а с с м о т р и м теперь п р о ц е д у р у с о з д а н и я с о б ы т и я . М ы н е м н о г о м о д и ф и ц и р у е м исход¬ ный код м е т о д а р а б о т а ю щ е г о в р а м к а х о т д е л ь н о г о потока: public
void
new null)
О б р а т и т е в н и м а н и е , что на к а ж д о й и т е р а ц и и ц и к л а мы с о з д а е м н о в ы й о б ъ е к т клас¬ са I n f o E v e n t A r g s , хранящий информацию о событии: new П р и этом т е к у щ и й н о м е р о т о б р а ж а е м о г о с и м в о л а п е р е д а е т с я к о н с т р у к т о р у класса а время возникновения с о б ы т и я к о н с т р у к т о р с о х р а н я е т самостоя¬ тельно. Глава
Делегаты и события
361
П о с л е того как о б ъ е к т , с о д е р ж а щ и й и н ф о р м а ц и ю о с о б ы т и и , с ф о р м и р о в а н , наша п р о г р а м м а в ы з ы в а е т о б р а б о т ч и к с о б ы т и я , о б р а щ а я с ь для этого к п е р е м е н н о й с о б ы т и я OnLoopDone: if
null)
В качестве п е р в о г о п а р а м е т р а мы п е р е д а е м ссылку на о б ъ е к т , с о з д а ю щ и й с о б ы т и е (указав к л ю ч е в о е с л о в о t h i s , о з н а ч а ю щ е е в этом к о н т е к с т е ссылку на д а н н ы й объ¬ ект), а в к а ч е с т в е
ссылку
на
объект c o u n t l n f o r m a t i o n ,
содержащий
информацию о событии. О б р а т и т е в н и м а н и е , что перед в ы з о в о м о б р а б о т ч и к а с о б ы т и я мы п р о в е р я е м пере¬ менную события на равенство значению n u l l .
Это д е л а е т с я для т о г о , ч т о б ы прове¬
р и т ь , и м е ю т с я л и п о д п и с ч и к и н а д а н н о е с о б ы т и е . Если п о д п и с ч и к о в нет, и н и ц и и р о в а ние с о б ы т и я п р и в е д е т к
и с к л ю ч е н и я во время р а б о т ы п р о г р а м м ы .
Н и ж е м ы п р и в е л и новый в а р и а н т класса S t r i n g W r i t e r , п у б л и к у ю щ и й с о б ы т и е и вызывающий обработчик этого события: public
class
StringWriter
public
delegate
public
event
private private
void
EventHandler
EventHandler
writer,
OnLoopDone;
Thread t h r ; int count;
public count
public
0;
void
Console WriteLine
new if
(OnLoopDone
null)
Г. R Фролов. Язык
Самоучитель
public
void
go()
new
Подписка на события И т а к , т е п е р ь у нас есть о б ъ е к т , п у б л и к у ю щ и й и с о з д а ю щ и й с о б ы т и я . Т е п е р ь нам нуж¬ ны о б ъ е к т ы , п о д п и с ы в а ю щ и е с я на с о б ы т и я и в ы п о л н я ю щ и е их о б р а б о т к у . З а м е т и м , что к а ж д о е с о б ы т и е могут о б р а б а т ы в а т ь п р о и з в о л ь н о е к о л и ч е с т в о п о д п и с ч и к о в . Здесь я з ы к С# В
накладывает никаких ограничений.
нашем
примере
мы
создадим
два
подписчика —
классы
DisplayCount
и public
class
public
DisplayCount
void
Subscribe (StringWriter
writer)
new
public
void
writer,
info)
dt at:
public
class
public
DisplayCount2
void
Subscribe (StringWriter
writer)
new
public
void
writer,
info)
count:
В
каждом
из
этих
и CountHasChanged.
классов Первый
мы из
объявили них
два
играет роль
метода
с
именами
подписчика,
Subscribe
а второй — обра¬
батывает событие. Глава
Делегаты и
363
в о б о и х классах в ы г л я д я т о д и н а к о в о : public
void
Subscribe (StringWriter
writer)
new С помощью операции чиков.
В
качестве
они д о б а в л я ю т о б р а б о т ч и к с о б ы т и я в ц е п о ч к у о б р а б о т
параметра
переменной
C o u n t H a s C h a n g e d , непосредственно
события
передается
ссылка
на
метод
обрабатывающий события.
П о с л е в ы п о л н е н и я этой о п е р а ц и и класс будет п о д п и с а н на с о б ы т и е и с м о ж е т его обрабатывать. Что ж е касается м е т о д о в C o u n t H a s C h a n g e d , в ы п о л н я ю щ и х о б р а б о т к у с о б ы т и й , то первый из них в ы в о д и т на к о н с о л ь в р е м я в о з н и к н о в е н и я с о б ы т и я , а второй — те¬ к у щ и й н о м е р о т о б р а ж а е м о г о символа. Как мы у ж е г о в о р и л и , эта и н ф о р м а ц и я переда¬ ется о б р а б о т ч и к у при в о з н и к н о в е н и и с о б ы т и я .
Программа с обработкой событий Теперь м ы р а с с м о т р и м п о л н ы й и с х о д н ы й т е к с т п р о г р а м м ы , о б р а б а т ы в а ю щ е й с о б ы т и я (листинг
В ней о п р е д е л е н ы классы и д р у г и е о б ъ е к т ы , р а с с м о т р е н н ы е нами ранее
в этом р а з д е л е . Листинг using using
11.4.
Файл
System;
namespace public
EventsDemo class
public
delegate
public
event
void
Thread thr; int count;
public
StringWriter
public
EventHandler
EventHandler
private private
count
364
StringWriter writer,
OnLoopDone;
0;
void
WriteThread
В Фролов, Г. В. Фролов Язык С# Самоучитель
new if
public
(OnLoopDone
void
thr
public
new T h r e a d ( n e w
class
public
EventArgs
InfoEventArgs
int
count)
count; dt
public public public
readonly readonly
class
public
int
count; dt;
DisplayCount
void
Subscribe (StringWriter writer)
new
public
void
writer,
InfoEventArgs
info)
dt at: dt.Hour,
public
class
public
void
DisplayCount2 Subscribe (StringWriter
writer)
new Глава
Делегаты и события
365
public
void
writer, count:
class
EventsDemoApp
static
void
args)
StringWriter
sw
new
DisplayCount
dc
new
DisplayCount2
Получив
управление,
dc2
new D i s p l a y C o u n t 2
метод
Main
нашей
программы
создаст
объект
класса
StringWriter: StringWriter
sw
new
StringWriter
Д а л е е о н с о з д а е т два о б ъ е к т а , п р е д н а з н а ч е н н ы е для о б р а б о т к и с о б ы т и й : DisplayCount
DisplayCount2
dc
dc2
new
new
из них о т о б р а ж а е т на к о н с о л и время возникновения с о б ы т и я , а второй — текущий номер отображаемого символа. После
создания
обработчиков
мы
вызываем
для
каждого
из
них
метод
Sub¬
s c r i b e , осуществляющий подписку н а события. Д а л е е нам о с т а е т с я т о л ь к о з а п у с т и т ь в ы в о д с о б ы т и я в о т д е л ь н о м п о т о к е , вызвав для этого м е т о д
Вот что вы у в и д и т е на э к р а н е , з а п у с т и в нашу п р о г р а м м у на в ы п о л н е н и е :
366
А В Фролов, Г. В Фролов. Язык
Самоучитель
Event at: Current
12:42:3 1
Event at: 12:42:4 Current count: 2 Event at: 12:42:5 Current count: 3 Event at: 12:42:6 Current count: 4 Event at: 12:42:7 Current count: 5 Event at: 12:42:8 Current count: 6 После отображения символа в дело включаются наши обработчики событий. П е р в ы й из них п о к а з ы в а е т в р е м я о т о б р а ж е н и я , а второй — т е к у щ и й н о м е р с и м в о л а . З а м е т и м , что д е л е г а т ы и о б р а б о т ч и к и с о б ы т и й н у ж н ы д а л е к о не в к а ж д о й про¬ г р а м м е . И с п о л ь з у й т е их (как и д р у г и е с р е д с т в а я з ы к а С#) т о л ь к о при н е о б х о д и м о с т и . В м е с т е с тем м о щ н ы й , а г л а в н о е , у д о б н ы й в и с п о л ь з о в а н и и м е х а н и з м о б р а б о т к и собы¬ тий о б я з а т е л ь н о вам п р и г о д и т с я в тех с л у ч а я х , когда н у ж н о сочетать д л и т е л ь н у ю об¬ р а б о т к у д а н н ы х или о ж и д а н и е ч е г о - л и б о с ф у н к ц и о н и р о в а н и е м п о л ь з о в а т е л ь с к о г о и н т е р ф е й с а п р о г р а м м ы . Эти с р е д с т в а будут вам н у ж н ы при р а б о т е в базами д а н н ы х и передаче д а н н ы х по л о к а л ь н ы м сетям и через И н т е р н е т .
Глава
Делегаты и события
367
Глава 12. Работа с текстовыми строками В то время как п е р в ы е к о м п ь ю т е р ы с о з д а в а л и с ь г л а в н ы м о б р а з о м для в ы п о л н е н и я ма¬ т е м а т и ч е с к и х в ы ч и с л е н и й , сейчас они а к т и в н о п р и м е н я ю т с я для р а б о т ы с т е к с т а м и , г р а ф и к о й , з в у к о м и в и д е о . П р и этом задача о б р а б о т к и т е к с т о в ы х с и м в о л о в и строк яв¬ л я е т с я одной из в а ж н е й ш и х . С е г о д н я вы не н а й д е т е ни одной п р о г р а м м ы , в к о т о р о й так или иначе не и с п о л ь з о в а л и с ь бы т е к с т о в ы е с т р о к и . П р а к т и ч е с к и в л ю б о м я з ы к е п р о г р а м м и р о в а н и я п р е д у с м о т р е н богатый набор ф у н к ц и й , п р е д н а з н а ч е н н ы х д л я р а б о т ы с с и м в о л а м и и т е к с т о в ы м и с т р о к а м и . Эти ф у н к ц и и п о з в о л я ю т о б ъ е д и н я т ь с т р о к и , в ы р е з а т ь и з строк ф р а г м е н т ы , менять строч¬ ные буквы на п р о п и с н ы е и о б р а т н о , искать в с т р о к а х д р у г и е строки и п р о и з в о л ь н ы е п о с л е д о в а т е л ь н о с т и с и м в о л о в и т. д. В я з ы к а х С и С + + с т а н д а р т н ы е ф у н к ц и и , о б р а б а т ы в а ю щ и е с и м в о л ы и с т р о к и , яв¬ л я ю т с я ч а с т ь ю б и б л и о т е к и т р а н с л я т о р а . Ф а к т и ч е с к и они уже стали ч а с т ь ю этих язы¬ ков и п о т о м у о п и с ы в а ю т с я во всех у ч е б н и к а х , п о с в я щ е н н ы х С и С + + . Что к а с а е т с я я з ы к а п р о г р а м м и р о в а н и я С # , т о , как м ы у ж е г о в о р и л и , для п р е д с т а в л е н и я т е к с т о в ы х с т р о к в б и б л и о т е к е классов Microsoft F r a m e w o r k имеется класс S y s t e m . S t r i n g . В м е с т е с н а б о р о м о п е р а ц и й , м е т о д о в и и н д е к с а т о р о в этот класс п р е д с т а в л я е т собой м о щ н о е с р е д с т в о о б р а б о т к и строк. П о м и м о этого класса, существуют и другие, предназначенные для построения строк. Э т о , например, класс S t r i n g B u i l d e r , а также класс R e g e x . Последний из них предна¬ значен для использования так называемых регулярных выражений (regular expressions), представляющих собой м о щ н е й ш е е средство обработки текста. Рассказ об этих возможно¬ стях в ы х о д и т за рамки нашей книги. При необходимости вы можете найти описание регу¬ л я р н ы х выражений в а также в учебниках по языку Perl. П е р е д тем как мы п р и с т у п и м к и з у ч е н и ю с п о с о б о в п о с т р о е н и я т е к с т о в ы х с т р о к н а п о м н и м , чем строки С# о т л и ч а ю т с я от строк, с к о т о р ы м и и м е ю т дело про¬ г р а м м и с т ы С, С + + и P a s c a l . П р е ж д е в с е г о , п р о г р а м м ы , с о с т а в л е н н ы е на я з ы к а х С, С + + и P a s c a l , и м е ю т непо¬ с р е д с т в е н н ы й д о с т у п к п р е д с т а в л е н и ю т е к с т о в ы х строк, х р а н я щ и х с я в о п е р а т и в н о й п а м я т и к о м п ь ю т е р а . Такие п р о г р а м м ы м о г у т , н а п р и м е р , п е р е з а п и с ы в а т ь или и з м е н я т ь с о д е р ж и м о е с т р о к по месту их р а с п о л о ж е н и я . Что же касается п р о г р а м м С # , то все о п е р а ц и и с т е к с т о в ы м и с т р о к а м и выполняют¬ ся т о л ь к о при п о м о щ и м е т о д о в , с в о й с т в , и н т е р ф е й с о в и и н д е к с а т о р о в , предусмотрен¬ ных в с о о т в е т с т в у ю щ и х классах б и б л и о т е к и Microsoft Framework. Д а л е е , п р о г р а м м ы С, С + + и Pascal м о г у т р а б о т а т ь с р а з л и ч н ы м и п р е д с т а в л е н и я м и т е к с т о в ы х с т р о к , т а к и м и , как, н а п р и м е р A S C I I и U N I C O D E . В з а в и с и м о с т и от пред¬ с т а в л е н и я ( и л и , как е щ е г о в о р я т , к о д и р о в к и ) для х р а н е н и я к а ж д о г о с и м в о л а строки может использоваться или н е с к о л ь к о байт п а м я т и . В я з ы к е С# т е к с т о в ы е строки п р е д с т а в л е н ы только в к о д и р о в к е U N I C O D E , п р е д п о л а г а ю щ е й п р е д с т а в л е н и е о д н о г о т е к с т о в о г о с и м в о л а 2 байтами п а м я т и .
368
Применение класса М е т о д ы и свойства класса
S t r i n g п о з в о л я ю т в ы п о л н я т ь над с т р о к а м и
с а м ы е н е о б х о д и м ы е о п е р а ц и и , т а к и е , н а п р и м е р , как с р а в н е н и е , п о и с к п о д с т р о к и , объ¬ е д и н е н и е строк, к о п и р о в а н и е с и м в о л о в из одной строки в д р у г у ю , и з в л е ч е н и е под¬ строки и т. д. Н и ж е мы р а с с м о т р и м п р и е м ы
в ы п о л н е н и я этих о п е р а ц и й и п р и в е д е м
примеры
программ.
Создание строк В гл.
1 мы р а с с к а з ы в а л и вам о т е к с т о в ы х строках и л и т е р а л а х , а также п р и в о д и л и не
к о т о р ы е п р и м е р ы их и с п о л ь з о в а н и я в п р о г р а м м а х С#. До сих пор в н а ш и х п р о г р а м м а х в с т р е ч а л и с ь г л а в н ы м о б р а з о м с т р о к и , с о з д а н н ы е при п о м о щ и л и т е р а л о в или п о л у ч е н н ы е при р а б о т е тех или и н ы х м е т о д о в ( н а п р и м е р , т е к с т о в ы е строки с о о б щ е н и й о б о ш и б к а х ) . В классе S y s t e m . S t r i n g и м е е т с я н а б о р п е р е г р у ж е н н ы х к о н с т р у к т о р о в , п о з в о л я ю щ и х с о з д а в а т ь т е к с т о в ы е строки и д р у г и м и способами.
П р е о б р а з о в а н и е м а с с и в а с и м в о л о в в строку Если у вас есть м а с с и в с и м в о л о в U N I C O D E типа c h a r , то его м о ж н о п р е о б р а з о в а т ь в с т р о к у s t r i n g , в о с п о л ь з о в а в ш и с ь для этого к о н с т р у к т о р о м , п р е д у с м о т р е н н ы м спе ц и а л ь н о д л я этой цели в классе S y s t e m . Р а с с м о т р и м п р и м е р п р о г р а м м ы , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е 12.1. Листинг
12.1.
Файл
chl2\CreateString\CreateStringApp.cs
using System; namespace CreateString class
CreateStringApp
static
void
chart] string s
args)
new
String(ch);
Здесь мы о б ъ я в и л и массив с и м в о л о в ch и п р о и н и ц и а л и з и р о в а л и его при п о м о щ и символьных литералов: ch Глава 12. Работа с
строками
369
Ч т о б ы п р е о б р а з о в а т ь этот м а с с и в с и м в о л о в в т е к с т о в у ю с т р о к у , мы воспользова¬ лись к о н с т р у к т о р о м класса S y s t e m . S t r i n g : string
s
new
О т о б р а з и в п о л у ч е н н у ю с т р о к у м е т о д о м W r i t e L i n e , м ы п о л у ч и м о ж и д а е м ы й ре¬ зультат, а и м е н н о у в и д и м н а к о н с о л и строку « Н е 1 1 о
С о з д а н и е с т р о к и н а базе ф р а г м е н т а м а с с и в а В п р е д ы д у щ е й п р о г р а м м е мы п р е о б р а з о в а л и в строку ц е л и к о м весь м а с с и в с и м в о л о в . С у щ е с т в у е т с п о с о б с о з д а н и я т е к с т о в о й строки на базе л ю б о г о ф р а г м е н т а такого мас¬ сива. Рассмотрим следующую программу (листинг Листинг
12.2.
12.2).
Файл
using System; namespace SubArray class
SubArrayApp
static
void
char
string
args) H
s
new
String(ch,
7,
6);
Здесь м ы в о с п о л ь з о в а л и с ь к о н с т р у к т о р о м класса параметра: string
s
new
String(ch,
7,
S t r i n g , и м е ю щ и м три
6)
Первый параметр задает ссылку на массив символов U N I C O D E , из которых нужно сделать с т р о к у . В т о р о й и т р е т и й п а р а м е т р ы о п р е д е л я ю т с о о т в е т с т в е н н о индекс перво¬ го э л е м е н т а м а с с и в а и к о л и ч е с т в о с и м в о л о в , из к о т о р ы х будет с о з д а н а строка. В р е з у л ь т а т е р а б о т ы п р и в е д е н н о й в ы ш е п р о г р а м м ы на к о н с о л ь в ы в о д и т с я т о л ь к о п о с л е д н и е 6 с и м в о л о в массива, о б р а з у ю щ и е строку
Заполнение строки символом И н о г д а п р о г р а м м и с т у м о ж е т п о т р е б о в а т ь с я создать с т р о к у , с о д е р ж а щ у ю ко¬ л и ч е с т в о о д и н а к о в ы х с и м в о л о в . Т и п и ч н ы й п р и м е р — с о з д а н и е р а з д е л и т е л е й для заго¬ ловков в листингах. В исходный текст которой представлен в листинге ются два с п о с о б а р е ш е н и я этой п р о б л е м ы .
370
Г.
12.3, демонстриру¬
Фролов. Язык С# Самоучитель
Листинг
12.3.
Файл
using System; namespace Fill class
FillApp
static
void
args
string string
header title=
string
headerl
"Оформление
new
40);
П е р в ы й с п о с о б п р е д п о л а г а е т « л о б о в о е » р е ш е н и е . М ы и с п о л ь з у е м для инициализа¬ ции с т р о к и д л и н н ы й т е к с т о в ы й л и т е р а л : string
header
Э т о т с п о с о б имеет, п о к р а й н е й м е р е , д в а н е д о с т а т к а . В о - п е р в ы х , такие л и т е р а л ы м о г у т з а г р о м о ж д а т ь л и с т и н г п р о г р а м м ы . В о - в т о р ы х , создавая литерал из повторяющихся символов, приходится подсчитывать количество этих с и м в о л о в в р у ч н у ю , что очень н е у д о б н о . Другой способ основан на использовании одного из конструкторов, предусмотрен¬ ных в классе S y s t e m . S t r i n g с п е ц и а л ь н о для и н и ц и а л и з а ц и и п о д о б н ы х с т р о к : string
headerl
new
В к а ч е с т в е п е р в о г о п а р а м е т р а этому к о н с т р у к т о р у н у ж н о п е р е д а т ь п о в т о р я ю щ и й с я символ, а в качестве второго
количество повторов символа.
Н е б е з о п а с н ы е к о н с т р у к т о р ы в классе Как м ы у ж е г о в о р и л и , п р о г р а м м ы С # р а б о т а ю т под у п р а в л е н и е м с и с т е м ы и с п о л н е н и я F r a m e w o r k , и д а ж е в случае н а л и ч и я в них о ш и б о к не п р е д с т а в л я ю т о п а с н о с т и для д р у г и х р а б о т а ю щ и х п р о г р а м м или о п е р а ц и о н н о й с и с т е м ы . Такая б е з о п а с н а я рабо¬ та н а к л а д ы в а е т н е к о т о р ы е о г р а н и ч е н и я на п р о г р а м м ы , что не всегда п р и е м л е м о . При о б р а щ е н и и к п р о г р а м м н о м у и н т е р ф е й с у A P I о п е р а ц и о н н о й с и с т е м ы часто п р и х о д и т с я иметь д е л о с у к а з а т е л я м и , з а п р е щ е н н ы м и к п р и м е н е н и ю в о б ы ч н ы х про¬ г р а м м а х С#. Тем не м е н е е , вы м о ж е т е с о з д а т ь так н а з ы в а е м у ю н е б е з о п а с н у ю про¬ г р а м м у (или н е к о н т р о л и р у е м у ю п р о г р а м м у ) , в к о т о р о й и с п о л ь з о в а н и е у к а з а т е л е й до¬ пускается. Глава
Работа с
371
такой п р о г р а м м ы п р и в е д е н в л и с т и н г е Листинг
12.4.
12.4.
Файл
using System; namespace UnsafeString class unsafe
static
ch string
void
H
args)
e
s; ptr
s
new
Обратите внимание на определение метода M a i n : unsafe s t a t i c void args)
Как в и д и т е , мы д о б а в и л и к л ю ч е в о е слово
Это к л ю ч е в о е слово с о о б щ а е т
к о м п и л я т о р у С# о т о м , что в н у т р и д а н н о г о м е т о д а б у д у т и с п о л ь з о в а н ы н е б е з о п а с н ы е к о н с т р у к ц и и С# ( т а к и е , н а п р и м е р , как у к а з а т е л и ) . В н у т р и м е т о д а M a i n имеется массив с и м в о л о в U N I C O D E с и м е н е м c h , п р о и н и циализированный
статически.
Нашей
задачей
будет п р е о б р а з о в а н и е
этого
массива
в т е к с т о в у ю строку с и с п о л ь з о в а н и е м у к а з а т е л е й . Все самое и н т е р е с н о е н а х о д и т с я в с л е д у ю щ е м ф р а г м е н т е кода: f i x e d ( c h a r * ptr s
new
Этот ф р а г м е н т п р е д с т а в л я е т собой блок fixed
расположен
оператор,
В к р у г л ы х с к о б к а х п о с л е ключе¬
вого
слова
выполняющий
ptr.
И н и ц и а л и з а ц и я в ы п о л н я е т с я п о с р е д с т в о м записи в п е р е м е н н у ю p t r адреса са
инициализацию
указателя
мого п е р в о г о э л е м е н т а м а с с и в а ch с и н д е к с о м 0.
372
Г. В. Фролов. Язык С# Самоучитель
П р о г р а м м и с т а м , с о с т а в л я в ш и м ранее п р о г р а м м ы на я з ы к а х С и С + + , и з в е с т н о , что для п о л у ч е н и я адреса р а с п о л о ж е н и я и с п о л ь з у е т с я символ Таким
образом,
в оперативной памяти компьютера
Этот же символ п р и м е н я е т с я и в н е б е з о п а с н о м коде С#. перед
выполнением
тела
блока
fixed
программа
записывает
в у к а з а т е л ь p t r адрес начала массива c h . Д а л е е этот у к а з а т е л ь п е р е д а е т с я с о о т в е т с т в у ю щ е м у к о н с т р у к т о р у класса
к о т о р ы й и ф о р м и р у е т из него тек¬
стовую строку. В д о к у м е н т а ц и и на С# вы найдете еще н е с к о л ь к о н е б е з о п а с н ы х к о н с т р у к т о р о в , по¬ з в о л я ю щ и х создавать строки из м а с с и в о в 8-разрядных ц е л ы х ч и с е л , а т а к ж е с и м в о л о в UNICODE. З а м е т и м т а к ж е , что при т р а н с л я ц и и н е б е з о п а с н о й п р о г р а м м ы , и с х о д н ы й т е к с т ко¬ торой п р и в е д е н в л и с т и н г е
12.4, н е о б х о д и м о и с п о л ь з о в а т ь ключ
(для п а к е т н о й
т р а н с л я ц и и в к о м а н д н о й строке) или п р и р а в н я т ь с в о й с т в а проекта A l l o w u n s a f e c o d e b l o c k s з н а ч е н и ю T r u e в д и а л о г о в о м окне с в о й с т в проекта (рис.
Включение
режима
использования
12.1).
небезопасного
кода
Чтобы о т о б р а з и т ь это о к н о на э к р а н е , щ е л к н и т е н а з в а н и е п р о е к т а в окне S o l u t i o n E x p l o r e r , р а с п о л о ж е н н о м по у м о л ч а н и ю в п р а в о м в е р х н е м углу г л а в н о г о окна систе¬ мы р а з р а б о т к и п р о г р а м м Microsoft Visual Studio
Копирование и клонирование строк Если вам н у ж н о создать к о п и ю т е к с т о в о й с т р о к и , то это м о ж н о сделать при п о м о щ и о б ы к н о в е н н о й о п е р а ц и и п р и с в а и в а н и я . В этом случае в п а м я т и будет н а х о д и т ь с я два о д и н а к о в ы х объекта. Глава
Работа с
строками
373
Н а п р и м е р , н и ж е мы о б ъ я в и л и т е к с т о в ы е стр о ки
и
после чего с к о п и р о в а л и
п е р в у ю с т р о к у во в т о р у ю : string
src
"Hello,
Теперь у строки.
нас
есть два р а з л и ч н ы х объекта, с о д е р ж а щ и х о д и н а к о в ы е текстовые
Для к о п и р о в а н и я строк м о ж н о также в о с п о л ь з о в а т ь с я с т а т и ч е с к и м м е т о д о м о п р е д е л е н н ы м в классе S t r i n g : string
s2
Р е з у л ь т а т будет такой ж е , как и при и с п о л ь з о в а н и и о п е р а т о р а п р и с в а и в а н и я ,
бу
дет создан н о в ы й о б ъ е к т , с о д е р ж а щ и й и с х о д н у ю строку. В п е р е м е н н у ю s2 будет за п и с а н а с с ы л к а на этот о б ъ е к т . О п е р а ц и я к л о н и р о в а н и я строк очень п о х о ж а н а о п е р а ц и ю к о п и р о в а н и я , однако она не п р и в о д и т к с о з д а н и ю о б ъ е к т а - б л и з н е ц а . В м е с т о этого с о з д а е т с я еще одна ссылка на у ж е с у щ е с т в у ю щ и й объект: string
s3
П о с л е в ы п о л н е н и я этого кода в п е р е м е н н у ю s3 src. Так как м е т о д
б у д е т з а п и с а н а с с ы л к а на строку
возвращает данные базового типа
нам здесь при
шлось выполнить явное приведение типов. О п и с а н н ы е в ы ш е о п е р а ц и и к о п и р о в а н и я и к л о н и р о в а н и я д е м о н с т р и р у ю т с я в про г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и в е д е н в л и с т и н г е 12.5. Листинг
12.5.
Файл
using System; namespace StringCopy class
StringCopyApp
static
void
string string string string
src
args) "Hello,
s2 s3 строка: 1: 2:
374
Ав.
src) s2);
Г. В. Фролов.
Конкатенация строк И н о г д а перед п р о г р а м м и с т о м встает задача строк. Эта о п е р а ц и я в ы п о л н я е т с я с л е д у ю щ и м а затем з а п и с а т ь в нее о б ъ е д и н я е м ы е тода Вот как о п е р а т о р о б ъ е д и н я е т строки string string string
конкатенации (объединения) текстовых образом: вначале нужно объявить новую строки при п о м о щ и о п е р а т о р а или ме и
s2 s2;
В результате будет с о д е р ж а т ь строку З а м е т и м , что т а к и м о б р а з о м м о ж н о о б ъ е д и н я т ь п р о и з в о л ь н о е к о л и ч е с т в о тексто¬ вых строк. Другой способ объединения текстовых строк string
res2
применение метода S t r i n g
s2);
О б ъ е д и н я е м ы е строки п е р е д а ю т с я э т о м у м е т о д у в к а ч е с т в е п а р а м е т р о в . М е т о д в о з в р а щ а е т с с ы л к у на н о в у ю строку, п о л у ч е н н у ю в р е з у л ь т а т е объ¬ е д и н е н и я и с х о д н ы х строк. Существует несколько перегруженных методов объединять до четырех текстовых строк: string
res3
s2,
Кроме того, метод S t r i n g щиеся в массиве: string[]
string
resl,
позволяющих res2);
в с о с т о я н и и о б ъ е д и н и т ь все с т р о к и , храня¬
array
res4
После выполнения этого
ф р а г м е н т а кода в
строка « Э т о м а с с и в строк». Все о п и с а н н ы е в ы ш е с п о с о б ы о б ъ е д и н е н и я в программе ( л и с т и н г 12.6). Листинг
12.6.
переменной
res4 будет н а х о д и т ь с я
текстовых
строк демонстрируются
Файл
using System; namespace StringConcat class
StringConcatApp
static
void
string string Глава
args) "Hello,
s2
Работа с текстовыми строками
375
string string
res2
s2);
string
res3
s2,
resl,
array
string
res4 1:
4:
Извлечение подстроки Ч т о б ы с о з д а т ь н о в у ю строку на базе ф р а г м е н т а с у щ е с т в у ю щ е й с т р о к и , м о ж н о вос¬ п о л ь з о в а т ь с я м е т о д о м S u b s t r i n g ( л и с т и н г 12.7). Листинг
12.7.
Файл
System; namespace StringSubstring class
StringSubstringApp
static
void
string string
src res
args) "Hello,
Первый параметр метода
6)
S u b s t r i n g задает н а ч а л ь н ы й и н д е к с и з в л е к а е м о й под
с т р о к и , а в т о р о й , н е о б я з а т е л ь н ы й , р а з м е р п о д с т р о к и . Н и ж е в с т р о к у r e s будет сан ф р а г м е н т с т р о к и s r c д л и н о й 6 с и м в о л о в и н а ч и н а ю щ и й с я с 7-й п о з и ц и и : string
res
Если в т о р о й п а р а м е т р не з а д а н , к о п и р у ю т с я все
376
метода
S u b s t r i n g ( о п р е д е л я ю щ и й размер п о д с т р о к и ) н а ч и н а я с и с х о д н о й п о з и ц и и и до конца строки. А. В Фролов, Г. В.
Самоучитель
Вставка подстроки Одна из о с о б е н н о с т е й с т р о к С# з а к л ю ч а е т с я в т о м , что прямая м о д и ф и к а ц и я сущест¬ в у ю щ е й строки н е в о з м о ж н а . Т а к и м о б р а з о м , если п р о г р а м м а создала т е к с т о в у ю стро¬ ку, т о , чтобы и з м е н и т ь ее, п р и д е т с я с о з д а в а т ь к о п и ю и уже п о т о м п е р е п и с ы в а т ь туда с и м в о л ы и с х о д н о й строки ( в н е и з м е н н о м или м о д и ф и ц и р о в а н н о м в и д е ) . С п е ц и а л ь н о для в ы п о л н е н и я о п е р а ц и и вставки одной строки в з а д а н н о е м е с т о дру¬ гой
строки
был
предусмотрен
метод
Insert.
Его
использование демонстрируется
в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и в е д е н в л и с т и н г е 12.8. Листинг
12.8.
Файл
using System; namespace InsertString class
InsertStringApp
static
void
args)
string string
В к а ч е с т в е п е р в о г о п а р а м е т р а м е т о д у I n s e r t н у ж н о п е р е д а т ь и н д е к с п о з и ц и и ис¬ х о д н о й с т р о к и , в к о т о р у ю н у ж н о вставить д р у г у ю строку. Через в т о р о й п а р а м е т р пе¬ р е д а е т с я с с ы л к а на в с т а в л я е м у ю строку. Здесь мы в с т а в и л и з а п я т у ю и слово
между словом
и восклица¬
тельным знаком: string string
res
В р е з у л ь т а т е , как н е т р у д н о д о г а д а т ь с я , мы п о л у ч и м б е с с м е р т н у ю фразу «Не11о,
Замена символов и строк Если вам н у ж н о з а м е н и т ь в с т р о к е к а к о й - л и б о с и м в о л д р у г и м или в ы п о л н и т ь т а к у ю замену для п о с л е д о в а т е л ь н о с т и с и м в о л о в , и с п о л ь з у й т е м е т о д Replace. В качестве п е р в о г о п а р а м е т р а этому м е т о д у н у ж н о п е р е д а т ь з а м е н я е м ы й с и м в о л или з а м е н я е м у ю п о с л е д о в а т е л ь н о с т ь с и м в о л о в . В т о р о й п а р а м е т р задает н о в ы е симво¬ л ы , на к о т о р ы е н е о б х о д и м о з а м е н и т ь и с х о д н ы й с и м в о л или п о с л е д о в а т е л ь н о с т ь сим¬ волов. Глава 1 2 . Работа с
377
Пример использования этого метода приведен в листинге Листинг
12.9.
Файл
using System; namespace Replace class
ReplaceApp
static
void
string string string
args)
str res resl
"Hello, "C#
Прежде всего м ы меняем в н а слова « С #
строке
слово
World»:
string str s t r i n g res
"Hello,
В результате получится фраза « H e l l o ,
С#
Далее мы меняем в полученной строке все буквы о на цифру string
resl
В итоге получим фразу в «хакерском» стиле:
Удаление символов из строки Теперь решим д р у г у ю задачу
удалим из текстовой строки фрагмент с заданным на
чальным и н д е к с о м и длиной. Для решения этой задачи нам потребуется м е т о д R e m o v e (листинг 12.10). Листинг
12.10.
Файл
using System; n a m e s p a c e R e m ove
static
void
string string
378
str res
args) "Hello,
В. Фролов, Г. В. Фролов. Язык С#. Самоучитель
В к а ч е с т в е п е р в о г о п а р а м е т р а мы п е р е д а е м методу R e m o v e и н д е к с з а п я т о й , а в ка честве в т о р о г о — число 7: string string
str res
"Hello,
В результате мы получим из строки
строку
Удаление незначащих пробелов При о б р а б о т к е строк, в в е д е н н ы х п о л ь з о в а т е л е м , из п о л у ч е н н о й в р е з у л ь т а т е ввода строки р а з л и ч н ы х еще н а з ы в а ю т , (white spaces). Это л я ц и и , с и м в о л ы в о з в р а т а к а р е т к и , п е р е в о д а строки и
часто в о з н и к а е т задача у д а л е н и я н е з н а ч а щ и х с и м в о л о в , и л и , к а к их обычные пробелы, символы табу т. п.
М е т о д T r i m п о з в о л я е т вам у д а л и т ь н е з н а ч а щ и е п р о б е л ы , р а с п о л о ж е н н ы е как в н а ч а л е , так и в к о н ц е т е к с т о в о й с т р о к и . Ее п р и м е н е н и е д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и в е д е н в л и с т и н г е 12.11. Листинг
12.11.
Файл
System; n a m e s p a c e Trim class static
void
string string
str res
"\t
Здесь м е т о д T r i m у д а л и т с и м в о л т а б у л я ц и и и п р о б е л , р а с п о л о ж е н н ы й в н а ч а л е строки s t r , а т а к ж е п р о б е л , н а х о д я щ и й с я в к о н ц е этой с т р о к и : string string
str res
"\t str.TrimO;
При этом с и м в о л т а б у л я ц и и , р а з д е л я ю щ и й слова фразы и р а с п о л о ж е н н ы й в сере¬ дине строки, останется нетронутым. П о м и м о т о л ь к о что о п и с а н н о г о м е т о д а незначащих пробелов мож но выполнять методами Они и с п о л ь з у ю т с я а н а л о г и ч н о ме тоду T r i m . П е р в ы й и з этих м е т о д о в у д а л я е т пробелы, расположенные в конце с т р о к и , а второй в ее начале. Глава 12. Работа с текстовыми строками
379
Преобразование к верхнему и нижнему регистру Для п р е о б р а з о в а н и я всех с и м в о л о в строки к в е р х н е м у или н и ж н е м у регистру вы м о жете и с п о л ь з о в а т ь м е т о д ы T o U p p e r и
соответственно.
Работа с э т и м и м е т о д а м и д е м о н с т р и р у е т с я в п р о г р а м м е , п р е д с т а в л е н н о й в листин¬ ге 12.12. Листинг
12.12.
Файл
using System; namespace
static
void
string string string
str resl res2
args) "Однажды,
в
с т у д е н у ю зимнюю
Если запустить эту программу на в ы п о л н е н и е , то на экране консоли появятся две стро¬ ки, первая из которых состоит из букв нижнего
а
из букв верхнего ре¬
гистра: однажды,
Так
как
в В
с т у д е н у ю зимнюю
в
я з ы к е С#
текстовые
символы
хранятся
в
кодировке
U N I C O D E , п р е о б р а з о в а н и е р е г и с т р а будет в ы п о л н я т ь с я п р а в и л ь н о не т о л ь к о для ла¬ т и н с к и х с и м в о л о в , но и д л я с и м в о л о в д р у г и х а л ф а в и т о в .
Выравнивание по левому и правому краю поля П р и ф о р м а т н о м в ы в о д е текста и н о г д а т р е б у е т с я в ы р о в н я т ь т е к с т о в у ю строку п о лево¬ му или п р а в о м у к р а ю поля з а д а н н о й ш и р и н ы . Это м о ж н о сделать при п о м о щ и м е т о д о в
и P a d R i g h t соответственно.
В к а ч е с т в е п е р в о г о п а р а м е т р а этим м е т о д а м п е р е д а е т с я ш и р и н а поля (в с и м в о л а х ) , внутри к о т о р о г о н е о б х о д и м о в ы п о л н и т ь в ы р а в н и в а н и е , а в к а ч е с т в е в т о р о г о вол-заполнитель
сим
пробел). этих м е т о д о в д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст которой
п р е д с т а в л е н в л и с т и н г е 12.13.
380
В. Фролов, Г.
Фролов Язык С#. Самоучитель
Листинг
12.13.
Файл
using System; namespace Padding class
PaddingApp
static
void
string string string
str resl res2
-
"Однажды,
в
студеную
В к а ч е с т в е с и м в о л а з а п о л н и т е л я мы п р и м е н и л и н а ш а п р о г р а м м а в ы в е д е т н а к о н с о л ь после з а п у с к а : Однажды, Однажды,
в
студеную
в
зимнюю
символ подчеркивания.
В о т что
с т у д е н у ю зимнюю
зимнюю
Объединение массива строк Для
объединения строк, хранящихся в массиве, удобно использовать метод В качестве первого параметра этому методу передается символр а з д е л и т е л ь , к о т о р ы й будет д о б а в л е н п о с л е вставки к а ж д о й строки м а с с и в а . Ч е р е з в т о р о й п а р а м е т р п е р е д а е т с я с с ы л к а н а о б ъ е д и н я е м ы й м а с с и в строк. Третий и четвертый параметры необязательные. Они задают соответственно индекс первого элемента массива, с которого д о л ж н о начинаться объединение, и количество объ¬ единяемых элементов. Если эти параметры не заданы, объединяется весь массив. П р и м е р и с п о л ь з о в а н и я этого м е т о д а вы н а й д е т е в п р о г р а м м е , и с х о д н ы й т е к с т ко¬ т о р о й п р е д с т а в л е н в л и с т и н г е 12.14. 12.14.
Файл
ch
using System; namespace Join class static
void
string[]
args) array "массив",
Глава 12. Работа с
"строк"
строками
381
string
res
array,
0,
М а с с и в с т р о к о п р е д е л е н в этой п р о г р а м м е с т а т и ч е с к и : array "Это",
"массив",
"строк"
В к а ч е с т в е с и м в о л а - з а п о л н и т е л я мы и с п о л ь з у е м пробел: s t r i n g res
array,
0,
3)
Х о т я м ы о б ъ е д и н я е м все с т р о к и м а с с и в а a r r a y , для п р и м е р а м ы у к а з а л и необяза параметры
метода
определяющие
первую
ячейку
массива
и количество объединяемых ячеек.
Разбор строки Метод
Split,
определенный в классе
String,
п о з в о л я е т в ы п о л н я т ь р а з б о р строк,
с о д е р ж а щ и х к л ю ч е в ы е с л о в а , о т д е л е н н ы е д р у г о т д р у г а символами-разделителями. Р а с с м о т р и м п р о г р а м м у , п р е д н а з н а ч е н н у ю для р а з б о р а строки з а п у с к а у т и л и т с па¬ р а м е т р а м и вида Листинг
( л и с т и н г 12.15).
12.15.
Файл
using System; namespace Split class
SplitApp
static
void
string
args)
str resArray res
382
str in
S p l i t (new Char resArray)
А В
Г. В. Фролов. Язык
Самоучитель
И с х о д н а я с т р о к а , п о д л е ж а щ а я р а з б о р у , п р е д с т а в л е н а ниже: string
str
Здесь
мы
о б ъ я в и л и с т р о к у , п р е д н а з н а ч е н н у ю для з а п у с к а п р о г р а м м ы с и м е н е м
m y p r g , в к о т о р о й этой п р о г р а м м е п е р е д а ю т с я 3 п а р а м е т р а с и м е н а м и
у,
и Z.
Р а з б о р в ы п о л н я е т с я при п о м о щ и м е т о д а S p l i t : resArray В качестве п е р в о г о п а р а м е т р а э т о м у м е т о д у н у ж н о п е р е д а т ь с с ы л к у на м а с с и в сим¬ в о л о в - р а з д е л и т е л е й . В качестве т а к и х с и м в о л о в мы у к а з а л и здесь з а п я т у ю и п р о б е л . М е т о д S p l i t р а з б и р а е т строку, с о з д а в а я м а с с и в и з о т д е л ь н ы х к л ю ч е в ы х с л о в , обна¬ р у ж е н н ы х в с т р о к е . П р и н е о б х о д и м о с т и вы м о ж е т е о г р а н и ч и т ь р а з м е р с о з д а в а е м о г о м а с с и в а , у к а з а в м а к с и м а л ь н о д о п у с т и м о е з н а ч е н и е верхней г р а н и ц ы при п о м о щ и вто¬ рого п а р а м е т р а метода S p l i t . П о с л е запуска о п и с а н н о й в ы ш е п р о г р а м м ы н а к о н с о л и п о я в и т с я р е з у л ь т а т р а з б о р а строки: myprg х=1 у=4 z 5
Сравнение строк Вы м о ж е т е с р а в н и в а т ь строки т а к и м же о б р а з о м , как и числа. Об этом мы п о д р о б н о рассказывали в главе, посвященной условным операторам. Однако
есть
и
еще
одна
использование
статического
метода
C o m p a r e . С р а в н и в а е м ы е строки п е р е д а ю т с я э т о м у м е т о д у в к а ч е с т в е п а р а м е т р о в . До¬ п о л н и т е л ь н о м е т о д у C o m p a r e м о ж н о у к а з ы в а т ь , каким и м е н н о о б р а з о м в ы п о л н я т ь сравнение. Если строки р а в н ы , метод C o m p a r e в о з в р а щ а е т н у л е в о е з н а ч е н и е . строка
меньше
второй
(используется лексикографическое
сравнение),
Если п е р в а я возвращается
о т р и ц а т е л ь н о е з н а ч е н и е , а если б о л ь ш е — п о л о ж и т е л ь н о е . В п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е 12.16, мы п е р е д а е м м е тоду C o m p a r e 3 п а р а м е т р а . П е р в ы е два из них п р е д с т а в л я ю т собой ссылки на сравни¬ ваемые строки, а третий, имеющий значение
t r u e , у к а з ы в а е т , что при с р а в н е н и и
не с л е д у е т у ч и т ы в а т ь р е г и с т р букв. 12.16.
Файл
using System; n a m e s p a c e Compare class static Глава
void
Работа с
args) строками
383
for строку: string
s "exit",
true)
0)
В нашей программе имеется бесконечный цикл, который прерывает свою работу, если ввести с к о н с о л и слово exit. З а м е т и м , что это слово м о ж е т быть н а б р а н о на лю¬ бом р е г и с т р е (так как при с р а в н е н и и р е г и с т р не у ч и т ы в а е т с я ) . S t r i n g содержит несколько перегруженных методов мы п р и в е л и п р о т о т и п т а к о г о м е т о д а , п р е д н а з н а ч е н н о г о д л я л е к с и к о г р а ф и ч е с к о г о сравне¬ ния т е к с т о в ы х с т р о к с у ч е т о м о с о б е н н о с т е й н а ц и о н а л ь н о г о алфавита: public s t a t i c int String stringl, Int32 String string2, Int32 index2, Int32 length, Boolean ignoreCase, Параметры s t r i n g l и i n d e x l задают соответственно первую сравниваемую с т р о к у и п о з и ц и ю в н у т р и ее. П а р а м е т р ы string2 и i n d e x 2 и м е ю т то же н а з н а ч е н и е , о т н о с я т с я к о в т о р о й с р а в н и в а е м о й с т р о к е . З а м е т и м , что п а р а м е т р ы indexl и i n d e x 2 м о ж н о не у к а з ы в а т ь . В этом случае п р о и с х о д и т с р а в н е н и е п о л н ы х строк. С п о м о щ ь ю н е о б я з а т е л ь н о г о п а р а м е т р а l e n g t h м о ж н о задать м а к с и м а л ь н у ю д л и н у с р а в н и в а е м ы х строк. Если з н а ч е н и е п а р а м е т р а i g n o r e C a s e р а в н о t r u e , т о м е т о д C o m p a r e н е будет у ч и т ы в а т ь р е г и с т р с р а в н и в а е м ы х строк. Однако самый интересный С его п о м о щ ь ю м о ж н о указать ф у н к ц и и н а ц и о н а л ь н ы й а л ф а в и т , к о т о р ы й д о л ж е н п р и м е н я т ь с я для л е к с и к о г р а ф и ч е ¬ ского с р а в н е н и я строк. В л и с т и н г е 12.17 мы п р и в е л и п р и м е р и с п о л ь з о в а н и я м е т о д а C o m p a r e для сравне¬ ния с т р о к с у ч е т о м н а ц и о н а л ь н о г о а л ф а в и т а . 12.17.
Листинг using namespace
Файл
Culture
class
static 384
void
args) А В
Г. В Фролов. Язык
Самоучитель
string s t r l string str2 string str3 string str4
" Я и з л е с у вышел"; "Был сильный м о р о з " ; "Hello, "Learning str2,
true,
str4,
true,
new new
о
Чтобы лексикографическое сравнение строк выполнялось правильно, необходимо у ч и т ы в а т ь о с о б е н н о с т и н а ц и о н а л ь н ы х а л ф а в и т о в , и с п о л ь з о в а н н ы х для п р е д с т а в л е н и я строк.
Библиотека
классов
Framework
содержит
позволяющий или, в терминологии
специальный
задать
класс
Sys-
национальный
язык,
F r a m e w o r k , культуру (culture).
Для о б о з н а ч е н и я к у л ь т у р ы п р о г р а м м и с т м о ж е т и с п о л ь з о в а т ь с и м в о л и ч е с к о е или код. В табл.
имя
12.1 мы привели имена и к о д ы н е к о т о р ы х к у л ь т у р . (Более п о л н ы й
с п и с о к имен и к о д о в к у л ь т у р вы найдете в п р и л о ж е н и и 2 к этой к н и г е . ) О б р а т и т е вни¬ м а н и е , что при с р а в н е н и и с т р о к s t r l и s t r 2 , с о д е р ж а щ и х с и м в о л ы к и р и л л и ц ы , м ы у к а з а л и р у с с к и й алфавит. Для этого мы п е р е д а л и о б о з н а ч е н и е р у с с к о г о а л ф а в и т а ru к о н с т р у к т о р у класса S y s t e m .
Латинский алфавит,
и с п о л ь з о в а н н ы й для с р а в н е н и я строк s t Таблица
12.1.
Имена
Национальный
и
язык,
коды
некоторых
страна
3 и s t r 4 , о б о з н а ч а е т с я как культур
или район
Имя
не у ч и т ы в а е т с я Английский
Код
культуры
0x007F 0x0009
Английский
(Канада)
0x1009
Английский (Объединенное Королевство)
en-GB
0x0809
Английский (Соединенные Штаты Америки)
en-US
0x0409
Арабский
0x0001
Белорусский Белорусский
(Беларусь)
Испанский Испанский
(Испания)
Итальянский Итальянский
(Италия)
Китайский Глава 12. Работа с текстовыми строками 13
культуры
(пустая с т р о к а )
Язык C # С а м о у ч и т е л ь
be
0x0023
be-BY
0x0423
es
OxOOOA
es-ES
OxOCOA
it
0x0010 0x0410 0x0804 385
язык,
страна
Имя
или район ко
Корейский
Код
культуры
0x0012 0x0412
(Корея) de
Немецкий
культуры
0x0007 0x0015
Польский
0x0415
(Польша)
Русский
ru
0x0019
Русский (Россия)
ru-RU
0x0419
Татарский
tt
0x0044
tt-RU
0x0444
Татарский
(Россия)
0x0043
Узбекский Узбекский (Узбекистан, кириллица)
Cy-uz-UZ
0x0843
Узбекский (Узбекистан, латиница)
Lt-uz-UZ
0x0443 0x0022
Украинский Украинский
(Украина)
Французский
uk-UA fr
Французский
(Франция)
fr-FR
Французский
(Швейцария)
fr-CH
Хинди Хинди
0x0422
hi
0x040C 0x0039 0x0439
(Индия)
0x0011 Японский
(Япония)
ja-JP
0x0411
Форматирование текстовых строк Одна и з в а ж н е й ш и х з а д а ч , к о т о р у ю п р и х о д и т с я р е ш а т ь п р о г р а м м и с т у при р а з р а б о т к е п р и л о ж е н и й л ю б о г о типа, — ф о р м а т и р о в а н и е т е к с т о в ы х с т р о к . С п о м о щ ь ю т е к с т о в ы х строк о б ы ч н о п р е д с т а в л я е т с я ч и с л о в а я и н ф о р м а ц и я , т а к а я , как н о м е р а з а к а з о в , коли¬ чество к а к и х - л и б о п р е д м е т о в , ц е н ы , д а т а , в р е м я и т. д. Если вы п р о г р а м м и р о в а л и раньше на я з ы к е С или С + + , то вам з н а к о м ы такие сред ства ф о р м а т и р о в а н и я , как ф у н к ц и и и а также управляющие симво лы в п о т о к а х в ы в о д а . Эти с р е д с т в а , с т а в ш и е с т а н д а р т н ы м и , п о з в о л я ю т п р е д с т а в и т ь числа различных типов практически в л ю б о м формате. Что же к а с а е т с я С # , то сам по себе этот я з ы к не с о д е р ж и т с р е д с т в ф о р м а т и р о в а н и я строк. О д н а к о б о г а т е й ш и е в о з м о ж н о с т и т а к о г о ф о р м а т и р о в а н и я п р е д о с т а в л я ю т с я п р о г р а м м и с т у в р а м к а х б и б л и о т е к и к л а с с о в Microsoft F r a m e w o r k . Мы ис пользовали некоторые средства форматирования, предоставляемые методом C o n 1е когда в ы в о д и л и в к о н с о л ь н о е о к н о ш е с т н а д ц а т е р и ч н ы е числа. А н а л о г и ч н о е ф о р м а т и р о в а н и е д о с т у п н о при ф о р м и р о в а н и и т е к с т о в ы х строк м е т о д о м String н е и м е ю щ и м н и к а к о г о о т н о ш е н и я к к о н с о л ь н о м у выводу. 386
А В Фролов. Г В Фролов. Язык С# Самоучитель
Так как д а н н ы е л ю б о г о типа в С# я в л я ю т с я о б ъ е к т а м и н е к о т о р ы х к л а с с о в , это от¬ к р ы в а е т н о в ы е в о з м о ж н о с т и для ф о р м а т н о г о п р е д с т а в л е н и я этих о б ъ е к т о в в виде тек¬ стовых строк. В этом разделе мы р а с с м о т р и м этот в о п р о с п р и м е н и т е л ь н о к форматно¬ му п р е д с т а в л е н и ю чисел. З а м е т и м , что б и б л и о т е к а классов F r a m e w o r k п о з в о л я е т легко р е ш и т ь и обрат¬ н у ю задачу — п р е о б р а з о в а н и е т е к с т о в ы х строк с ч и с л а м и в ч и с л о в ы е з н а ч е н и я . Это н е о б х о д и м о , н а п р и м е р , для о б р а б о т к и ч и с е л , в в е д е н н ы х п о л ь з о в а т е л я м и при п о м о щ и клавиатуры.
Представление целых чисел Ч т о б ы п р е о б р а з о в а т ь ц е л о ч и с л е н н о е з н а ч е н и е в т е к с т о в у ю строку с ф о р м а т и р о в а н и е м при п о м о щ и м е т о д а F o r m a t , н е о б х о д и м о задать этому методу так н а з ы в а е м у ю строку формата (format а т а к ж е п е р е д а т ь в к а ч е с т в е п а р а м е т р о в о д н о или н е с к о л ь к о п р е о б р а з у е м ы х з н а ч е н и й . В ответ д а н н ы й м е т о д в о з в р а т и т о т ф о р м а т и р о ¬ ванную строку. П о п р и н ц и п у и с п о л ь з о в а н и я метод S t r i n g . F o r m a t б о л ь ш е всего п о х о ж н а ф у н к цию
з н а к о м у ю всем п р о г р а м м и с т а м С .
Строка формата задается методу
F o r m a t в с л е д у ю щ е м виде:
N
М
Здесь
ч и с л о N задает н о м е р п р е о б р а з у е м о г о F o r m a t в качестве параметра.
аргумента,
передаваемого
методу
Необязательное число задает ш и р и н у области т е к с т о в о й строки (в с и м в о л а х ) , внутри к о т о р о й н е о б х о д и м о п о м е с т и т ь ц и ф р ы п р е о б р а з у е м о г о з н а ч е н и я . Е с л и это число о т р и ц а т е л ь н о е , ц и ф р ы ч и с л а в ы р а в н и в а ю т с я п о л е в о й г р а н и ц е д а н н о й о б л а с т и , а если п о л о ж и т е л ь н о е — по правой г р а н и ц е о б л а с т и . И наконец, строка задает к о д ы ф о р м а т и р о в а н и я , к о т о р ы е м ы с к о р о р а с с м о т р и м . Для ф о р м а т и р о в а н и я ц е л ы х чисел и с п о л ь з у ю т с я э к в и в а л е н т н ы е к о д ы ф о р м а т и р о в а н и я d и D. Р а с с м о т р и м п р о г р а м м у , и с х о д н ы й текст к о т о р о й Эта п р о г р а м м а д е м о н с т р и р у е т п р и м е н е н и е р а з л и ч н ы х с п о с о б о в п р е д с т а в л е н и я ц е л ы х чисел в в и д е т е к с т о в ы х с т р о к . 12.18.
в л и с т и н г е 12.18. ф о р м а т и р о в а н и я для
Файл
using System; namespace StringFormat class static
void
int string
args) 777; result;
result
Глава
Работа с
строками
387
result
result
result
result
result
result
счастливое
Автоматическое форматирование В с а м о м п р о с т е й ш е м случае м о ж н о в о о б щ е не при этом будет и с п о л ь з о в а н ф о р м а т по у м о л ч а н и ю : int iSignedNumber string result;
указывать
код
форматирования,
777;
result В д а н н о м с л у ч а е на к о н с о л ь будет в ы в е д е н а с т р о к а 7 7 7 . А н а л о г и ч н о г о р е з у л ь т а т а м о ж н о б ы л о бы Добиться и с л е д у ю щ и м более п р о с т ы м о б р а з о м :
И м е н н о так мы в ы в о д и л и на к о н с о л ь д а н н ы е в п р и м е р а х п р о г р а м м , в нашей к н и г е . З а м е т и м , о д н а к о , что этот с п о с о б п о д х о д и т т о л ь к о для к о н с о л ь н ы х п р о г р а м м . Что же к а с а е т с я п р о г р а м м с о к о н н ы м п о л ь з о в а т е л ь с к и м и н т е р ф е й с о м , т о т а м , как п р а в и л о , н е о б х о д и м о п е р е д в ы в о д о м п р е о б р а з о в ы в а т ь ч и с л о в ы е значения в т е к с т о в ы е с т р о к и . Эта задача и р е ш а е т с я при п о м о щ и м е т о д а S t r i n g
Представление чисел в
формате
Если вам н у ж н о п р е д с т а в и т ь ц е л о ч и с л е н н о е з н а ч е н и е в ш е с т н а д ц а т е р и ч н о м ф о р м а т е , н е о б х о д и м о у к а з а т ь код ф о р м а т и р о в а н и я х или X: result
388
А
Г.
Фролов. Язык
Самоучитель
result
0x23fabc);
В п е р в о м случае ш е с т н а д ц а т е р и ч н о е число 0x23fabc б у д е т о т о б р а ж а т ь с я с и с п о л ь з о в а н и е м н и ж н е г о р е г и с т р а , а во втором
верхнего:
23 При отображении
ч и с е л п р е ф и к с Ох не д о б а в л я е т с я , так что
вы м о ж е т е в ы д е л я т ь
числа л ю б ы м с п о с о б о м по в а ш е м у у с м о т р е
н и ю или не в ы д е л я т ь их вовсе.
Определение ш и р и н ы поля вывода Непосредственно после кода ф о р м а т и р о в а н и я вы м о ж е т е у к а з а т ь ш и р и н у поля в ы в о д а в с и м в о л а х . П р и этом если в ч и с л е м е н ь ш е ц и ф р , чем ш и р и н а поля в ы в о д а , то при ф о р м и р о в а н и и строки это ч и с л о будет д о п о л н е н о слева н у л я м и . Если ж е ц и ф р б о л ь ш е , чем з н а ч е н и е ш и р и н ы в ы в о д а , то будут в ы в е д е н ы все ц и ф р ы числа. Р а с с м о т р и м с л е д у ю щ и й п р и м е р , где м ы ф о р м а т и р у е м т р е х з н а ч н о е ч и с л о : int iSignedNumber string result;
777;
result
result
В р е з у л ь т а т е р а б о т ы этого ф р а г м е н т а п р о г р а м м ы на к о н с о л и п о я в я т с я с л е д у ю щ и е две с т р о к и : 777 00000777 Н е с м о т р я на то что в п е р в о м случае мы у к а з а л и в с т р о к е ф о р м а т а ш и р и н у п о л я , р а в н у ю д в у м , в п о л у ч е н н о й т е к с т о в о й с т р о к е п р и с у т с т в у ю т все ц и ф р ы и с х о д н о г о чис¬ ла. Таким о б р а з о м , ф о р м а т и р о в а н и е не м о ж е т и с к а з и т ь з н а ч е н и е числа. Во в т о р о м с л у ч а е ш и р и н а поля в ы в о д а с о с т а в л я е т 8 с и м в о л о в , но в ч и с л е т о л ь к о 3 значащих цифры.
П о э т о м у при ф о р м а т и р о в а н и и это число
было д о п о л н е н о слева
пятью нулями. Ш и р и н а поля в ы в о д а м о ж е т з а д а в а т ь с я и при о т о б р а ж е н и и числе в ш е с т н а д ц а т е р и ч н о й с и с т е м е счисления.
Выравнивание числа внутри поля вывода У к а з а в ш и р и н у поля в ы в о д а после н о м е р а а р г у м е н т а (через з а п я т у ю ) , м о ж н о выпол¬ нить в ы р а в н и в а н и е числа внутри этого п о л я . Глава 12. Работа с
строками
389
Ш и р и н а поля в ы в о д а м о ж е т з а д а в а т ь с я как п о л о ж и т е л ь н ы м и ч и с л а м и , так и отри¬ цательными: -
result
счастливое
число",
В первом и в т о р о м случаях мы у к а з а л и ш и р и н у поля в ы в о д а , р а в н у ю пяти симво¬ л а м . К о г д а это ч и с л о п о л о ж и т е л ь н о е , с и м в о л ы п р о б е л а д о б а в л я ю т с я слева, а когда от¬ р и ц а т е л ь н о е — справа: 777 777 Это
счастливое
число
Таким о б р а з о м , у к а з ы в а я ш и р и н у поля в ы в о д а , в ы м о ж е т е в ы р а в н и в а т ь отобра¬ ж а е м ы е числа по п р а в о й или левой г р а н и ц е поля.
Представление чисел с фиксированной десятичной точкой Для п р е д с т а в л е н и я чисел с ф и к с и р о в а н н о й д е с я т и ч н о й т о ч к о й и с п о л ь з у ю т с я равно¬ з н а ч н ы е к о д ы ф о р м а т а d и D. В с л е д за к о д о м ф о р м а т а о б ы ч н о у к а з ы в а ю т н е о б х о д и м о е к о л и ч е с т в о з н а к о в после д е с я т и ч н о й т о ч к и . П р и м е р п р о г р а м м ы ф о р м а т н о г о в ы в о д а чисел с ф и к с и р о в а н н о й д е с я т и ч н о й т о ч к о й п р и в е д е н в л и с т и н г е 12.19. Листинг
12.19.
Файл
using System; namespace Decimal class
DecimalApp
static
void
double string
args)
pi 3.1415926; result;
result
result
result
result
390
pi);
А В
Г
Фролов Язык С#
result
pi);
result
pi);
Ф о р м а т ПО у м о л ч а н и ю П р е ж д е всего н а ш а п р о г р а м м а в ы в о д и т з н а ч е н и е ч и с л а тирование по умолчанию: double string
и с п о л ь з у я для этого форма¬
pi 3.1415926; result;
result
Д а н н о е з н а ч е н и е будет в ы в е д е н о на к о н с о л ь в с л е д у ю щ е м виде:
М о ж н о и с п о л ь з о в а т ь т о л ь к о код ф о р м а т а , не у к а з ы в а я к о л и ч е с т в о з н а к о в п о с л е де¬ сятичной точки: result
В этом с л у ч а е , о д н а к о , з н а ч е н и е м о ж е т быть о к р у г л е н о : 3 , 14 Как
результат,
точность
отображаемого
значения
может
оказаться
недоста¬
точной.
У к а з а н и е к о л и ч е с т в а знаков после д е с я т и ч н о й т о ч к и Для п о л у ч е н и я п р е д с к а з у е м о г о р е з у л ь т а т а мы р е к о м е н д у е м всегда задавать необходи¬ мое к о л и ч е с т в о знаков после д е с я т и ч н о й т о ч к и : result
В этом п р и м е р е п о с л е запятой будет о т о б р а ж е н о 3 ц и ф р ы : 142
12.
с текстовыми
391
Ограничение ширины поля вывода Для о г р а н и ч е н и я ш и р и н ы поля в ы в о д а м о ж н о задать н е о б х о д и м о е п я т у ю после н о м е р а а р г у м е н т а м е т о д а
через за
F o r m a t , как м ы это д е л а л и для целых
чисел: result
pi);
В р е з у л ь т а т е на консоль будет в ы в е д е н о т о л ь к о 5 с и м в о л о в н а ш е г о ч и с л а ( в к л ю ч а я десятичную запятую):
Извлечение целой части числа Если вам н у ж н о о т ф о р м а т и р о в а т ь при в ы в о д е ч и с л о с п л а в а ю щ е й д е с я т и ч н о й т о ч к о й т а к и м о б р а з о м , ч т о б ы о т о б р а з и т ь т о л ь к о ц е л у ю часть числа, у к а ж и т е к о л и ч е с т в о цифр после д е с я т и ч н о й т о ч к и , р а в н о е н у л ю : result
Этот ф р а г м е н т кода в ы в е д е т на к о н с о л ь ч и с л о 3.
Избыточная точность при выводе Если ш и р и н а п о л я в ы в о д а п р е в ы ш а е т к о л и ч е с т в о з н а ч а щ и х ц и ф р в числе с фиксиро¬ ванной д е с я т и ч н о й т о ч к о й , то такое число б у д е т д о п о л н е н о с п р а в а н е о б х о д и м ы м ко¬ личеством нулей. Н а п р и м е р , з д е с ь мы ф о р м а т и р у е м н а ш е ч и с л о для в ы в о д а в поле ш и р и н о й 10 сим¬
result
П р и этом к ч и с л у будет д о п и с а н о с п р а в а 3 нуля: 3,1415926000 О б р а т и т е в н и м а н и е , что т а к и м с п о с о б о м м ы н е у в е л и ч и л и т о ч н о с т ь п р е д с т а в л е н и я числа
а т о л ь к о д о б а в и л и к числу д о п о л н и т е л ь н ы е нули.
Представление чисел в научном формате Н а у ч н ы й , или э к с п о н е н ц и а л ь н ы й ,
формат обычно используется в научных расчетах
для п р е д с т а в л е н и я ч и с е л , л е ж а щ и х в б о л ь ш о м д и а п а з о н е з н а ч е н и й . Д л я форматирова¬ ния т а к и х ч и с е л п р и м е н я ю т с я коды ф о р м а т о в е и Е. П р и м е р п р о г р а м м ы , о т о б р а ж а ю щ е й на к о н с о л и о т ф о р м а т и р о в а н н ы е ч и с л а в науч¬ ном ф о р м а т е , п р е д с т а в л е н в л и с т и н г е 12.20.
392
А В. Фролов, Г. В. Фролов. Язык
Самоучитель
Листинг
12.20.
Файл
using System; namespace Science class static
void
double string result
args)
pi 0.31415926E1; result; pi)
result
result
Здесь мы будем в ы в о д и т ь з н а ч е н и е ч и с л а тс, п р е д с т а в л е н н о г о в н а у ч н о м ф о р м а т е : double
pi
Такие ч и с л а м о ж н о в ы в о д и т ь с и с п о л ь з о в а н и е м ф о р м а т а по у м о л ч а н и ю : result Если при э т о м р е з у л ь т а т м о ж н о будет п р е д с т а в и т ь в ф о р м а т е с ф и к с и р о в а н н о й де¬ с я т и ч н о й т о ч к о й , то он и б у д е т и с п о л ь з о в а н . В о т что вы у в и д и т е на к о н с о л и , з а п у с т и в нашу программу:
П е р в о е ч и с л о в ы в о д и т с я с и с п о л ь з о в а н и е м ф о р м а т а по у м о л ч а н и ю . В о в т о р о м случае м ы у к а з а л и ф о р м а т в ы в о д а я в н ы м о б р а з о м : result И н а к о н е ц , в т р е т ь е м с л у ч а е мы о г р а н и ч и л и т р е м я к о л и ч е с т в о ц и ф р , о т о б р а ж а е м ы х после з а п я т о й : result
Глава
Работа с
393
Выделение тысяч при отображении больших чисел При б о л ь ш и х м н о г о з н а ч н ы х чисел для большей н а г л я д н о с т и часто отде¬ л я ю т д р у г от д р у г а р а з р я д ы тысяч при п о м о щ и п р о б е л а (по 3 р а з р я д а ) . Для п о д о б н о г о ф о р м а т и р о в а н и я п р и м е н я е т с я код ф о р м а т а п или N. Р а с с м о т р и м п р и м е р п р о г р а м м ы ( л и с т и н г 12.21). Листинг
Файл
using System; namespace
static
void
double string
args)
oneMByte result;
1 0 4 8 5 7 6;
result
result
result
Здесь мы с н а ч а л а у к а з ы в а е м код ф о р м а т а п, не д о п о л н я я его к о л и ч е с т в о м ц и ф р д р о б н о й части: double string
oneMByte result;
1048576;
result В р е з у л ь т а т е н а ш е ч и с л о ( к о л и ч е с т в о байтов в о д н о м м е г а б а й т е ) будет о т о б р а ж е н о следующим образом: 1
048
576,00
П р и н е о б х о д и м о с т и м ы м о ж е м у к а з а т ь н у ж н о е к о л и ч е с т в о ц и ф р после з а п я т о й : result
result
394
А В.
Г. В Фролов. Язык
Самоучитель
В первом случае мы указали 4 ц и ф р ы п о с л е з а п я т о й , а во в т о р о м — 3. В о т что по¬ л у ч и л о с ь после з а п у с к а п р о г р а м м ы : 1 1
048 048
576,0000 576,000
Универсальный формат для представления чисел В тех с л у ч а я х , когда р е з у л ь т а т в ы ч и с л е н и й д о л ж е н быть п р е д с т а в л е н в н а и б о л е е на¬ глядном в и д е , у д о б н е е и с п о л ь з о в а т ь у н и в е р с а л ь н ы й ф о р м а т . Код этого ф о р м а т а
g
или В з а в и с и м о с т и от т о г о , какое ф о р м а т и р у е т с я , система а в т о м а т и ч е с к и вы¬ бирает ф о р м а т с п л а в а ю щ е й д е с я т и ч н о й точкой или н а у ч н ы й формат. Р а с с м о т р и м п р о г р а м м у , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е 12.22. Листинг
12.22.
using System; namespace General class
GeneralApp
static
void
double double string
args)
oneMByte small result;
1 0 4 8 5 7 6;
result
result
result
oneMByte)
result
small)
Здесь мы в ы в о д и м два числа, одно из к о т о р ы х по своей в е л и ч и н е дос¬ таточно большое, а другое — маленькое: double double string
oneMByte small result;
1 0 4 8 5 7 6;
Глава 12. Работа с текстовыми строками
395
result result При этом д л я п е р в о г о числа будет а в т о м а т и ч е с к и в ы б р а н ф о р м а т с п л а в а ю щ е й де¬ сятичной точкой, а для второго научный формат: 1048576 1, 2345е-06 При н е о б х о д и м о с т и м о ж н о о г р а н и ч и т ь т о ч н о с т ь о т о б р а ж а е м о г о з н а ч е н и я , указав к о л и ч е с т в о з н а ч а щ и х д р о б н ы х разрядов: result
result
small)
В о т р е з у л ь т а т р а б о т ы этого ф р а г м е н т а кода:
Формат для представления денежных сумм С п е ц и а л ь н о для в ы в о д а з н а ч е н и й д е н е ж н ы х сумм п р е д у с м о т р е н ы ф о р м а т ы с и
С.
П р и и с п о л ь з о в а н и и этих ф о р м а т о в ч и с л а в ы в о д я т с я с р а з д е л е н и е м р а з р я д о в на т ы с я ч и , а также с добавлением обозначения денежной единицы. Символ-разделитель, а также о б о з н а ч е н и е д е н е ж н о й е д и н и ц ы з а в и с я т от н а с т р о й к и л о к а л и з а ц и и в у п р а в л я ю щ е й па¬ нели ОС Microsoft W i n d o w s . Пример
программы,
отображающей
форматах, приведен в листинге Листинг
12.23.
на. к о н с о л и
денежную
сумму
в
различных
12.23.
Файл
using System; namespace Currency class
CurrencyApp
static void double string
args)
result;
result
396
А В Фролов, Г. В. Фролов. Язык
Самоучитель
result
result
Вот что эта п р о г р а м м а в ы в о д и т на к о н с о л ь : 1 1 1
048 048 048
576,25р. 576,250р.
При н а с т р о й к е л о к а л и з а ц и и для Р о с с и и в к а ч е с т в е о б о з н а ч е н и я д е н е ж н о й е д и н и ц ы и с п о л ь з у е т с я р у б л ь . У к а з а в к о л и ч е с т в о р а з р я д о в после з а п я т о й , р а в н о е д в у м , м о ж н о о т о б р а ж а т ь не т о л ь к о р у б л и , но и к о п е й к и .
Использование шаблонов при форматировании Ш а б л о н ы о т к р ы в а ю т перед п р о г р а м м и с т а м и д о п о л н и т е л ь н ы е в о з м о ж н о с т и формати¬ р о в а н и я чисел в п р о ц е с с е их п р е о б р а з о в а н и я в т е к с т о в ы е с т р о к и . Ш а б л о н ы с о з д а ю т с я с п о м о щ ь ю с и м в о л о в 0, % и точки с з а п я т о й . И с п о л ь з о в а н и е этих с и м в о л о в з а в и с и т о т с п о с о б а ф о р м а т и р о в а н и я .
Форматирование целых чисел П р и ф о р м а т и р о в а н и и ц е л ы х чисел с и м в о л 0 с о о т в е т с т в у е т с и м в о л у п р е о б р а з у е м о й строки или н у л е в о м у з н а ч е н и ю (если д а н н а я п о з и ц и я н е и с п о л ь з у е т с я ) . П о з и ц и я , в к о т о р о й стоит с и м в о л будет и г н о р и р о в а т ь с я , если в ш а б л о н е п е р е д ней не с т о и т 0 или д р у г о й с и м в о л . В п е р в о м с л у ч а е будет в ы в е д е н о н у л е в о е з н а ч е н и е или с и м в о л п р е о б р а з у е м о й с т р о к и , а во в т о р о м — д а н н а я п о з и ц и я будет п р о п у щ е н а или в ней б у д е т с и м в о л п р е о б р а з о в а н н о й с т р о к и . Р а с с м о т р и м п р о г р а м м у , о т о б р а ж а ю щ у ю н а к о н с о л и 7-значный т е л е ф о н н ы й н о м е р ( л и с т и н г 12.24). ( Т е л е ф о н н ы й н о м е р п р и в е д е н в ней т о л ь к о для п р и м е р а , так что не п ы т а й т е с ь по н е м у звонить!) Листинг
12.24.
Файл
using System; namespace class static
void
Глава 12. Работа с текстовыми строками
args)
397
u i n t number 9367733 string result;
только
для
result
result
result
В п е р в о м с л у ч а е мы в ы д е л я е м в т е л е ф о н н о м н о м е р е г р у п п ы ц и ф р , р а з д е л я я их де¬ фисом: result В о т что п о я в и т с я на к о н с о л и в р е з у л ь т а т е в ы п о л н е н и я этих с т р о к : 936-77-33 Если н у ж н о д о п о л н и т ь о т о б р а ж а е м о е целое число н у л я м и с левой с т о р о н ы , укажи¬ те н е о б х о д и м о е к о л и ч е с т в о нулей в л е в о й п о з и ц и и : result Т е п е р ь н а ш н о м е р будет д о п о л н е н слева о д н и м н у л е м : 0936-77-33 Если у к а з а т ь м е н ь ш е н у л е й , чем н а д о , ч и с л о все р а в н о будет в ы в е д е н о п о л н о с т ь ю : result Вот результат работы
фрагмента программы:
936-77-33
Форматирование чисел с плавающей десятичной точкой При чисел с десятичной точкой символы шаблона 0 и п р и м е н я ю т с я а н а л о г и ч н о т о м у , как они п р и м е н я ю т с я при ф о р м а т и р о в а н и и ц е л ы х чи¬ сел. Д о п о л н и т е л ь н о для в ы д е л е н и я т ы с я ч в ш а б л о н е п р и м е н я е т с я з а п я т а я . Мы приведем пример использования нестандартного форматирования для решения та кой задачи, как выделение тысяч при выводе чисел (листинг 12.25).
398
А В. Фролов, Г. В Фролов. Язык
Самоучитель
Листинг
12.25.
Файл
using System; namespace Template2 class static
void
double
args)
number
string
result;
result
Здесь мы у к а з а л и в ш а б л о н е м е ж д у с и м в о л а м и при в ы в о д е числа р а з р я д ы т ы с я ч
символ з а п я т о й , в р е з у л ь т а т е чего
будут в ы д е л е н ы . Н а ш а п р о г р а м м а в ы в о д и т ч и с л о
в и с х о д н о м и о т ф о р м а т и р о в а н н о м виде: 478
903
208
Обратите
236,568000 внимание,
В результате
наше
что
число
мы
задали
отображение шести
цифр
после запятой.
после округления было д о п о л н е н о тремя н е з н а ч а щ и м и
нулями.
Форматирование чисел с процентами Если к с т р о к е ш а б л о н а д о б а в и т ь с и м в о л п р о ц е н т а
то перед в ы в о д о м з н а ч е н и е
числа будет у в е л и ч е н о в 100 раз. К р о м е т о г о , будет в ы в е д е н и сам с и м в о л Этот п р и е м ф о р м а т и р о в а н и я д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст к о т о рой п р и в е д е н в л и с т и н г е 12.26. Листинг
12.26.
Файл
using System; namespace TemplatePercent class
TemplatePercentApp
static
void
double string Глава 12. Работа с
number result;
args) 0.4756; строками
399
result
Вот что эта п р о г р а м м а в ы в е д е т на к о н с о л ь : 47, З а м е т и м , что в о д н о м ш а б л о н е м о ж е т в с т р е ч а т ь с я н е с к о л ь к о с и м в о л о в и з них будет д е й с т в о в а т ь о п и с а н н ы м в ы ш е о б р а з о м .
Каждый
Форматирование с учетом знака чисел Если для п о л о ж и т е л ь н ы х , о т р и ц а т е л ь н ы х и н у л е в ы х чисел н у ж н о и с п о л ь з о в а т ь разное ф о р м а т и р о в а н и е , вы м о ж е т е у к а з а т ь 3 ш а б л о н а , р а з д е л и в их т о ч к о й с з а п я т о й . П е р в ы й из этих ш а б л о н о в будет и с п о л ь з о в а т ь с я для в ы в о д а п о л о ж и т е л ь н ы х ч и с е л , второй — отрицательных, а третий — равных нулю. В л и с т и н г е 12.27 мы привели и с х о д н ы й т е к с т п р о г р а м м ы , д е м о н с т р и р у ю щ е й ис¬ пользование описанного выше форматирования. 12.27.
Файл
using System; namespace class static
void
string
args) result;
result result
0);
result
-10);
В о т что н а ш а п р о г р а м м а вывела на к о н с о л ь после запуска: плюс 0 минус Как венно.
мы смогли заменить с и м в о л ы
и - на слова п л ю с и м и н у с соответст¬
А В. Фролов, Г. В. Фролов. Язык
Самоучитель
Создание новых форматов Как вы объект л ю б о г о класса м о ж е т быть представлен в виде текстовой строки. Для э т о г о в классе н е о б х о д и м о определить м е т о д T o S t r i n g . Однако таким о б р а з о м м о ж н о получить только о д н о - е д и н с т в е н н о е текстовое представление объекта, что не Реализуя м е т о д T o S t r i n g интерфейса ваша программа сможет выбирать различные форматы для преобразования объектов в текстовые строки. Рассмотрим пример программы,
исходный текст которой представлен в л и с т и н
ге 12.28. Листинг
12.28.
Файл
ch
using System; namespace class
MyNumber:
IFormattable
public
uint
public
MyNumber ( u i n t
number public
n)
n;
string
ToString (string
format,
I Format P r o v i d e r
fp)
i return else return else return else return
class С static
number
void
MyNumber n Глава
fp)
args) new
Работа с текстовыми строками
(10241024)
401
String
В
result
result
null);
result
null);
этой
программе
class
мы
объявили
класс
реализующий
интерфейс
IFormattable
public public
MyNumber ( u i n t
number
public
n)
n
string
ToString (string
format,
fp)
В рамках э т о г о интерфейса мы определили м е т о д T o S t r i n g , которому п е р е д а ю т ся д в а параметра — текстовая строка с о б о з н а ч е н и е м формата и ссылка на интерфейс провайдера формата I F o r m a t P r o v i d e r . Класс M y N u m b e r с о д е р ж и т также конструктор и поле n u m b e r для хранения целых чисел б е з В о т и с х о д н ы й текст м е т о д а T o S t r i n g , выполняющего т о или и н о е форматирова ние в з а в и с и м о с т и от строки формата, передаваемого в качестве первого параметра: public
string
ToString (string
format,
IFormatProvider
fp)
return
return
10);
lse return
402
Фролов, Г.
Фролов. Язык
Самоучитель
return
fp)
Преобразование vert
чисел
ToString.
в
текстовые
строки
выполняется
здесь
методом
Con
В качестве п е р в о г о п а р а м е т р а этому м е т о д у передается п р е о б р а
з у е м о е ч и с л о , а в качестве второго
о с н о в а н и е ( д в о и ч н о е , д е с я т и ч н о е или ш е с т н а -
дцатеричное). Получив MyNumber
управление,
метод
Main
нашей
программы
создает
объект
класса
при п о м о щ и к о н с т р у к т о р а , а затем п р е о б р а з у е т его в т е к с т о в у ю строку
тремя
способами:
MyNumber n
new
String result
result
null);
result
null);
В
первом
случае з н а ч е н и е
числа
выводится
на к о н с о л ь
в
двоичном
формате,
во в т о р о м — в д е с я т и ч н о м ф о р м а т е , а в т р е т ь е м — в ш е с т н а д ц а т е р и ч н о м ф о р м а т е : 100111000100010000000000 10241024 9с4400 В т о м с л у ч а е , если ни один у к а з а н н ы й ф о р м а т не п о д о й д е т , наш м е т о д T o S t r i n g использует форматирование по умолчанию.
Преобразование текстовых строк в числа Очень часто перед программистом встает задача преобразования текстовых строк в числа. Для решения этой задачи проще всего воспользоваться методом P a r s e , предусмотрен ным с этой целью в классах числовых типов (таких, как I n t l 6
I n t 3 2 и т. д.).
Р а з у м е е т с я , не в с я к у ю т е к с т о в у ю с т р о к у м о ж н о п р е о б р а з о в а т ь в ч и с л о . О б ы ч н о программистам
приходится
проверять,
содержит
ли
такая
строка
только
цифры
или д р у г и е д о п у с т и м ы е с и м в о л ы . М е т о д P a r s e и з б а в и т вас о т этой н е о б х о д и м о с т и , так как при н е в о з м о ж н о с т и в ы п о л н и т ь п р е о б р а з о в а н и е о б р а з у е т с я и с к л ю ч е н и е S y s ¬ FormatException. И с п о л ь з о в а н и е метода P a r s e д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст к о т о рой п р и в е д е н в л и с т и н г е Глава 12. Работа с
строками
403
Листинг
12.29.
Файл
using System; namespace Text2Number class static
void
int
args)
0;
число: input
try val ex) Ошибка:
число
val);
мы в ц и к л е в в о д и м т е к с т о в ы е с т р о к и и в ы п о л н я е м п о п ы т к у их преобразова¬ ния в числа. В о т как в ы г л я д и т к о д , в ы п о л н я ю щ и й п р е о б р а з о в а н и е :
val ex)
404
А В.
Г. В. Фролов.
Самоучитель
Здесь м ы в ы з ы в а е м с т а т и ч е с к и й м е т о д I n t 3 2 P a r s e , передавая ему п р е о б р а з у е м у ю т е к с т о в у ю строку. Если т е к с т о в у ю строку у д а е т с я п р е о б р а з о в а т ь в ч и с л о в о е зна¬ ч е н и е , метод в о з в р а щ а е т р е з у л ь т а т п р е о б р а з о в а н и я . В п р о т и в н о м случае в о з н и к а е т ис¬ ключение Если в о з н и к л о и с к л ю ч е н и е , н а ш а п р о г р а м м а в о з о б н о в л я е т работу цикла с с а м о г о начала. Вот пример сценария работы с программой: Введите Ввели ч и с л о Введите
123 123
число: Input
s t r i n g was
not
in
a
correct
С т р о к а 1 2 3 была у с п е ш н о п р е о б р а з о в а н а в ч и с л о
format. а попытка преобразования
в число строки q w e r t y з а в е р ш и л а с ь а в а р и й н о с в о з н и к н о в е н и е м и с к л ю ч е н и я tem.
Sys
FormatException.
Глава 1 2 . Работа с текстовыми строками
405
Глава 13. Контейнеры для хранения объектов При создании п р о г р а м м самого р а з н о г о н а з н а ч е н и я часто п р и х о д и т с я р е ш а т ь задачу создания н а б о р о в д а н н ы х , п о з в о л я ю щ и х х р а н и т ь д а н н ы е в с т р у к т у р и р о в а н н о м виде. П р о с т е й ш и м п р и м е р о м такого набора д а н н ы х м о ж е т п о с л у ж и т ь о б ы к н о в е н н ы й сив. М а с с и в п о з в о л я е т х р а н и т ь о б ъ е к т ы о д н о г о и того же т и п а , а д р е с у я с ь к ним по н о м е р а м , т. е. с и с п о л ь з о в а н и е м и н д е к с а Зная и н д е к с , вы м о ж е т е и з м е н и т ь или извлечь с о д е р ж и м о е л ю б о й я ч е й к и массива. Заметим, что, несмотря на удобство использования, обычным массивам присущи довольно существенные недостатки: •
р а з м е р массива нельзя и з м е н и т ь после т о г о , как м а с с и в был создан;
•
массив н а в я з ы в а е т у п о р я д о ч е н н о е р а с п о л о ж е н и е д а н н ы х , д а ж е если по своей внут¬ ренней природе данные неупорядоченны;
•
для поиска д а н н ы х в массиве необходимо последовательно перебрать все его ячейки.
Р а з у м е е т с я , эти н е д о с т а т к и будут сказываться д а л е к о не всегда. О д н а к о в ряде слу¬ чаев д а н н ы е н а м н о г о у д о б н е е х р а н и т ь с п о м о щ ь ю д р у г и х с р е д с т в , в ч а с т н о с т и с по¬ м о щ ь ю так н а з ы в а е м ы х контейнеров. К о н т е й н е р ы п р е д с т а в л я ю т собой к л а с с ы , ально п р е д н а з н а ч е н н ы е для у п о р я д о ч е н н о г о х р а н е н и я д а н н ы х р а з л и ч н ы х т и п о в . В т е р м и н а х я з ы к а С# такие к о н т е й н е р ы н а з ы в а ю т с я наборами данных (collections), од нако в н а ш е й к н и г е мы будем и с п о л ь з о в а т ь более п р и в ы ч н ы й т е р м и н контейнер.
Контейнеры в библиотеке классов Framework Б и б л и о т е к а классов Microsoft F r a m e w o r k с о д е р ж и т н е с к о л ь к о к л а с с о в , позво¬ л я ю щ и х создавать к о н т е й н е р ы с л е д у ю щ и х т и п о в :
•
массив
•
словарь
is Hashtable,
сортированный •
стек
•
очередь Q u e u e ,
список
Stack,
массив
BitArray.
Д а л е е в этой главе мы п о д р о б н о р а с с к а ж е м вам об и с п о л ь з о в а н и и этих контейне¬ ров и п р и в е д е м п р и м е р ы п р о г р а м м .
406
Массив ArrayList Как м ы у ж е г о в о р и л и , о б ы ч н ы е м а с с и в ы о б л а д а ю т н е д о с т а т к а м и , н е с к о л ь к о о г р а н и ч и в а ю щ и м и и х п р и м е н е н и е . Н и ж е м ы р а с с м о т р и м п р е и м у щ е с т в а использо¬ вания м а с с и в а ArrayList, а также п р и в е д е м п р и м е р ы п р о г р а м м , р а б о т а ю щ и х с та¬ кими м а с с и в а м и .
Создание массива М а с с и в A r r a y L i s t создается о б ы ч н ы м о б р а з о м , как и о б ъ е к т л ю б о г о д р у г о г о клас¬ са, — с п о м о щ ь ю к о н с т р у к т о р а . Н а п р и м е р , здесь мы создали м а с с и в A r r a y L i s t myWords
new
Это один и з трех в о з м о ж н ы х к о н с т р у к т о р о в класса A r r a y L i s t . В т о р о й в а р и а н т к о н с т р у к т о р а п о з в о л я е т при создании массива задать его началь¬ ный р а з м е р : A r r a y L i s t myWords
new A r r a y L i s t
Здесь сразу п о с л е с о з д а н и я м а с с и в m y W o r d s будет с о д е р ж а т ь 10 я ч е е к (по у м о л ч а н и ю создается м а с с и в р а з м е р о м 16 я ч е е к ) . З а м е т и м , что при н е о б х о д и м о с т и по мере д о б а в л е н и я э л е м е н т о в р а з м е р т а к о г о м а с с и в а будет а в т о м а т и ч е с к и у в е л и ч и в а т ь с я . При с о з д а н и и массива с п о м о щ ь ю этого к о н с т р у к т о р а в о з м о ж н о п о я в л е н и е исклю¬ чения О н о будет с о з д а н о в том с л у ч а е , если п о п ы т а т ь с я задать о т р и ц а т е л ь н ы й р а з м е р с о з д а в а е м о г о м а с с и в а . И н а к о н е ц , т р е т и й к о н с т р у к т о р п р е д н а з н а ч е н для с о з д а н и я м а с с и в о в A r r a y L i s t на базе у ж е A r r a y L i s t myWords ArrayList
new new
Если п е р е д а т ь этому к о н с т р у к т о р у с с ы л к у , с о д е р ж а щ у ю пустое з н а ч е н и е n u l l , возникнет исключение П р о г р а м м а , и с х о д н ы й текст к о т о р о й п р е д с т а в л е н в л и с т и н г е 13.1, д е м о н с т р и р у е т п р о с т е й ш и й способ с о з д а н и я м а с с и в а класса A r r a y L i s t , д о б а в л е н и я в него н о в ы х э л е м е н т о в , а т а к ж е способ и з в л е ч е н и я э л е м е н т о в м а с с и в а . Листинг using using
13.1.
Файл
System;
namespace class
ArrayListDemoApp
static
void
A r r a y L i s t myWords
Глава 13. Контейнеры для хранения
args) new
407
О б р а т и т е в н и м а н и е , что для р а б о т ы с к о н т е й н е р а м и мы п о д к л ю ч и л и к нашей про¬ г р а м м е п р о с т р а н с т в о имен using В д а л ь н е й ш е м мы е щ е в е р н е м с я к и с х о д н о м у тексту этой п р о г р а м м ы .
Добавление элементов в массив Для д о б а в л е н и я э л е м е н т о в в м а с с и в A r r a y L i s t п р е д н а з н а ч е н м е т о д A d d . В к а ч е с т в е е д и н с т в е н н о г о п а р а м е т р а этот м е т о д п о л у ч а е т ссылку н а д о б а в л я е м ы й э л е м е н т . П о с л е д о б а в л е н и я метод A d d в о з в р а щ а е т и н д е к с н о в о г о э л е м е н т а в м а с с и в е . В п р о г р а м м е , и с х о д н ы й текст к о т о р о й был п р и в е д е н в л и с т и н г е 13.1, мы в о с п о л ь з о в а л и с ь этим м е т о д о м для н а ч а л ь н о й и н и ц и а л и з а ц и и м а с с и в а , д о б а в и в в него 4 эле¬ мента: C#
Так как и н д е к с ы
э л е м е н т о в , в о з в р а щ а е м ы е м е т о д о м A d d , нам были н е н у ж н ы ,
мы проигнорировали возвращаемое значение. О б р а т и т е в н и м а н и е , что при с о з д а н и и м а с с и в а A r r a y L i s t м ы н е у к а з ы в а е м тип о б ъ е к т о в , к о т о р ы е он будет с о д е р ж а т ь . Это очень в а ж н ы й м о м е н т
подобно другим
к о н т е й н е р а м , р а с с м о т р е н н ы м в этой г л а в е , м а с с и в A r r a y L i s t м о ж е т с о д е р ж а т ь лю¬ бые
п р о и з в о д н ы е о т класса o b j e c t . А так как о т этого к л а с с а о б р а з у ю т с я
все о б ъ е к т ы , т о это о з н а ч а е т , что м а с с и в A r r a y L i s t м о ж е т х р а н и т ь э л е м е н т ы л ю б о г о типа. З а м е т и м , что м а с с и в A r r a y L i s t м о ж е т быть создан в т а к о м р е ж и м е , когда для не¬ го д о п у с к а е т с я т о л ь к о о п е р а ц и я ч т е н и я , но не з а п и с и или и з м е н е н и я к о л и ч е с т в а эле¬ м е н т о в . При п о п ы т к е д о б а в и т ь м е т о д о м A d d н о в ы й э л е м е н т в м а с с и в , для к о т о р о г о допустима
только
операция
чтения,
возникает
исключение
NotSupportedEx-
ception.
408
А
Фролов, Г.
Фролов. Язык С# Самоучитель
Чтение элементов массива Элементы массива можно читать с помощью специальных курсоров (ука зателей или итераторов), а также более привычным с п о с о б о м , адресуясь к ним по ин дексу с помощью квадратных скобок.
Использование итераторов Для последовательного извлечения всех элементов массива можно создать в програм ме специальный итератор в виде объекта класса rator.
Этот итератор н е о б х о д и м о проинициализировать при
помощи метода G e t -
E n u m e r a t o r , вызванного для обрабатываемого контейнера: myEnumerator Здесь
в
переменной
myEnumerator
будет
храниться
итератор
для
массива
Для перемещения итератора в направлении от начала массива к его концу необхо¬ димо использовать метод M o v e N e x t .
При этом текущий элемент массива (т. е. тот,
на который указывает итератор) можно будет извлечь с помощью свойства C u r r e n t :
В результате работы этого цикла на консоль будет выведено полное с о д е р ж и м о е массива. Для установки итератора
I E n u m e r a t o r в исходное
тояние нужно использовать метод R e s e t .
Использование индексов Более привычный с п о с о б извлечения элементов массива A r r a y L i s t , не предпола¬ гающий применения курсоров, демонстрируется в программе, исходный текст которой представлен в листинге 13.2. 13.2.
Листинг using using
Файл
System;
namespace
static
void
A r r a y L i s t myWords
Глава
args) new
Контейнеры для хранения объектов
409
C#
i
i
i++)
Здесь п р и м е н я е т с я к л а с с и ч е с к о е и н д е к с и р о в а н и е массива при п о м о щ и к в а д р а т н ы х с к о б о к , п р и ч е м р а з м е р массива о п р е д е л я е т с я с п о м о щ ь ю с в о й с т в а C o u n t : i
0;
i
i++)
Изменение элементов массива Для и з м е н е н и я э л е м е н т о в м а с с и в а м о ж н о и с п о л ь з о в а т ь о б ы ч н ы е к в а д р а т н ы е с к о б к и . З а м е т и м , о д н а к о , что с л е д у ю щ и й ф р а г м е н т кода работать н е б у д е т : A r r a y L i s t myWords new A r r a y L i s t ( 4 ) "Hello"; C# myWords "World"; В р е з у л ь т а т е его р а б о т ы в о з н и к н е т и с к л ю ч е н и е RangeExcept i o n . П р е ж д е чем и з м е н я т ь с о д е р ж и м о е э л е м е н т о в м а с с и в а , и х н е о б х о д и м о д о б а в и т ь в массив методом Add. Т е х н и к а и з м е н е н и я д о б а в л е н н ы х ранее э л е м е н т о в м а с с и в а с и с п о л ь з о в а н и е м квад¬ р а т н ы х с к о б о к д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й вы н а й д е т е в л и с т и н г е 13.3. Файл using using
System;
namespace class
ItemDemolApp
static
void
ArrayList
args) myWords
new
Фролов, Г.
Язык С#. Самоучитель
(int i
0;
i
myWords
i+ + )
мир "языка
for(int
i
0;
i
i+ + )
Здесь мы в н а ч а л е м а с с и в и в ы в е л и его с о д е р ж и м о е на кон¬ с о л ь . Затем с о д е р ж и м о е к а ж д о й я ч е й к и м а с с и в а б ы л о и з м е н е н о , п о с л е чего п р о г р а м м а в н о в ь в ы в е л а массив на к о н с о л ь . Так как с в о й с т в о и т е р а т о р а C u r r e n t д о с т у п н о т о л ь к о д л я ч т е н и я , с его п о м о щ ь ю в ы н е с м о ж е т е и з м е н и т ь с о д е р ж и м о е я ч е е к массива. С л е д у ю щ а я п о п ы т к а д о б а в л е н и я к к а ж д о й т е к с т о в о й с т р о к е , х р а н я щ е й с я в м а с с и в е , с и м в о л а п о д ч е р к и в а н и я будет пре¬ с е ч е н а еще н а этапе к о м п и л я ц и и и с х о д н о г о т е к с т а п р о г р а м м ы : I E n u m e r a t o r myEnumerator
и текущий размер массива Н а и б о л е е серьезный н е д о с т а т о к о б ы ч н о г о м а с с и в а A r r a y — н е в о з м о ж н о с т ь измене¬ ния р а з м е р а м а с с и в а после его с о з д а н и я . Что же к а с а е т с я м а с с и в о в , с о з д а н н ы х на базе класса A r r a y L i s t , то их р а з м е р у в е л и ч и в а е т с я а в т о м а т и ч е с к и по мере д о б а в л е н и я в него н о в ы х о б ъ е к т о в . Для того чтобы у с т а н о в и т ь н а ч а л ь н ы й р а з м е р м а с с и в а A r r a y L i s t , в ы м о ж е т е и с пользовать свойство A r r a y L i s t Т е к у щ и й размер м а с с и в а д о с т у п е н ч е рез с в о й с т в о Count. Глава
для хранения
Когда в увеличения размеров массива значение дос тигает значения A r r a y L i s t размер массива автоматически увеличива ется. При этом автоматически увеличивается объем памяти, полученной у ОС для хра нения массива. Таким образом, значение A r r a y L i s t всегда больше зна чения Сказанное демонстрируется в программе, исходный текст которой показан в лис тинге 13.4. Листинг using using
13.4.
Файл
System;
namespace class
Capacity CapacityApp
static
void
args)
A r r a y L i s t myWords
new
C#
элементов:
емкость
массива
После запуска этой программы на консоль будет выведено следующее: Всего 4, емкость массива 16 Таким образом, емкость массива превышает текущее количество хранящихся в нем элементов.
Объединение массивов Помимо автоматического увеличения размера массив A r r a y L i s t предоставляет про¬ граммистам и другие возможности. В частности, программа может добавлять, встав лять или удалять как отдельные элементы массива, так и наборы таких элементов. На¬ помним, что обычный массив допускает работу только с одним элементом массива, причем программа может либо получить, либо изменить хранящееся там В листинге 13.5 мы показали способ слияния двух массивов при помощи метода AddRange.
А
Г. В. Фролов. Язык
Самоучитель
13.5.
Листинг
Файл
System; using namespace class
AddRangeApp
static
void
args)
A r r a y L i s t myWords
new
C#
ArrayList
new
ArrayListO;
элементов:
емкость
массива
myEnumerator
Массивы создаются обычным образом A r r a y L i s t myWords
с помощью конструкторов и метода Add:
new
C#
A r r a y L i s t myWordsl
Глава
new
для хранения
413
Далее мы д о б а в л я е м в п е р в ы й массив все э л е м е н т ы из второго м а с с и в а , как это по¬ казано н и ж е :
П о д о б н ы м о б р а з о м м о ж н о о б ъ е д и н я т ь не только м а с с и в ы , но и д р у г и е к о н т е й н е р ы .
Удаление элементов из массива П о с л е с о з д а н и я о б ы ч н о г о м а с с и в а A r r a y в ы н е м о ж е т е у д а л и т ь и з него существую¬ щие э л е м е н т ы . Е д и н с т в е н н ы й с п о с о б в ы п о л н е н и я такой о п е р а ц и и з а к л ю ч а е т с я в соз¬ дании нового м а с с и в а и п о с л е д у ю щ е м п е р е п и с ы в а н и и в него всех э л е м е н т о в , кроме у д а л я е м ы х . С т о и т ли г о в о р и т ь , что этот способ д а л е к о не с о в е р ш е н е н . Что же касается м а с с и в о в A r r a y L i s t , то для них вы м о ж е т е и с п о л ь з о в а т ь не¬ сколько методов удаления элементов: • •
метод C l e a r у д а л я е т все э л е м е н т ы и з массива, в ы п о л н я я его п о л н у ю о ч и с т к у ; с п о м о щ ь ю м е т о д а R e m o v e п р о г р а м м а м о ж е т у д а л и т ь из м а с с и в а л ю б о й з а д а н н ы й элемент; метод RemoveAt п р е д н а з н а ч е н для у д а л е н и я э л е м е н т а , з а д а н н о г о с в о и м и н д е к с о м ; метод
п р и г о д и т с я в том с л у ч а е , когда н у ж н о удалить сразу не¬
с к о л ь к о э л е м е н т о в массива, р а с п о л о ж е н н ы х р я д о м . И с п о л ь з о в а н и е всех п е р е ч и с л е н н ы х в ы ш е м е т о д о в д е м о н с т р и р у е т с я в п р о г р а м м е , исходный текст которой приведен в листинге Листинг using using
13.6.
Файл System;
namespace
static
Remove
void
A r r a y L i s t myWords
args) new
емкость myWords
массива
myWords myEnumerator
А В.
Г. В Фролов. Язык
Самоучитель
элементов:
емкость
массива
элементов:
емкость
массива
элементов:
емкость
массива
Глава 13. Контейнеры для хранения объектов
В н а ч а л е н а ш а п р о г р а м м а создает и с х о д н ы й м а с с и в , д о б а в л я е т в м е н т ы , а затем о т о б р а ж а е т на к о н с о л и р а з м е р м а с с и в а и его myWords
myWords
new
C o u n t , myWords
элементов: Capacity)
System.
емкость
массива
myEnumerator
Далее «C#
новые эле
наша
программа
ищет
в
массиве
первый
элемент,
содержащий
строку
и у д а л я е т его при п о м о щ и м е т о д а R e m o v e :
Далее
содержимое
измененного
массива отображается
на консоли
при
помощи
итератора Console
("Всего
System. C o l l e c t i o n s
элементов:
(0),
емкость
массива
IEnumerator myEnumeratorl
Обратите внимание, что здесь нам пришлось создать новый итератор m y E n u m e r a t o r l , так как р а з м е р ы м а с с и в а были и з м е н е н ы . В р е з у л ь т а т е итератор с о з д а н н ы й ранее для э т о г о м а с с и в а , стал б е с п о л е з н ы м . В дальней¬ ш е м при к а ж д о м и з м е н е н и и м а с с и в а наша п р о г р а м м а создает для его о т о б р а ж е н и я но¬ вый и т е р а т о р . Н а с л е д у ю щ е м шаге п р о г р а м м а у д а л я е т все э л е м е н т ы и з м а с с и в а м е т о д о м C l e a r , а потом заполняет з а н о в о при п о м о щ и метода A d d :
А В
Г. В Фролов. Язык
Самоучитель
Далее из восстановленного таким способом массива удаляется элемент с индек сом 1 (т. е. строка « С #
В качестве единственного параметра этому методу нужно передать индекс удаляе¬ мого элемента. Содержимое массива вновь отображается на консоли при помощи итератора И наконец, программа удаляет из массива два элемента, начиная с элемента, имеющего индекс 1, для чего она вызывает метод RemoveRange:
Через первый параметр методу R e m o v e R a n g e нужно передать индекс первого удаляемого элемента, а в качестве второго — количество удаляемых элементов.
Запрет записи данных в массив При необходимости с помощью программа может создать массив A r r a y L i s t дос тупным только для чтения. Эта возможность отсутствует в обычных массивах, реали¬ зованных как с применением класса A r r a y и языка программирования С#, так и с применением других языков программирования. Создание массива, доступного только для чтения, демонстрируется в программе, исходный текст которой представлен в листинге 13.7. Листинг using using
13.7.
Файл
System;
namespace class
Readonly ReadOnlyApp
static
void
args)
A r r a y L i s t myWords
new
C#
ArrayList
"Исходный
м а с с и в myWords
доступен
только
для
else "Исходный м а с с и в myWords д о с т у п е н для чтения и Глава 13. Язык
Самоучитель
для
объектов
417
"Массив
доступен
только
"Массив m y R e a d O n l y W o r d s
доступен
для
для
else
catch
(Exception
чтения
и
ex)
В н а ч а л е мы с о з д а е м о б ы ч н ы й м а с с и в , д о с т у п н ы й для ч т е н и я и з а п и с и : A r r a y L i s t myWords
new A r r a y L i s t
Д а л е е этот м а с с и в и н и ц и а л и з и р у е т с я о б ы ч н ы м о б р а з о м с п о м о щ ь ю м е т о д а A d d : C#
К с о ж а л е н и ю , мы не м о ж е м сделать с о з д а н н ы й т а к и м с п о с о б о м м а с с и в д о с т у п н ы м т о л ь к о для ч т е н и я , о д н а к о есть д р у г а я в о з м о ж н о с т ь . М ы м о ж е м с о з д а т ь к о п и ю н а ш е г о массива, д о с т у п н у ю т о л ь к о для ч т е н и я . Эта о п е р а ц и я в ы п о л н я е т с я при п о м о щ и м е т о д а ArrayList
myReadOnlyWords
Readonly
В р е з у л ь т а т е будет создан м а с с и в m y R e a d O n l y W o r d s , д о с т у п н ы й т о л ь к о для чте¬ ния. П р о г р а м м а не с м о ж е т д о б а в л я т ь в него н о в ы е э л е м е н т ы , а т а к ж е и з м е н я т ь содер¬ жимое существующих элементов. В классе A r r a y L i s t о п р е д е л е н о с в о й с т в о I s R e a d O n l y , п о з в о л я ю щ е е опреде¬ л и т ь , в о з м о ж н о л и и з м е н е н и е с о д е р ж и м о г о м а с с и в а , или этот массив д о с т у п е н т о л ь к о для з а п и с и . В о т как мы п р о в е р я е м это с в о й с т в о д л я и с х о д н о г о м а с с и в а :
массив
myWords
доступен
только
для
else "Исходный м а с с и в myWords
д о с т у п е н для
чтения и
Фролов, Г. В. Фролов. Язык С#. Самоучитель
А н а л о г и ч н а я о п е р а ц и я в ы п о л н я е т с я и для н о в о г о массива, д о с т у п н о г о т о л ь к о для
myReadOnlyWords
доступен
только
"Массив m y R e a d O n l y W o r d s
доступен
для
для
else чтения
и
Д а л е е наша п р о г р а м м а в ы п о л н я е т п о п ы т к у д о б а в л е н и я н о в о г о э л е м е н т а в м а с с и в , д о с т у п н ы й т о л ь к о д л я ч т е н и я . Так как эта о п е р а ц и я о б я з а т е л ь н о в ы з о в е т и с к л ю ч е н и е , мы предусмотрели соответствующий обработчик: try
catch
(Exception
ex) исключение:
В о т что п о я в и т с я на к о н с о л и после запуска п р о г р а м м ы : Исходный м а с с и в myWords д о с т у п е н д л я ч т е н и я и з а п и с и Массив m y R e a d O n l y W o r d s д о с т у п е н т о л ь к о д л я ч т е н и я Возникло исключение: Collection at at
args) 30
in
is
c#
Как в и д и т е , п о п ы т к а и з м е н е н и я м а с с и в а , д о с т у п н о г о т о л ь к о для ч т е н и я , п р и в е л а к возникновению исключения
NotSupportedException.
Ограничение размера массива Если вам н у ж н ы все с в о й с т в а м а с с и в а A r r a y L i s t , к р о м е а в т о м а т и ч е с к о г о у в е л и ч е ния его р а з м е р а , то такая в о з м о ж н о с т ь т а к ж е п р е д у с м о т р е н а . П р и н е о б х о д и м о с т и п р о г р а м м а м о ж е т з а ф и к с и р о в а т ь размер м а с с и в а ArrayList, п р е д о т в р а т и в его а в т о м а т и ч е с к и й рост ( л и с т и н г 13.8). 13.8.
Листинг using using
ch
cs
System;
namespace class
Глава
Файл
FixedSize FixedSizeApp
Контейнеры
хранения объектов
419
static
void
args)
A r r a y L i s t myWords
new
C#
ArrayList
myFixedSizeWords
"Исходный
массив
myWords
имеет
фиксированный
else "Допускается
"Массив
изменение
myReadOnlyWords
размеров
имеет
массива
фиксированный
else "Допускается
изменение
размеров
массива
try
catch
(Exception
ex) исключение:
Здесь мы в н а ч а л е с о з д а е м и и н и ц и а л и з и р у е м массив m y W o r d s , а затем создаем н а его базе м а с с и в m y F i x e d S i z e W o r d s , и м е ю щ и й ф и к с и р о в а н н ы й р а з м е р : A r r a y L i s t myWords
ArrayList
new
myFixedSizeWords
Такой массив создастся при помощи метода F i x e d S i z e .
420
А В.
Г. В Фролов. Язык С# Самоучитель
Программа может определить, фиксирован ли размер массива, проверив свойство IsFixedSize:
"Исходный
массив
myWords
имеет
фиксированный
else "Допускается
изменение
После создания массива программа пытается добавить в
размеров
массива с фиксированным размером наша
новый элемент:
try
catch
(Exception
ex) исключение:
Эта п о п ы т к а н е и з б е ж н о п р и в е д е т к в о з н и к н о в е н и ю и с к л ю ч е н и я S u p p o r t e d E x c e p t i o n , о чем м о ж н о с у д и т ь п о и н ф о р м а ц и и , о т о б р а ж а е м о й н а ш е й п р о г р а м м о й на к о н с о л и : Д о п у с к а е т с я и з м е н е н и е р а з м е р о в м а с с и в а myWords Массив m y R e a d O n l y W o r d s и м е е т ф и к с и р о в а н н ы й р а з м е р Возникло исключение: C o l l e c t i o n was of a fixed size. at at args) in c# 30
Сортировка М а с с и в ы класса A r r a y L i s t , так ж е как и м а с с и в ы класса A r r a y , у д о б н о сортиро¬ вать. С п е ц и а л ь н о д л я этого п р е д у с м о т р е н о н е с к о л ь к о п е р е г р у ж е н н ы х в а р и а н т о в мето¬ да Sort. В простейшем случае сортировка массива выполняется с п о м о щ ь ю метода S o r t , н е и м е ю щ е г о п а р а м е т р о в . П р и м е р п р о г р а м м ы , с о р т и р у ю щ е й массив п о д о б н ы м обра¬ з о м , п р и в е д е н в л и с т и н г е 13.9. Листинг
13.9.
using using
System;
namespace class
Sort SortApp
static Глава
Файл
void
args) хранения объектов
421
A r r a y L i s t myWords
new
myEnumerator
System. C o l l e c t i o n s
IEnumerator myEnumeratorl
Получив управление, наша программа создает массив, записывая в него слова из вестной фразы, п о м о г а ю щ е й запомнить 7 цветов радуги: A r r a y L i s t myWords
new
Далее мы отображаем с о д е р ж и м о е массива с помощью итератора и сортируем мас сив, вызывая м е т о д S o r t без параметров:
После сортировки с о д е р ж и м о е массива отображается вновь: каждый о х о т н и к ж е л а е т з н а т ь г д е г д е ж е л а е т з н а т ь каждый о х о т н и к 422
сидит сидит А
фазан
Фролов, Г.
Фролов. Язык
Как в и д и т е , с о р т и р о в к а т е к с т о в ы х строк с с и м в о л а м и к и р и л л и ц ы была в ы п о л н е н а правильно. К а к и м образом о с у щ е с т в л я е т с я с о р т и р о в к а м а с с и в а ? Для этого п р и м е н я е т с я а л г о р и т м б ы с т р о й с о р т и р о в к и Q u i c k s o r t .
Сравнение эле
ментов выполняется с использованием интерфейса I C o m p a r a b l e , в рамках которого о п р е д е л е н метод М е т о д у C o m p a r e T o п е р е д а е т с я один п а р а м е т р
с с ы л к а на о б ъ е к т , с р а в н и в а е м ы й
с т е к у щ и м о б ъ е к т о м . В з а в и с и м о с т и от р е з у л ь т а т а с р а в н е н и я м е т о д C o m p a r e T o воз в р а щ а е т о т р и ц а т е л ь н о е , н у л е в о е или п о л о ж и т е л ь н о е з н а ч е н и е . О т р и ц а т е л ь н о е значе¬ ние в о з в р а щ а е т с я , если т е к у щ и й о б ъ е к т м е н ь ш е , чем о б ъ е к т , ссылка на к о т о р ы й пере¬ д а е т с я методу C o m p a r e T o через п а р а м е т р , а п о л о ж и т е л ь н о е — если б о л ь ш е . В том с л у ч а е , когда объекты р а в н ы , м е т о д C o m p a r e T o в о з в р а щ а е т н у л е в о е з н а ч е н и е . Когда в ы с о р т и р у е т е м а с с и в , с о д е р ж а щ и й т о л ь к о в с т р о е н н ы е т и п ы д а н н ы х ( т а к и е , как числа и т е к с т о в ы е с т р о к и ) , м о ж н о и с п о л ь з о в а т ь в с т р о е н н у ю в эти т и п ы д а н н ы х реализацию интерфейса Если ж е с о р т и р у е т с я м а с с и в , с о д е р ж а щ и й с о з д а н н ы е вами о б ъ е к т ы , н е о б х о д и м о реализовать интерфейс I C o m p a r a b l e .
Ссылка на реализованный интерфейс
p a r a b l e д о л ж н а быть п е р е д а н а методу S o r t в качестве параметра. М о ж н о о т с о р т и р о в а т ь не весь м а с с и в , а т о л ь к о его часть. В этом случае необходи¬ мо и с п о л ь з о в а т ь третий вариант метода S o r t , п р и н и м а ю щ и й 3 параметра. В к а ч е с т в е п е р в о г о п а р а м е т р а этому м е т о д у н е о б х о д и м о п е р е д а т ь и н д е к с первого э л е м е н т а мас¬ сива, с к о т о р о г о н а ч и н а е т с я с о р т и р у е м ы й блок, а в качестве сортируемых элементов.
Третий
параметр должен
с о д е р ж а т ь ссылку н а и н т е р ф е й с
I C o m p a r a b l e л и б о н а з н а ч е н и е n u l l , если будет и с п о л ь з о в а н а р е а л и з а ц и я интер¬ фейса I C o m p a r a b l e о б ъ е к т о в , с о д е р ж а щ и х с я в м а с с и в е .
Обратное расположение элементов массива С п о м о щ ь ю метода R e v e r s e п р о г р а м м а м о ж е т и з м е н и т ь р а с п о л о ж е н и е э л е м е н т о в м а с с и в а на о б р а т н о е . Это д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и веден в л и с т и н г е 13.10. Листинг using using
13.10.
Файл
System;
namespace class
Reverse ReverseApp
static
void
A r r a y L i s t myWords —
Глава 13. Контейнеры для хранения
args) ArrayList
423
myWords
Add IEnumerator myEnumerator
IEnumerator myEnumeratorl
От предыдущей программы, т о л ь к о одной с т р о к о й :
выполняющей
сортировку
массива,
она о т л и ч а е т с я
В н а ч а л е п р о г р а м м а в ы в о д и т на к о н с о л ь с о д е р ж и м о е и с х о д н о г о м а с с и в а , а затем м а с с и в а , э л е м е н т ы в к о т о р о м были п е р е с т а в л е н ы м е т о д о м R e v e r s e . В о т результат работы нашей программы: каждый о х о т н и к ж е л а е т фазан сидит где знать
знать где сидит фазан ж е л а е т о х о т н и к каждый
При н е о б х о д и м о с т и м о ж н о о б р а б о т а т ь м е т о д о м R e v e r s e т о л ь к о часть массива. Для этого н у ж н о и с п о л ь з о в а т ь п е р е г р у ж е н н у ю в е р с и ю этого м е т о д а , п р и н и м а ю щ у ю два п а р а м е т р а . п а р а м е т р задает и н д е к с п е р в о г о э л е м е н т а о б р а б а т ы в а е м о г о блока, а в т о р о й — э л е м е н т о в в блоке.
Поиск в массиве П о д о б н о м а с с и в а м A r r a y , р а с с м о т р е н н ы м нами в гл. 7 , м а с с и в ы A r r a y L i s t д о п у с кают в ы п о л н е н и е б и н а р н о г о поиска э л е м е н т о в . Для этого в классе A r r a y L i s t опре¬ делен
424
метод
B i n a r y S e a r c h (нестатический).
В
качестве
п а р а м е т р а этому м е т о д у
А В. Фролов, Г. В Фролов. Язык
Самоучитель
нужно передать и с к о м ы й э л е м е н т . При у с п е х е метод в о з в р а т и т и н д е к с э л е м е н т а , а в т о м с л у ч а е , если э л е м е н т не н а й д е н , — о т р и ц а т е л ь н о е И с х о д н ы й текст п р о г р а м м ы , д е м о н с т р и р у ю щ и й метода B i n a r y S e a r c h для п о и с к а т е к с т о в о й с т р о к и в массиве класса A r r a y L i s t , п р и в е д е н в л и с тинге 1 3 . 1 1 . 13.11.
Листинг using using
System;
namespace class
Search SearchApp
static
void
ArrayList
string int
args) myWords
new
searchString
"фазан";
index
if
0 не
else строки
равен
index
В п р о г р а м м е о б ъ я в л е н и п р о и н и ц и а л и з и р о в а н с т а т и ч е с к и массив т е к с т о в ы х строк myWords. М е т о д M a i n ищет в массиве слово string searchString int index
вызывая для этого метод B i n a r y S e a r c h ;
"фазан";
М е т о д B i n a r y S e a r c h п о л у ч а е т в качестве п а р а м е т р а ссылку н а и с к о м у ю строку. Если искомая с т р о к а н е н а й д е н а , м е т о д B i n a r y S e a r c h в о з в р а щ а е т о т р и ц а т е л ь н о е з н а ч е н и е . В случае у с п е х а в о з в р а щ а е т с я индекс н а й д е н н о й с т р о к и . Н а ш а п р о г р а м м а о т о б р а ж а е т на консоли и с к о м у ю строку и ее индекс. Глава 13. Контейнеры для хранения объектов
425
Работа в многопоточном режиме Для мах, Это ным ции
того чтобы использовать массив класса A r r a y L i s t в многопоточных програм¬ н е о б х о д и м о создать не его базе так называемый синхронизированный массив. можно сделать статическим методом предусмотрен для этой цели в классе Существует также возможность синхрониза многопоточных приложений при помощи свойства S y n c R o o t .
Применение метода Synchronized Один из самых простых с п о с о б о в работы с массивами A r r a y L i s t в многопоточных приложениях предполагает создание синхронизированных массивов методом S y n c h ¬ ronized. В листинге 13.12 мы привели исходный текст многопоточной программы, исполь з у ю щ е й этот м е т о д на практике. Листинг using using using
13.12.
Файл
System;
namespace class
Synchronized SynchronizedApp
static
ArrayList
public
static
void поток myEnumerator
static
void
ArrayList
426
args) myWords
new A r r a y L i s t
А В Фролов, Г. В. Фролов. Язык
Самоучитель
ThreadStart Thread thr
myThreadDelegate new
new
ThreadStart
потока
Метод M a i n , работающий в рамках основного потока нашей программы, создает массив m y W o r d s класса A r r a y L i s t и затем и н и ц и а л и з и р у е т его: A r r a y L i s t myWords
new
Д а л е е наша п р о г р а м м а создает с и н х р о н и з и р о в а н н ы й м а с с и в , с т а т и ч е с к и й м е т о д Synchronized:
в ы з ы в а я для этого
myWordsSynchro В качестве единственного параметра методу передается ссылка на исходный массив. После этого программа создает новый поток для метода M y T h r e a d и запускает его: ThreadStart Thread thr
myThreadDelegate new new потока
Эта п р о ц е д у р а была нами п о д р о б н о о п и с а н а в гл. 10 этой книги. М е т о д M y T h r e a d , р а б о т а ю щ и й в р а м к а х о т д е л ь н о г о п о т о к а , о т о б р а ж а е т содержи¬ мое м а с с и в а на к о н с о л и при п о м о щ и итератора: public
static
void поток IEnumerator myEnumerator
Глава 13.
427
Свойство Для того чтобы определить, является ли массив синхронизированным или нет, можно воспользоваться свойством I s S y n c h r o n i z e d , доступным только для чтения.
Свойство SyncRoot Вы
обеспечить синхронизацию, н е о б х о д и м у ю для использования массивов в
многопоточных
приложениях,
и
без
создания
синхронизированного
массива. Для этого можно воспользоваться свойством S y n c R o o t и с х о д н о г о массива, возвращающего объект синхронизации. Поясним
использование свойства
SyncRoot
на примере программы,
исходный
текст которой представлен в листинге Листинг using
13.13.
Файл
System;
using using namespace class
SyncRoot SyncRootApp
static
ArrayList
public
static
myWords;
void поток
lock myEnumerator
static
void
myWords
428
1
args)
new
В. Фролов, Г. В. Фролов. Язык С#. Самоучитель
ThreadStart Thread thr
myThreadDelegate new
new
потока
М е т о д M a i n этой программы создает массив m y W o r d s , инициализирует его, а затем запускает метод M y T h r e a d в отдельном потоке. В о т исходный текст этого метода: public
static
void поток
lock myEnumerator
Как в и д и т е , перед н а ч а л о м цикла и т е р а ц и и п о массиву m y W o r d s метод б л о к и р у е т объект myWords с п о м о щ ь ю к л ю ч е в о г о слова l o c k . Это н е о б х о д и м о , так как п р о ц е д у р а и т е р а ц и и по массиву не м о ж е т в ы п о л н я т ь с я в м н о г о п о т о ч н о м ре¬ ж и м е без с и н х р о н и з а ц и и . В о с т а л ь н о м н а ш а п р о г р а м м а п о х о ж а на п р е д ы д у щ у ю , ис¬ х о д н ы й т е к с т к о т о р о й был п р е д с т а в л е н в л и с т и н г е 13.12.
Недостатки массивов ArrayList Разумеется, массивам A r r a y L i s t присущи и некоторые недостатки по сравнению с м а с с и в а м и A r r a y . П о э т о м у вы д о л ж н ы и с п о л ь з о в а т ь м а с с и в ы такого типа, к о т о р ы й в н а и л у ч ш е й степени с о о т в е т с т в у е т л о г и к е р а б о т ы в а ш е й п р о г р а м м ы . Вот основные недостатки массивов A r r a y L i s t по сравнению с массивами A r r a y : •
массивы A r r a y L i s t в о т л и ч и е о т м а с с и в о в A r r a y н е м о г у т быть м н о г о м е р н ы м и ;
•
массивы A r r a y L i s t и при чтении д а н н ы х . Глава 13. Контейнеры для хранения
м е д л е н н е е м а с с и в о в A r r a y как при з а п и с и , так
429
Словарь Hashtable П о м и м о м а с с и в о в с д и н а м и ч е с к и и з м е н я е м ы м р а з м е р о м при с о з д а н и и п р о г р а м м часто б ы в а ю т н у ж н ы т а к и е с т р у к т у р ы д а н н ы х , как с л о в а р и . В о т л и ч и е от м а с с и в о в , где каж¬ д ы й х р а н я щ и й с я э л е м е н т и д е н т и ф и ц и р у е т с я своим и н д е к с о м , в с л о в а р я х для иденти¬ фикации элементов используются специальные объекты
ключи.
В к а ч е с т в е к л ю ч е й могут в ы с т у п а т ь , н а п р и м е р , т е к с т о в ы е с т р о к и . Тогда к а ж д о й такой строке м о ж н о будет п о с т а в и т ь в с о о т в е т с т в и е к а к о й - л и б о объект. П р о с т е й ш и й п р и м е р — т е л е ф о н н ы й с п р а в о ч н и к , где к а ж д о й ф а м и л и и с т а в и т с я в с о о т в е т с т в и е тот или иной н о м е р т е л е ф о н а . С
использованием
можно
создать
на
библиотеки
основе
к л а с с о в Microsoft
хеш-таблиц
класса
F r a m e w o r k такой
Hashtable.
Рассмотрим
словарь
основные
приемы и с п о л ь з о в а н и я этого класса.
Создание словаря Для с о з д а н и я с л о в а р е й ( х е ш - т а б л и ц ) в классе H a s h t a b l e п р е д у с м о т р е н о н е с к о л ь к о перегруженных
конструкторов.
К о н с т р у к т о р без п а р а м е т р о в К о н с т р у к т о р без п а р а м е т р о в п о з в о л я е т создать п у с т о й с л о в а р ь с п а р а м е т р а м и , приня¬ т ы м и п о у м о л ч а н и ю . П о л ь з о в а т ь с я и м очень п р о с т о : Hashtable
myPhones
new
Исходный текст программы, в которой словарь H a s h t a b l e создается с помощью т а к о г о к о н с т р у к т о р а , п р е д с т а в л е н в л и с т и н г е 13.14. Листинг using using
13.14.
Файл
System;
namespace class
HashTable HashTableApp
static
void
Hashtable
args) myPhones
new
Hashtable
myEnumerator
430
А В Фролов, Г В. Фролов. Язык С# Самоучитель
П о с л е з а п у с к а п р о г р а м м а создает словарь и д о б а в л я е т в него 3 з а п и с и . Д а л е е эти записи о т о б р а ж а ю т с я на к о н с о л и : Сидоров: Иванов: Петров:
8-999-323-33-44 322-44-22 8-999-323-33-33
О п е р а ц и и д о б а в л е н и я э л е м е н т о в и п р о с м о т р а с о д е р ж и м о г о , в ы п о л н я е м ы е в этой п р о г р а м м е , будут р а с с м о т р е н ы позже в этом р а з д е л е .
Копирование словаря Если п е р е д а т ь к о н с т р у к т о р у класса H a s h t a b l e ссылку н а у ж е и м е ю щ и й с я с л о в а р ь , то все э л е м е н т ы э т о г о с л о в а р я будут с к о п и р о в а н ы в н о в ы й с л о в а р ь . Это д е м о н с т р и р у ¬ ется в п р о г р а м м е , и с х о д н ы й т е к с т которой вы н а й д е т е в л и с т и н г е 13.15. Листинг
13.15.
using using
Файл
System;
namespace class
HashTablel HashTablelApp
static
void
Hashtable
args) new
myEnumerator
Hashtable Глава
Контейнеры для хранения объектов
new
431
myEnumeratorl
Здесь мы в н а ч а л е создаем с л о в а р ь m y P h o n e s и д о б а в л я е м в него 3 з а п и с и : Hashtable myPhones
new
Hashtable
Далее с о д е р ж и м о е с л о в а р я m y P h o n e s
к о п и р у е т с я в о вновь с о з д а в а е м ы й словарь
myPhonesCopy: H a s h t a b l e myPhonesCopy
new
Hashtable
П р о г р а м м а о т о б р а ж а е т на к о н с о л и с о д е р ж и м о е и с х о д н о г о с л о в а р я и его КОПИИ.
Указание начальной емкости словаря Для у в е л и ч е н и я э ф ф е к т и в н о с т и р а б о т ы со с л о в а р е м вы м о ж е т е у к а з а т ь , с к о л ь к о при¬ м е р н о э л е м е н т о в в нем будет х р а н и т ь с я (если, к о н е ч н о , это к о л и ч е с т в о вам известно). Для у к а з а н и я н а ч а л ь н о й е м к о с т и с л о в а р я и с п о л ь з у й т е к о н с т р у к т о р класса H a s h t a b l e с о д н и м п а р а м е т р о м , как это п о к а з а н о н и ж е : Hashtable
myPhones
new
Н а ч а л ь н а я емкость д о л ж н а задаваться п о л о ж и т е л ь н ы м ч и с л о м , в п р о т и в н о м случае возникнет исключение
RangeException
Другие В
конструкторы
классе H a s h t a b l e имеются и д р у г и е к о н с т р у к т о р ы , к о т о р ы е п о д р о б н о о п и с а н ы
в д о к у м е н т а ц и и на б и б л и о т е к у к л а с с о в Microsoft Среди них
Framework.
конструктор, позволяющий при создании нового словаря скопировать в
него с о д е р ж и м о е с у щ е с т в у ю щ е г о словаря и при этом дополнительно задать так называе¬ мый фактор -загрузки (load factor).
Фактор загрузки задается как отношение количества
и м е ю щ и х с я элементов к общей емкости словаря. Чем меньше фактор загрузки, тем быст¬ рее выполняется поиск элементов в словаре и тем больше требуется памяти для хранения словаря. По у м о л ч а н и ю фактор загрузки равен 1.0, что обеспечивает наилучший компро¬ мисс между с к о р о с т ь ю работы словаря и занимаемой этим словарем памятью.
432
А В Фролов, Г. В. Фролов. Язык
Самоучитель
Другие конструкторы обеспечивают создание словарей, имеющих нестандартный а л г о р и т м в ы ч и с л е н и я х е ш - к о д о в э л е м е н т о в , словарей с заданной е м к о с т ь ю и факто¬ ром з а г р у з к и , с л о в а р е й , д о п у с к а ю щ и х запись с о д е р ж и м о г о в п о т о к в ы в о д а (об этом мы п о г о в о р и м п о з ж е ) , а т а к ж е н е с т а н д а р т н ы е а л г о р и т м ы с р а в н е н и я э л е м е н т о в , хра¬ н я щ и х с я в словаре.
Добавление новых элементов Для д о б а в л е н и я в словарь н о в ы х э л е м е н т о в вы м о ж е т е в о с п о л ь з о в а т ь с я м е т о д о м A d d : H a s h t a b l e myPhones
new "322-44-22")
В качестве п е р в о г о п а р а м е т р а э т о м у м е т о д у н у ж н о п е р е д а т ь к л ю ч , а в к а ч е с т в е в т о р о г о — з н а ч е н и е , с в я з а н н о е с этим к л ю ч о м . В н а ш е м п р и м е р е роль к л ю ч а играет ф а м и л и я , а роль значения — н о м е р т е л е ф о н а .
Чтение содержимого словаря П р о г р а м м а м о ж е т получить список всего с о д е р ж и м о г о с л о в а р я при п о м о щ и и т е р а т о р а класса П р и м е р ы и с п о л ь з о в а н и я этого и т е р а т о р а есть в л и с т и н г а х 13.14 и 13.15, приведен¬ ных нами р а н е е . В о т с о о т в е т с т в у ю щ и й ф р а г м е н т кода: I D i c t i o n a r y E n u m e r a t o r myEnumerator
Здесь м ы вначале создаем и т е р а т о р , в ы з ы в а я для этого м е т о д G e t E n u m e r a t o r . Эта о п е р а ц и я
аналогична операции
получения
итератора массива A r r a y L i s t ,
рас¬
с м о т р е н н о г о в н а ч а л е этой г л а в ы . Для п о л у ч е н и я пар к л ю ч - з н а ч е н и е мы создали ц и к л , в к о т о р о м в ы п о л н я е т с я после¬ д о в а т е л ь н о е п е р е м е щ е н и е и т е р а т о р а с п о м о щ ь ю метода M o v e N e x t . П р и н е о б х о д и м о ¬ сти вы м о ж е т е снова у с т а н о в и т ь и т е р а т о р на начало с л о в а р я , в о с п о л ь з о в а в ш и с ь мето¬ дом
Reset. извлекается с п о м о щ ь ю свойства итератора с именем Key,
а
—
с п о м о щ ь ю свойства и т е р а т о р а с и м е н е м V a l u e .
Поиск по ключу П о с л е д о б а в л е н и я всех э л е м е н т о в в словарь ваша п р о г р а м м а м о ж е т и з в л е к а т ь н у ж н ы е э л е м е н т ы при п о м о щ и с в о й с т в а I t e m . Это с в о й с т в о д о с т у п н о через к в а д р а т н ы е скоб
ки Глава
Контейнеры для хранения объектов
433
Листинг
13.16.
using using
Файл
System;
namespace class
HashTable2App
static
void
args)
H a s h t a b l e myPhones
new емкость:
В этой программе мы создали словарь с фамилиями и телефонами, а потом извлек ли телефон Иванова, указав эту фамилию в качестве Иванова:
Заметим, что скобки можно использовать не только для извлечения элементов по ключу, но и для изменения значения элемента с данным ключом.
Предварительная проверка содержимого словаря При неудачной попытке извлечения из словаря элемента с несуществующим ключом будет получена ссылка n u l l . Чтобы избежать возникновения исключения в этой си туации, программа может предварительно проверить наличие элемента в словаре. Этот прием демонстрируется в программе, исходный текст которой приведен в листинге (Здесь и далее телефоны приведены только для примера, не пытай тесь Листинг using using
13.17.
Файл
System;
namespace class
HashTable3 HashTable3App
static
void
args) А В.
Г. В Фролов. Язык
Самоучитель
Hashtable
myPhones
new емкость:
Иванова: else Иванова
не
Если словарь m y P h o n e s с о д е р ж и т элемент с к л ю ч о м «Иванов», то программа и з влекает соответствующее значение (номер телефона) и отображает его на консоли: Иванова: else Иванова
не
В противном случае на консоль выводится с о о б щ е н и е о т о м , что т е л е ф о н Иванова не найден. Д л я проверки с о д е р ж и м о г о словаря вы также м о ж е т е использовать свойства ContainsKey и ContainsValue Первое из них позволяет проверить, имеется ли в словаре элемент с заданным к л ю ч о м , а второй имеется ли в словаре э л е м е н т с за¬ данным значением. М е т о д C o n t a i n s K e y аналогичен только что р а с с м о т р е н н о м у м е т о д у C o n t a i n s Использование
этого
метода,
а также
метода C o n t a i n s V a l u e демонстрируется
в программе, и с х о д н ы й текст которой приведен в листинге Листинг
13.18.
using using
Файл
System;
namespace class
HashTable4App
static
void
args)
H a s h t a b l e myPhones
Глава
new
для хранения объектов
435
i
else Иванова
не
есть
в
else 322-44-22
в
словаре
не
Здесь после с о з д а н и я с л о в а р я и д о б а в л е н и я в него всех э л е м е н т о в мы вначале про¬ в е р я е м , если ли там т е л е ф о н И в а н о в а : Иванова: else Иванова
не
Если этот т е л е ф о н и м е е т с я в с л о в а р е , то мы о т о б р а ж а е м его на к о н с о л и . В против¬ ном случае в ы в о д и т с я с о о б щ е н и е о т о м , что т е л е ф о н не н а й д е н . Далее с п о м о щ ь ю м е т о д а C o n t a i n s V a l u e мы п р о в е р я е м , есть ли в с л о в а р е за¬ пись, содержащая телефон 322-44-22: 322-44-22
есть
322-44-22
в
в
else словаре
не
Удаление элементов из словаря Чтобы полностью очистить словарь H a s h t a b l e , вы можете использовать C l e a r . Для у д а л е н и я э л е м е н т а с з а д а н н ы м к л ю ч о м в о с п о л ь з у й т е с ь м е т о д о м R e m o v e . К л ю ч п е р е д а в а е м о г о э л е м е н т а п е р е д а е т с я э т о м у методу в качестве е д и н с т в е н н о г о па¬ раметра. Если в с л о в а р е имеется э л е м е н т с з а д а н н ы м к л ю ч о м , то он будет у д а л е н м е т о д о м Если же т а к о г о э л е м е н т а в словаре нет, то с о д е р ж и м о е с л о в а р я о с т а н е т с я изменным. И с п о л ь з о в а н и е м е т о д а R e m o v e д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст ко¬ торой п р е д с т а в л е н в л и с т и н г е 13.19.
436
А.
Г.
Фролов. Язык С#. Самоучитель
Листинг
13.19.
using using
Файл
System;
namespace class
HashtableRemoveApp
static
void
args)
Hashtable
myPhones
new
myEnumerator
myEnumeratorl
ми
Получив наша программа заполняет словарь фамилиями и т е л е ф о н а после чего с о д е р ж и м о е словаря отображается на
H a s h t a b l e myPhones myPhones
new
Д а л е е из словаря удаляется запись с т е л е ф о н о м
Глава
для хранения
437
После этого содержимое словаря снова отображается. Теперь в нем стало на одну запись меньше: Сидоров: Иванов: Петров:
322-44-22 8-999-323-33-33
Сидоров: Петров:
8-999-323-33-44 8-999-323-33-33
Емкость и
размер словаря
При добавлении в словарь новых элементов происходит автоматическое увеличение его размера. Текущий размер словаря вы можете узнать при помощи свойства Count. Пример использования этого свойства приведен в листинге 13.20. Листинг using using
13.20.
Файл
System;
namespace
HashTable5
class static
void
args)
H a s h t a b l e myPhones
new
элементов
в
словаре:
Словари и многопоточность Если словари H a s h t a b l e используются в многопоточных программах, необходимо позаботиться о синхронизации. Синхронизация словарей H a s h t a b l e выполняется аналогично синхронизации массивов с применением статического ме тода или свойства S y n c R o o t . Применение метода S y n c h r o n i z e d демонстрируется в программе, исходный текст которой приведен в листинге 13.21.
438
А. В. Фролов, Г. В. Фролов. Язык С# Самоучитель
Листинг
13.21.
Файл
ch13\HashTable6\HashTable6App.cs
using using using namespace class static static
Hashtable Hashtable
public
static
myPhones;
void "MyThread:
поток myEnumerator
static
void
myPhones
args) new
ThreadStart myThreadDelegate Thread thr new
new
потока
Контейнеры для хранения объектов
439
П о л у ч и в у п р а в л е н и е , м е т о д M a i n , р а б о т а ю щ и й в р а м к а х о с н о в н о г о потока, с о з д а е т словарь m y P h o n e s и з а п о л н я е т его: myPhones
Далее
new
на
базе
Hashtable
этого
словаря
создается
новый,
синхронизированный
словарь
myPhonesSynchro Затем м е т о д M a i n з а п у с к а е т поток
о с у щ е с т в л я ю щ и й вывод содержи¬
мого словаря на к о н с о л ь . В р а м к а х этого метода с о з д а е т с я и т е р а т о р , к о т о р ы й и с п о л ь з у е т с я для ц и к л и ч е с к о г о п р о с м о т р а с о д е р ж и м о г о массива: myEnumerator
И с п о л ь з о в а н и е и т е р а т о р о в в м н о г о п о т о ч н ы х п р о г р а м м а х всегда т р е б у е т синхрони¬ з а ц и и , но для этого вам не о б я з а т е л ь н о с о з д а в а т ь н о в ы й , с и н х р о н и з и р о в а н н ы й сло¬ варь. В п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р е д с т а в л е н в л и с т и н г е 13.22, использует¬ ся о б ъ е к т с и н х р о н и з а ц и и , п о л у ч е н н ы й с п о м о щ ь ю с в о й с т в а SyncRoot. Листинг using using using
13.22.
Файл
System;
namespace class
HashTable7 HashTable7App
static
Hashtable
public
static
void поток
myEnumerator
440
В.
Г. В. Фролов. Язык
Самоучитель
static
void
args)
myPhones
new
ThreadStart Thread thr
myThreadDelegate new
new
T h r e a d S t a r t (MyThread)
потока
С в о й с т в о S y n c R o o t м ы и с п о л ь з у е м в блоке l o c k , как это п о к а з а н о н и ж е :
myEnumerator
Б л о к о г р а н и ч и в а е т ф р а г м е н т кода, внутри к о т о р о г о д о л ж н а в ы п о л н я т ь с я с и н х р о низация.
Сортированный список С о р т и р о в а н н ы й с п и с о к класса S o r t e d L i s t п р е д с т а в л я е т собой г и б р и д м а с с и в а и с л о в а р я , о п и с а н н ы х ранее в этой главе. Вы м о ж е т е з а п и с ы в а т ь в этот с п и с о к э л е м е н ты с к л ю ч а м и , как в с л о в а р ь , а затем извлекать эти э л е м е н т ы по ключу. Но есть и д р у гая в о з м о ж н о с т ь п р о г р а м м а м о ж е т извлекать э л е м е н т ы из с о р т и р о в а н н о г о списка по и н д е к с у , как это д е л а е т с я при р а б о т е с м а с с и в а м и . Глава
для хранения
441
Создание и наполнение списка Создание и наполнение сортированного списка можно выполнять та ким же образом, как и в случае словаря H a s h t a b l e Чтобы создать сортированный список, воспользуйтесь одним из конструкторов. Простейший не имеет параметров и создает пустой список: SortedList
myPhones
new
Здесь мы создали пустой список телефонных номеров. Для заполнения списка вы можете воспользоваться методом ниже:
как это показано
myPhones "8-999-323-33-33")
В качестве первого параметра методу A d d передается ключ добавляемого элемен¬ та, а в качестве второго значение элемента. Здесь мы используем те же самые прие мы, что и при работе со словарем H a s h t a b l e . Заметим, что в классе S o r t e d L i s t предусмотрено несколько конст¬ рукторов. С их помощью вы можете создавать сортированные списки на базе других кон¬ тейнеров, задавать начальный размер словаря и определять свои собственные методы сор¬ тировки элементов. Эти конструкторы аналогичны соответствующим конструкторам клас¬ са H a s h t a b l e . При необходимости вы найдете более подробную информацию об этих в справочной системе Microsoft Visual
Извлечение данных из списка Как мы уже говорили, программа может извлекать данные из сортированного списка двумя способами — по ключу (как из словаря) и по индексу (как из массива). Оба эти способа демонстрируются в программе, исходный текст которой вы найде те в листинге Листинг
Файл
using using SortedListNameSpace
namespace class
Classl
static
void
SortedList
args) myPhones
new
myEnumerator
442
A.
Г.
Фролов. Язык
Самоучитель
myEnumerator
myEnumerator
Console Иванова:
i
0;
i
i++)
(l}\n",
Вначале программа с о з д а е т и наполняет сортированный с п и с о к , используя д л я э т о го описанные выше приемы. Д а л е е о на отображает с о д е р ж и м о е списка с п о м о щ ь ю итератора, отображая на к о н с о л и п о л н о е с о д е р ж и м о е списка: I D i c t i o n a r y E n u m e r a t o r myEnumerator
А н а л о г и ч н у ю о п е р а ц и ю м ы выполняли ранее д л я словарей класса H a s h t a b l e . Сортированный список д о п у с к а е т использование квадратных с к о б о к для и з в л е ч е н и я и изменения элементов по ключу. Мы применили этот с п о с о б д л я получения те л е ф о н а Иванова: Иванова: Теперь о т о м , как извлечь Этот п р и е м д е м о н с т р и р у е т с я for(int i
0;
i
Глава 13. Контейнеры для хранения
сортированного списка S o r t e d L i s t по индексу. i++)
443
З д е с ь мы в цикле все менная цикла i играет роль индекса.
сортированного списка, причем п е р е
Для получения ключа элемента с заданным и н д е к с о м мы и с п о л ь з у е м з д е с ь м е т о д G e t K e y , передавая е м у э т о т и н д е к с в качестве единственного параметра. Что же касается значения элемента с заданным и н д е к с о м , то е г о м о ж н о извлечь с помощью м е т о д а G e t B y В ы должны передать э т о м у м е т о д у индекс нужного элемента в качестве параметра. Д а л е е наша программа д е м о н с т р и р у е т р а б о т у со с п и с к о м как с массивом: i
i
i++)
i, З д е с ь переменная цикла i используется для прямой индексации списка. Ключ и з влекается м е т о д о м G e t K e y , а значение — м е т о д о м G e t B y l n d e x . П е р е д т е м как завершить свою р а б о т у , наша программа выводит на консоль ключ и значение самого первого элемента сортированного списка элемента с нулевым
В о т что программа выведет на консоль после своего запуска: Иванов: Петров: Сидоров: Телефон
322-44-22 8-999-323-33-33 8-999-323-33-44 Иванова:
322-44-22
0. 1.
Иванов: Петров:
322-44-22 8-999-323-33-33
2.
Сидоров:
8-999-323-33-44
Иванов:
322-44-22
Определение индекса по ключу и значению В классе S o r t e d L i s t , предназначенном для создания сортированных списков, опре¬ д е л е н ы в с е м е т о д ы , рассмотренные нами р а н е е при описании словаря H a s h t a b l e . Это означает, что при р а б о т е с таким списком вам д о с т у п н ы те же самые в о з м о ж н о с т и , что и при р а б о т е с о словарем Так как сортированный список S o r t e d L i s t в е д е т себя и как в классе п р е д у с м о т р е н ы и д р у г и е м е т о д ы , позволяющие обращаться к элементам списка по и н д е к с у . Два таких м е т о д а ( G e t K e y и G e t B y l n d e x ) были рассмотрены нами в п р е д ы д у щ е м р а з д е л е . Первый из них позволяет извлечь ключ для элемента с заданным и н д е к с о м , а второй — значение элемента с заданным и н д е к с о м . 444
А В . Фролов, Г.В. Фролов. ЯзыкС#. Самоучитель
Помимо этого в классе S o r t e d L i s t есть методы, позволяющие определить ин декс элемента по ключу и значение элемента по ключу. Первая из этих операций вы полняется методом I n d e x O f K e y , а методом Использование методов I n d e x O f K e y и I n d e x O f V a l u e демонстрируется в про грамме, исходный текст которой приведен в листинге Листинг
13.24.
using using
Файл
System;
namespace
SortedListl
class static
void
args)
S o r t e d L i s t myPhones
new
myEnumerator
i
0;
i
i++)
Получив управление, метод M a i n нашей программы создает сортированный спи сок и заполняет его парами ключ-значение: S o r t e d L i s t myPhones
Глава
new "322-44-22"
Контейнеры для хранения объектов
445
Д а л е е в п р о г р а м м е с о з д а е т с я и т е р а т о р для п о с л е д о в а т е л ь н о г о п р о с м о т р а списка. П р о с м о т р о с у щ е с т в л я е т с я в цикле: myEnumerator
З н а ч е н и е и н д е к с а э л е м е н т а , о т о б р а ж а е м о г о на к о н с о л и , по к л ю ч у мы п о л у ч а е м при п о м о щ и в ы р а ж е н и я m y P h o n e s
IndexOfKey
Использование метода I n d e x O f V a l u e ,
в о з в р а щ а ю щ е г о и н д е к с э л е м е н т а п о зна
ч е н и ю , д е м о н с т р и р у е т с я в п р е д с т а в л е н н о м ниже цикле: i
Здесь
0;
i
i++)
мы
передаем
по индексу с
помощью
методу
IndexOfValue
метода G e t B y l n d e x .
значение
Очевидно,
элемента, что
индекс,
от м е т о д а I n d e x O f V a l u e , д о л ж е н быть равен п е р е м е н н о й
полученного полученный
В э т о м м о ж н о убе¬
диться, запустив программу на выполнение: 0. 1. 2.
Иванов: Петров: Сидоров:
322-44-22 8-999-323-33-33 8-999-323-33-44
0. 1. 2.
Иванов: Петров: Сидоров:
322-44-22 8-999-323-33-44
Изменение значения по индексу В ы ш е мы р а с с к а з а л и В а м , как м о ж н о извлечь из с о р т и р о в а н н о г о с п и с к а к л ю ч и значе ние э л е м е н т а по его индексу. П р е д у с м о т р е н а и д р у г а я в о з м о ж н о с т ь
и з м е н е н и е зна¬
чения э л е м е н т а , з а д а н н о г о с в о и м и н д е к с о м . Это м о ж н о с д е л а т ь при п о м о щ и метода
Д а н н о м у м е т о д у н у ж н о пе¬
редать д в а п а р а м е т р а — и н д е к с и з м е н я е м о г о э л е м е н т а и новое з н а ч е н и е э л е м е н т а . Ис¬ п о л ь з о в а н и е м е т о д а S e t B y l n d e x д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст ко торой п р и в е д е н в л и с т и н г е 1 3 . 2 5 .
446
А. В. Фролов, Г. В. Фролов. Язык С#. Самоучитель
Листинг
13.25.
using using
Файл
System;
namespace class static
void
SortedList
args) myPhones
new
myEnumerator
myPhones.
"+7- (095)
I D i c t i o n a r y E n u m e r a t o r myEnumeratorl
Здесь мы создаем сортированный список, используя для этого тот же прием, что и в предыдущей программе (см. листинг Затем мы изменяем значение элемен тов с индексами 1 и 2, вызывая для этого метод S e t B y l n d e x : myPhones.
Глава
для хранения объектов
447
П р о г р а м м а о т о б р а ж а е т на к о н с о л и и с х о д н о е содержимое списка, а т а к ж е с о д е р ж и мое списка после и з м е н е н и й : 0. 1. 2.
Иванов: Петров: Сидоров:
322-44-22 8-999-323-33-33 8-999-323-33-44
Иванов: Петров: Сидоров:
322-44-22
1. 2.
Удаление элементов списка В классе S o r t e d L i s t о п р е д е л е н ы 3 м е т о д а , п р е д н а з н а ч е н н ы е для у д а л е н и я э л е м е н тов из с о р т и р о в а н н о г о списка. С д в у м я из них вы у ж е з н а к о м ы по с л о в а р я м H a s h t a b l e это м е т о д C l e a r , у д а л я ю щ и й из с п и с к а все и м е ю щ и е с я там э л е м е н т ы , и м е т о д R e m o v e , с п о м о щ ь ю ко т о р о г о м о ж н о у д а л и т ь э л е м е н т с з а д а н н ы м к л ю ч о м . К л ю ч у д а л я е м о г о э л е м е н т а пере¬ дается методу R e m o v e в качестве единственного параметра. Третий м е т о д с н а з в а н и е м R e m o v e A t п о з в о л я е т у д а л и т ь э л е м е н т по его индексу. И н д е к с у д а л я е м о г о э л е м е н т а п е р е д а е т с я м е т о д у R e m o v e A t ч е р е з е д и н с т в е н н ы й пара метр. И с п о л ь з о в а н и е м е т о д а д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й вы н а й д е т е в л и с т и н г е 13.26. Листинг using using
13.26.
Файл
ch
System;
namespace class
SortedList3 SortedList3App
static
void
S o r t e d L i s t myPhones
args) new
SortedList
myEnumerator
448
А. В Фролов, Г. В Фролов. Язык С#. Самоучитель
myEnumeratorl
Вот как мы у д а л я е м э л е м е н т с и н д е к с о м myPhones
RemoveAt
П р о г р а м м а в ы в о д и т на к о н с о л ь с о д е р ж и м о е и с х о д н о г о списка, а т а к ж е с о д е р ж и м о е списка п о с л е у д а л е н и я из него элемента: 1. 2.
Иванов: Петров: Сидоров:
322-44-22 8-999-323-33-33 8-999-323-33-44
1.
Иванов: Сидоров:
322-44-22 8-999-323-33-44
О б р а т и т е в н и м а н и е , что и н д е к с э л е м е н т а с к л ю ч о м « С и д о р о в » и з м е н и л с я и стал равен е д и н и ц е . Д о б а в л е н и е э л е м е н т о в в с п и с о к или у д а л е н и е из него п р и в о д и т к и з м е н е н и ю и н д е к с о в . У ч т и т е э т о , если с о б и р а е т е с ь о б р а щ а т ь с я к э л е м е н т а м сорти¬ р о в а н н о г о списка по и н д е к с а м .
Работа в многопоточном режиме С т а т и ч е с к и е с п и с к и S o r t e d L i s t , о б ъ я в л е н н ы е как p u b l i c , м о ж н о и с п о л ь з о в а т ь в м н о г о п о т о ч н ы х п р и л о ж е н и я х без п р и н я т и я д о п о л н и т е л ь н ы х мер по с и н х р о н и з а ц и и . Если же вы создаете н е с т а т и ч е с к и е о б ъ е к т ы этого класса, то н е о б х о д и м о о б е с п е ч и т ь синхронизацию. Это м о ж н о сделать л и б о создав с и н х р о н и з и р о в а н н ы й с п и с о к м е т о д о м S y n c h r o n i z e d , л и б о п о л у ч и в о б ъ е к т с и н х р о н и з а ц и и с п о м о щ ь ю с в о й с т в а S y n c R o o t . Оба способа а н а л о г и ч н ы с п о с о б а м , о п и с а н н ы м в этой главе ранее для массива A r r a y L i s t и с л о в а р я H a s h t a b l e , п о э т о м у мы не будем о с т а н а в л и в а т ь с я на этом п о д р о б н о .
Стек Stack Такой к о н т е й н е р , как стек (stack), п о з в о л я е т о р г а н и з о в а т ь х р а н е н и е д а н н ы х по п р и н ципу вошел последний Так р а б о т а е т магазин а в т о м а т а К а л а ш н и кова. Вы заполняете его п а т р о н а м и с о д н о й с т о р о н ы , п р и ч е м те п а т р о н ы , к о т о р ы е бы¬ ли в с т а в л е н ы в магазин п о с л е д н и м и , будут и с п о л ь з о в а н ы в п е р в у ю о ч е р е д ь . Глава 15
Язык С#
Контейнеры для хранения объектов Самоучитель
449
Для к л а с с и ч е с к о г о стека о п р е д е л е н ы как м и н и м у м две о п е р а ц и и : •
запись в стек ( p u s h ) ,
•
и з в л е ч е н и е из стека (pop).
При записи д о б а в л я е м ы й э л е м е н т о к а з ы в а е т с я в в е р х у ш к е стека, а все х р а н и в ш и е с я там ранее э л е м е н т ы с д в и г а ю т с я в н и з . П р и и з в л е ч е н и и э л е м е н т а из стека все его э л е м е н т ы с д в и г а ю т с я на одну п о з и ц и ю вверх. Д о п о л н и т е л ь н о м о ж е т быть о п р е д е л е н а о п е р а ц и я п р о в е р к и с о д е р ж и м о г о в е р х у ш к и стека к о т о р а я не п р и в о д и т к и з в л е ч е н и ю э л е м е н т о в из стека. Среди к о н т е й н е р о в б и б л и о т е к и к л а с с о в Microsoft
F r a m e w o r k имеется класс
S t a c k , с п е ц и а л ь н о п р е д н а з н а ч е н н ы й для о р г а н и з а ц и и с т е к о в . Он очень у д о б е н и не¬ сложен в и с п о л ь з о в а н и и .
Создание стека Для с о з д а н и я стека класса S t a c k в ы м о ж е т е и с п о л ь з о в а т ь один и з трех конструкто¬ р о в . П р о с т е й ш и й к о н с т р у к т о р не имеет п а р а м е т р о в и п р е д н а з н а ч е н для с о з д а н и я пус¬ того стека с р а з м е р о м , з а д а н н ы м по у м о л ч а н и ю : Stack
new
И м е ю т с я т а к ж е к о н с т р у к т о р ы с о д н и м п а р а м е т р о м . П е р в ы й из них позволяет соз¬ дать стек на базе д р у г о г о к о н т е й н е р а , а второй — пустой стек з а д а н н о г о размера.
Добавление элементов в стек Ч т о б ы д о б а в и т ь э л е м е н т в стек, н е о б х о д и м о вызвать м е т о д P u s h и передать этому ме¬ тоду ч е р е з е д и н с т в е н н ы й п а р а м е т р ссылку н а д о б а в л я е м ы й э л е м е н т . Н и ж е мы с о з д а л и пустой стек и д о б а в и л и в него н е с к о л ь к о т е к с т о в ы х строк: Stack
wordsStack
new
З а м е т и м , что при н е о б х о д и м о с т и м о ж н о д о б а в и т ь в стек и пустой э л е м е н т , передав м е т о д у P u s h з н а ч е н и е null.
Извлечение элементов из стека Для того чтобы извлечь элемент из стека, воспользуйтесь методом Pop. Этот метод извле¬ кает из стека один элемент и возвращает в ы з ы в а ю щ е й программе ссылку на этот элемент: один П р и п о п ы т к е и з в л е ч е н и я э л е м е н т а и з п у с т о г о стека в о з н и к а е т и с к л ю ч е н и е I n v a l i d O p e r a t i o n E x c e p t i o n . Если при работе в а ш е й п р о г р а м м ы в о з м о ж н о воз¬ н и к н о в е н и е д а н н о й с и т у а ц и и , н е забудьте п р е д у с м о т р е т ь с о о т в е т с т в у ю щ и й обработ¬ чик и с к л ю ч е н и й .
450
Фролов, Г. В. Фролов. Язык С#. Самоучитель
Чтобы
и з б е ж а т ь в ы п о л н е н и я о п е р а ц и и и з в л е ч е н и я д а н н ы х из п у с т о г о стека, вы
также м о ж е т е к о н т р о л и р о в а т ь т е к у щ и й размер стека при п о м о щ и с в о й с т в а C o u n t : в
стеке
Проверка содержимого верхушки стека При п о м о щ и м е т о д а P e e k п р о г р а м м а м о ж е т п р о в е р и т ь с о д е р ж и м о е в е р х у ш к и стека, не и з в л е к а я из стека х р а н я щ и й с я там элемент: элемент: М е т о д P e e k в о з в р а щ а е т э л е м е н т , з а п и с а н н ы й в в е р х у ш к е стека. Если стек пуст, при в ы з о в е этого метода в о з н и к а е т и с к л ю ч е н и е I n v a l i d O p e r a t i o n E x c e p t i o n .
Просмотр стека с помощью итератора Содержимое
стека
можно
просмотреть
при
помощи
итератора
класса
Sys-
I E n u m e r a t o r , как это п о к а з а н о н и ж е : IEnumerator myEnumerator
О с о б е н н о с т ь э т о г о м е т о д а з а к л ю ч а е т с я в т о м , что э л е м е н т ы б у д у т п р о с м а т р и ¬ в а т ь с я не в п о р я д к а д о б а в л е н и я , а в о б р а т н о м п о р я д к е , к а к б у д т о они и з в л е к а ю т с я и з с т е к а . П р о ц е д у р а п р о с м о т р а с п о м о щ ь ю и т е р а т о р а , о д н а к о , н е и з м е н я е т содер¬ жимого самого стека.
Удаление элементов стека Для у д а л е н и я всех э л е м е н т о в и з стека п р е д н а з н а ч е н м е т о д C l e a r . Что ж е к а с а е т с я у д а л е н и я о т д е л ь н ы х э л е м е н т о в , то эта о п е р а ц и я в ы п о л н я е т с я при и з в л е ч е н и и элемен¬ тов из стека м е т о д о м P o p .
Другие методы и свойства класса Stack Если стек с о з д а е т с я п р о г р а м м о й , р а б о т а ю щ е й в м н о г о п о т о ч н о м р е ж и м е , н е о б х о д и м о обеспечить синхронизацию. Это м о ж н о с д е л а т ь л и б о создав м е т о д о м S y n c h o n i z e d с и н х р о н и з и р о в а н н ы й стек, л и б о п о л у ч и в о б ъ е к т с и н х р о н и з а ц и и при п о м о щ и с в о й с т в а S y n c R o o t . С в о й с т в о п о з в о л я е т о п р е д е л и т ь , я в л я е т с я л и стек с и н х р о н и з и р о в а н н ы м или нет. п р и е м ы были о п и с а н ы р а н е е , когда мы р а с с к а з ы в а л и о д р у г и х Глава 13. Контейнеры для хранения
451
С п о м о щ ь ю метода C o n t a i n s , и м е ю щ е г о один п а р а м е т р , в ы м о ж е т е о п р е д е л и т ь , х р а н и т с я ли з а д а н н ы й э л е м е н т в П р и в ы з о в е н у ж н о п е р е д а т ь э т о м у методу ссылку на э л е м е н т . Если такой э л е м е н т н а х о д и т с я в стеке, м е т о д вернет значение t r u e , в противном — значение f a l s e . При н е о б х о д и м о с т и вы м о ж е т е с к о п и р о в а т ь с о д е р ж и м о е стека в д р у г о й стек (мето¬ дом C l o n e ) или в о д н о м е р н ы й м а с с и в класса A r r a y ( м е т о д о м С о р у Т о ) .
Пример программы В листинге
13.27 мы п р и в е л и и с х о д н ы й текст п р о г р а м м ы , в ы п о л н я ю щ е й о с н о в н ы е
о п е р а ц и и со с т е к о м . Так как все эти о п е р а ц и и были о п и с а н ы р а н е е , мы о с т а в л я е м вам эту п р о г р а м м у д л я с а м о с т о я т е л ь н о г о и з у ч е н и я . Листинг using using
13.27.
Файл
System;
StackDemo class static
void
Stack wordsStack
args) new
в
стеке
I E n u m e r a t o r myEnumerator
элемент:
w o r d s S t a c k . Peek
один один
452
A
элемент:
Г. В. Фролов. Язык С#. Самоучитель
извлечения
в
стеке
myEnumeratorl
П о с л е запуска п р о г р а м м а создает стек и з а п и с ы в а е т в него 7 т е к с т о в ы х с т р о к фра¬ зы « к а ж д ы й о х о т н и к ж е л а е т з н а т ь , где сидит фазан». Д а л е е п р о г р а м м а о т о б р а ж а е т на к о н с о л и к о л и ч е с т в о э л е м е н т о в и с о д е р ж и м о е стека (при п о м о щ и и т е р а т о р а ) . П о с л е этого п р о г р а м м а п о к а з ы в а е т н а к о н с о л и с о д е р ж и м о е в е р х у ш к и стека, и з в л е к а е т из стека два э л е м е н т а , а затем вновь о т о б р а ж а е т весь стек: Всего
в
стеке
7
элементов:
фазан где желает каждый Первый фазан И з в л е к а е м один э л е м е н т : фазан И з в л е к а е м один э л е м е н т : сидит П о с л е и з в л е ч е н и я в с т е к е пяти
элементов:
где энать желает каждый О б р а т и т е в н и м а н и е , что слова и с х о д н о й фразы о т о б р а ж а ю т с я в о б р а т н о м п о р я д к е , так как они х р а н и л и с ь в стеке.
Очередь Queue очереди з н а к о м о , н а в е р н о е , в с е м , кто хоть раз бывал в магазине. О ч е р е д ь ра¬ ботает по п р и н ц и п у « п е р в ы й в о ш е л — первый в ы ш е л » . Для создания очередей в б и б л и о т е к е классов Microsoft Framework предусмот рен класс Q u e u e . Р а с с м о т р и м о с н о в н ы е п р и е м ы его и с п о л ь з о в а н и я . Глава
Контейнеры для хранения объектов
453
Создание очереди Для создания
вы можете и с п о л ь з о в а т ь один из ч е т ы р е х п е р е г р у ж е н н ы х кон
с т р у к т о р о в , п р е д у с м о т р е н н ы х в классе Q u e u e . П р о с т е й ш и й к о н с т р у к т о р не имеет п а р а м е т р о в . Он создает п у с т у ю очередь с ха рактеристиками, заданными по умолчанию: Queue
new
Другие к о н с т р у к т о р ы п р е д н а з н а ч е н ы для с о з д а н и я о ч е р е д и на базе с у щ е с т в у ю щ е г о к о н т е й н е р а , о ч е р е д и с з а д а н н ы м н а ч а л ь н ы м р а з м е р о м и ф а к т о р о м роста. Ф а к т о р рос та
это число р а з , в к о т о р о е у в е л и ч и в а е т с я т е к у щ а я е м к о с т ь о ч е р е д и при д о б а в л е н и и
элемента в полностью заполненную очередь.
Добавление элемента в очередь Ч т о б ы д о б а в и т ь в о ч е р е д ь н о в ы й э л е м е н т , п р и м е н я й т е м е т о д E n q u e u e . Н и ж е мы по¬ казали п р и м е р и с п о л ь з о в а н и я э т о г о метода: Queue w o r d s Q u e u e
new
П р и н е о б х о д и м о с т и вы м о ж е т е д о б а в и т ь в о ч е р е д ь з н а ч е н и е null.
Извлечение элементов из очереди С п о м о щ ь ю м е т о д а D e q u e u e п р о г р а м м а м о ж е т и з в л е ч ь один э л е м е н т и з о ч е р е д и : один
элемент:
К а к мы у ж е г о в о р и л и , в н а ч а л е из очереди и з в л е к а ю т с я те э л е м е н т ы , к о т о р ы е были туда з а п и с а н ы п е р в ы м и .
П о с л е и з в л е ч е н и я э л е м е н т а с о д е р ж и м о е очереди с д в и г а е т с я
на одну п о з и ц и ю . При
попытке
извлечения
элемента
из
пустой
очереди
возникает
исключение
Чтобы избежать выполнения операции извлечения д а н н ы х из п у с т о й о ч е р е д и , вы м о ж е т е к о н т р о л и р о в а т ь ее т е к у щ и й р а з м е р при п о м о щ и свойства
Count: в
454
очереди
В. Фролов, Г. В. Фролов. Язык
Самоучитель
Проверка содержимого начала очереди При п о м о щ и м е т о д а P e e k п р о г р а м м а м о ж е т п р о в е р и т ь с о д е р ж и м о е начала о ч е р е д и , не и з в л е к а я х р а н я щ и й с я там элемент: элемент: М е т о д P e e k в о з в р а щ а е т э л е м е н т , з а п и с а н н ы й в начале очереди и г о т о в ы й к «об¬ с л у ж и в а н и ю » . Е с л и о ч е р е д ь пустая, при в ы з о в е этого метода в о з н и к а е т и с к л ю ч е н и е
Просмотр очереди с помощью итератора Содержимое
очереди
можно
просмотреть
при
помощи
итератора
класса
t e m . C o l l e c t i o n s . I E n u m e r a t o r , как это п о к а з а н о н и ж е : IEnumerator myEnumerator
В о т л и ч и е от стека э л е м е н т ы о ч е р е д и будут п р о с м а т р и в а т ь с я в п о р я д к е добавле¬ ния. П р о ц е д у р а п р о с м о т р а с п о м о щ ь ю и т е р а т о р а не и з м е н я е т с о д е р ж и м о г о о ч е р е д и .
Удаление элементов очереди Для у д а л е н и я всех э л е м е н т о в и з очереди ваша п р о г р а м м а м о ж е т и с п о л ь з о в а т ь м е т о д Clear.
Удаление
отдельных
элементов
выполняется
при
извлечении
элементов
из очереди методом D e q u e u e .
Другие методы и свойства класса Queue Если о ч е р е д ь (как и л ю б о й д р у г о й к о н т е й н е р ) с о з д а е т с я п р о г р а м м о й , р а б о т а ю щ е й в многопоточном режиме, необходимо обеспечить синхронизацию. Это м о ж н о с д е л а т ь , создав м е т о д о м синхронизированную очередь л и б о п о л у ч и в о б ъ е к т с и н х р о н и з а ц и и при п о м о щ и с в о й с т в а S y n c R o o t . С в о й с т в о I s S y n c h r o n i z e d позволяет определить, является ли очередь синхронизированной или нет. С п о м о щ ь ю метода C o n t a i n s , и м е ю щ е г о один п а р а м е т р , в ы м о ж е т е о п р е д е л и т ь , н а х о д и т с я ли з а д а н н ы й э л е м е н т в о ч е р е д и . П р и в ы з о в е н у ж н о п е р е д а т ь этому м е т о д у с с ы л к у н а э л е м е н т . Если такой э л е м е н т н а х о д и т с я в о ч е р е д и , м е т о д C o n t a i n s в е р н е т значение t r u e , в противном значение f a l s e . П р и н е о б х о д и м о с т и вы м о ж е т е с к о п и р о в а т ь с о д е р ж и м о е о ч е р е д и в д р у г у ю о ч е р е д ь ( м е т о д о м C l o n e ) или в о д н о м е р н ы й массив класса A r r a y ( м е т о д о м С о р у Т о ) .
Глава 13. Контейнеры для хранения объектов
455
Пример
программы
В л и с т и н г е 13.28 мы п р и в е л и и с х о д н ы й текст п р о г р а м м ы , в ы п о л н я ю щ е й о с н о в н ы е операции с очередью. Все в ы п о л н я е м ы е этой п р о г р а м м о й , были о п и с а н ы р а н е е , п о э т о м у мы о с т а в л я е м ее вам для с а м о с т о я т е л ь н о г о и з у ч е н и я . Листинг using using
13.28.
Файл
System;
namespace
static
void
args)
Queue w o r d s Q u e u e
new Q u e u e
в
очереди
I E n u m e r a t o r myEnumerator
"После
извлечения
в
один
элемент:
один
элемент:
очереди
myEnumeratorl
456
A.
Г. R Фролов. Язык C# Самоучитель
П о с л е запуска п р о г р а м м а создает очередь и з а п и с ы в а е т в нее 7 т е к с т о в ы х с т р о к фразы о х о т н и к ж е л а е т з н а т ь , где с и д и т Далее программа отображает на к о н с о л и к о л и ч е с т в о э л е м е н т о в и с о д е р ж и м о е очереди (при п о м о щ и и т е р а т о р а ) . По¬ сле э т о г о п р о г р а м м а п о к а з ы в а е т н а к о н с о л и с о д е р ж и м о е н а ч а л а о ч е р е д и , и з в л е к а е т из о ч е р е д и два э л е м е н т а , а затем вновь о т о б р а ж а е т всю о ч е р е д ь : Всего
в очереди
7
элементов:
каждый желает знать где фазан Первый каждый И з в л е к а е м один э л е м е н т : каждый И з в л е к а е м один э л е м е н т : охотник После и з в л е ч е н и я в о ч е р е д и пяти
элементов:
желает знать где фазан Так как э л е м е н т ы очереди о б р а б а т ы в а ю т с я в п о р я д к е п о с т у п л е н и я , при отображе¬ нии с о д е р ж и м о г о очереди п о р я д о к слов и с х о д н о й фразы не м е н я е т с я .
Битовый массив BitArray П о с л е д н и й к о н т е й н е р , к о т о р ы й мы р а с с м о т р и м в этой г л а в е , сив B i t A r r a y .
В
отличие
допускают выполнение NOT и
от обычных массивов типа b o o l
над собой
Для этого в
для хранения
это б и т о в ы й м а с массивы
поразрядных операций, таких,
BitArray
как A N D ,
OR,
Bi t A r r a y п р е д у с м о т р е н ы с о о т в е т с т в у ю щ и е ме-
457
З а м е т и м , что, подобно массивам A r r a y , массивы B i t A r r a y н е д о п у с к а ю т и з м е нения своих размеров после создания. П о п ы т к а з а п и с и или чтения за пределами мас сива
неизбежно приведет к возникновению исключения.
Н а п о м н и м , что значения л о г и ч е с к и х данных в языке п р о г р а м м и р о в а н и я С ставляются константами t r u e (истина) и
пред
Эти значения никак не соот
носятся с числовыми з н а ч е н и я м и , в ч а с т н о с т и со з н а ч е н и я м и 1 и 0.
Создание массива BitArray Для
создания
массива
BitArray
следует воспользоваться
одним
из
конструкторов,
определенных в классе B i t A r r a y . Простейший из этих конструкторов создает массив заданного размера, все которого
содержат значения
false.
Размер
создаваемого
массива
передается
конст¬
руктору в качестве п а р а м е т р а : BitArray Другие
byteArray
new
конструкторы
BitArray(8);
позволяют создать битовый
щ е с т в у ю щ е г о массива B i t A r r a y ,
массив
B i t A r r a y на базе су
а т а к ж е массивов чисел b o o l , b y t e и
int.
Есть
т а к ж е в о з м о ж н о с т ь создания массива з а д а н н о г о размера, во всех ячейках к о т о р о г о из¬ начально будут записаны значения t r u e или
выбору программиста.
Инициализация ячеек массива Ячейки массива BitArray п р о щ е всего проинициализировать с помощью квадратных скобок, как это п о к а з а н о н и ж е : BitArray
byteArray
new
true; true;
false; С у щ е с т в у ю т и д р у г и е методы и н и ц и а л и з а ц и и ячеек массива. С помощью метода SetAll можно записать во все ячейки массива одно и то же например:
После в ы п о л н е н и я этой
операции
во всех ячейках массива b y t e A r r a y будет хра¬
ниться з н а ч е н и е t r u e . Чтобы у с т а н о в и т ь з н а ч е н и е для заданной я ч е й к и массива, воспользуйтесь методом Set: false); 458
А.
Фролов, Г.
Фролов. Язык
Самоучитель
В качестве п е р в о г о п а р а м е т р а этому методу п е р е д а е т с я индекс э л е м е н т а , а в каче¬ стве в т о р о г о — новое з н а ч е н и е . Таким о б р а з о м , в п р и в е д е н н о м в ы ш е п р и м е р е мы за¬ писываем значение ячейку массива с и н д е к с о м 10.
Извлечение значений из массива Для того ч т о б ы извлечь з н а ч е н и е из заданной ваться к в а д р а т н ы м и с к о б к а м и .
м а с с и в а , вы м о ж е т е воспользо¬
Эта п р о ц е д у р а вам и з в е с т н а по р а б о т е с о б ы ч н ы м и
м а с с и в а м и . К р о м е т о г о , м о ж н о извлечь з н а ч е н и е и з массива B i t A r r a y при п о м о щ и метода G e t , передав ему индекс н у ж н о г о элемента.
Просмотр массива П р о г р а м м а м о ж е т п о с л е д о в а т е л ь н о п р о с м о т р е т ь с о д е р ж и м о е всего м а с с и в а с помо¬ щью итератора IEnumerator. Это д е м о н с т р и р у е т с я н и ж е : System. C o l l e c t i o n s
I E n u m e r a t o r myEnumerator
else
О б р а т и т е в н и м а н и е , что для о т о б р а ж е н и я на к о н с о л и л о г и ч е с к и х з н а ч е н и й в виде е д и н и ц и нулей мы в ы н у ж д е н ы в ы п о л н я т ь с п е ц и а л ь н о е п р е о б р а з о в а н и е .
Логические операции над массивами Н а наш
взгляд, — это
наиболее интересная
возможность,
выполнение логических операций
м а с с и в о в . В табл.
предоставляемая классом B i t ¬
над с о д е р ж и м ы м ц е л ы х б и т о в ы х
13.1 мы п е р е ч и с л и л и м е т о д ы , п р е д н а з н а ч е н н ы е для в ы п о л н е н и я по¬
р а з р я д н ы х б и т о в ы х операций над м а с с и в а м и класса B i t A r r a y . Таблица
Методы
для
выполнения
поразрядных
Операция
Метод
и
And
или
Or
ИСКЛЮЧАЮЩЕЕ
операций
ИЛИ
НЕ
Not
И с п о л ь з о в а н и е м е т о д о в для в ы п о л н е н и я п о р а з р я д н ы х о п е р а ц и й д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р е д с т а в л е н в л и с т и н г е 13.29.
Глава
Контейнеры для хранения объектов
459
Листинг using using
13.29.
Файл
System;
namespace class static
void
args)
С BitArray
byteArray new true; false; true; true;
BitArray(8);
false;
myEnumerator
else
BitArray maskArray
maskArray maskArray maskArray maskArray maskArray maskArray maskArray
[0] [1] [2] [3] [5] [6] [7]
new
BitArray(8)
false false false true true; true; myEnumeratorl
460
А В.
Г В. Фролов. Язык
Самоучитель
else
BitArray
andArray
new
else
BitArray
orArray
new
else
Эта
программа
создает
и
инициализирует
два
битовых
и m a s k A r r a y , записывая в них различные логические значения: BitArray
byteArray new true; true;
Глава
Контейнеры для хранения объектов
массива,
byteArray
byteArray[4]
false; false; true; false;
BitArray maskArray new true; false; false;
BitArray
true
Исходные
этих массивов отображаются на консоли при помощи итерато
ров. Вот, например, как отображается массив b y t e A r r a y : I E n u m e r a t o r myEnumerator
else
Массив m a s k A r r a y отображается аналогично. Далее программа выполняет над массивами b y t e A r r a y и m a s k A r r a y логиче скую операцию И, записывая ее результат в новый массив a n d A r r a y : BitArray
andArray
new
el s e
462
Фролов, Г.
Язык
Самоучитель
Результат
выполнения
операции
к о н с т р у к т о р у класса B i t A r r a y ,
с о з д а ю щ е м у массив
мы andArray.
передаем
Далее содержимое
массива a n d A r r a y о т о б р а ж а е т с я н а к о н с о л и . Аналогичным
образом
п р о г р а м м а в ы п о л н я е т над
этими
массивами
поразрядную
о п е р а ц и ю И Л И . В о т что п о я в и т с я н а к о н с о л и после з а в е р ш е н и я р а б о т ы н а ш е й п р о граммы: Число: Маска: AND: OR:
Глава
10110010 10001111 10000010 10001111
для хранения объектов
463
Глава 14. Файлы и потоки Б о л ь ш и н с т в о п р о г р а м м так или и н а ч е р а б о т а ю т с каталогами и ф а й л а м и . С р е д с т в а д л я р а б о т ы с ф а й л а м и п р е д у с м о т р е н ы п р а к т и ч е с к и в л ю б о м я з ы к е п р о г р а м м и р о в а н и я . Эти средства м о г у т быть н е п о с р е д с т в е н н о в я з ы к , а также в х о д и т ь в с т а н д а р т н ы е б и б л и о т е к и ф у н к ц и й и к л а с с о в , п о с т а в л я ю щ и х с я вместе с к о м п и л я т о р о м . П р о г р а м м ы , с о с т а в л е н н ы е на я з ы к е С # , р а б о т а ю т с каталогами и ф а й л а м и при по¬ м о щ и с п е ц и а л ь н о п р е д н а з н а ч е н н ы х для этого к л а с с о в , в х о д я щ и х в состав библиотеки классов Microsoft Framework.
Потоки данных и классы Все операции с файлами в п р о г р а м м а х С# с п о м о щ ь ю так н а з ы в а е м ы х потоков данных (data stream). п у т а й т е их с п о т о к а м и в ы п о л н е н и я (thread), о к о т о рых мы р а с с к а з ы в а л и в гл. 10. П р о г р а м м ы м о г у т в ы п о л н я т ь над п о т о к а м и д а н н ы х 3 операции: •
запись д а н н ы х в п о т о к ,
•
ч т е н и е д а н н ы х из потока,
•
позиционирование.
Для п о т о к о в , с в я з а н н ы х с ф а й л а м и , о п р е д е л е н о т а к ж е т а к о е п о н я т и е , как т е к у щ а я п о з и ц и я в н у т р и файла. П е р е д н а ч а л о м о п е р а ц и й в в о д а - в ы в о д а п р о г р а м м а д о л ж н а о т к р ы т ь п о т о к . П р и этом т е к у щ а я п о з и ц и я у с т а н а в л и в а е т с я на н а ч а л о файла. П р и чтении ф а й л а или записи в файл б л о к о в д а н н ы х т е к у щ а я п о з и ц и я с д в и г а е т с я к концу файла на к о л и ч е с т в о бай¬ т о в , р а в н о е р а з м е р у п р о ч и т а н н о г о или з а п и с а н н о г о блока д а н н ы х . П р и п о м о щ и с р е д с т в п о з и ц и о н и р о в а н и я п р о г р а м м а м о ж е т у с т а н о в и т ь т е к у щ у ю п о з и ц и ю в произ¬ в о л ь н о е м е с т о ф а й л а . К о г д а р а б о т а с ф а й л о м з а к о н ч е н а , п р о г р а м м а о б я з а т е л ь н о долж¬ н а з а к р ы т ь с о о т в е т с т в у ю щ и й поток я в н ы м о б р а з о м . П р о г р а м м а С# м о ж е т р а б о т а т ь с п о т о к а м и н е с к о л ь к и х т и п о в : •
стандартными потоками ввода и вывода,
•
потоками, связанными с локальными файлами,
•
п о т о к а м и , с в я з а н н ы м и с ф а й л а м и в о п е р а т и в н о й памяти.
Р а с с м о т р и м к р а т к о классы б и б л и о т е к и Microsoft токами перечисленных выше типов.
F r a m e w o r k , с в я з а н н ы е с по¬
Стандартные потоки С т а н д а р т н ы е п о т о к и о б ы ч н о с в я з а н ы с к о н с о л ь ю и к л а в и а т у р о й , но м о г у т быть пере¬ н а п р а в л е н ы в ф а й л ы с р е д с т в а м и О С . По своему н а з н а ч е н и ю эти потоки б о л ь ш е всего н а п о м и н а ю т с т а н д а р т н ы е потоки ввода и в ы в о д а , а т а к ж е с т а н д а р т н ы й поток вывода сообщений об ошибках ОС M S - D O S .
464
П р а к т и ч е с к и все п р о г р а м м ы , и с х о д н ы е тексты к о т о р ы х п р и в о д и л и с ь в нашей кни¬ ге, р а б о т а ю т со с т а н д а р т н ы м и п о т о к а м и ввода и в ы в о д а . Для в ы в о д а д а н н ы х в стан¬ д а р т н ы й п о т о к в ы в о д а мы п р и м е н я л и м е т о д ы Write и Sys tem Console В в о д д а н н ы х из с т а н д а р т н о г о потока в ы п о л н я л с я с по мощью метода Console ReadLine.
Базовые классы для работы с файлами и потоками Количество классов, созданных для работы с файлами, достаточно велико и может при¬ вести начинающего программиста в растерянность. Прежде чем мы займемся конкретны¬ ми классами и приведем примеры п р и л о ж е н и й , р а б о т а ю щ и х с потоками и файлами, рас¬ смотрим иерархию классов, предназначенных для организации ввода и вывода.
Основные классы ввода и вывода Как и все классы в
о с н о в н ы е к л а с с ы ввода и в ы в о д а п р о и з о ш л и от класса Obj ect
(рис. 14.1).
Path
Основные
классы
для работы с
файлами
и потоками
Р а с с м о т р и м кратко их н а з н а ч е н и е . Класс B i n a r y R e a d e r предназначен для чтения блоков данных из потоков ввода на у р о в н е о т д е л ь н ы х б а й т о в . О б ы ч н о для о б ъ е к т о в из ф а й л о в , т а к и х , как стро¬ ки и ч и с л а , п р о г р а м м и с т ы и с п о л ь з у ю т д р у г и е , более м о щ н ы е классы. Класс с л у ж и т в к а ч е с т в е н и з к о у р о в н е в о г о средства записи дан¬ ных в п о т о к и в ы в о д а . Класс F i l e п р е д н а з н а ч е н для р а б о т ы с о г л а в л е н и я м и к а т а л о г о в . С п о м о щ ь ю ста¬ т и ч е с к и х м е т о д о в этого класса м о ж н о п о л у ч и т ь с п и с о к ф а й л о в и к а т а л о г о в , располо¬ ж е н н ы х в з а д а н н о м к а т а л о г е , с о з д а т ь или у д а л и т ь каталог, п е р е и м е н о в а т ь файл или каталог, а т а к ж е в ы п о л н и т ь н е к о т о р ы е д р у г и е о п е р а ц и и . С п о м о щ ь ю с т а т и ч е с к и х м е т о д о в класса D i r e c t o r y п р о г р а м м а м о ж е т п о л у ч а т ь списки к а т а л о г о в и п о д к а т а л о г о в , с о з д а в а т ь и у д а л я т ь к а т а л о г и , а т а к ж е в ы п о л н я т ь над к а т а л о г а м и д р у г и е п о д о б н ы е о п е р а ц и и . Класс P a t h о б е с п е ч и в а е т м е т о д ы и с в о й с т в а , с п о м о щ ь ю к о т о р ы х п р о г р а м м ы мо¬ гут р а б о т а т ь с и м е н а м и и п о л н ы м и путями к а т а л о г о в . Глава
14.
Файлы
и
465
Р а с с м о т р е н н ы е в п р е д ы д у щ е м разделе классы D i r e c t o r y и F i l e с о д е р ж а т т о л ь к о статические
члены
и
потому предназначены
для работы
с
файлами и
каталогами
без о б р а з о в а н и я э к з е м п л я р о в с о о т в е т с т в у ю щ и х о б ъ е к т о в . Классы
и
напротив, требуют создания экземпляров
о б ъ е к т о в , п р е д с т а в л я ю щ и х с о о т в е т с т в е н н о каталоги и ф а й л ы . Эти к л а с с ы унаследова¬ н ы о т о б щ е г о а б с т р а к т н о г о базового класса
к о т о р ы й , в с в о ю оче
р е д ь , я в л я е т с я п р о и з в о д н ы м о т а б с т р а к т н о г о базового класса M a r s h a l B y R e f
ect
(рис. 14.2).
Рис. Абстрактный
базовый
14.2.
Классы на
бЭЗв
класс M a r s h a l B y R e f O b j e c t о б е с п е ч и в а е т в о з м о ж н о с т ь
д о с т у п а к о б ъ е к т а м через г р а н и ц ы д о м е н о в п р и л о ж е н и й , что н е о б х о д и м о в тех случа¬ ях, когда п р и л о ж е н и я в ы п о л н я ю т у д а л е н н у ю о б р а б о т к у д а н н ы х .
Классы для работы с потоками Н а базе класса
S t r e a m создано несколько производных классов,
ально п р е д н а з н а ч е н н ы х для р а б о т ы с п о т о к а м и ввода и в ы в о д а ( р и с . 14.3). Класс и вывода. Класс
S t r e a m с л у ж и т б а з о в ы м для д р у г и х к л а с с о в п о т о к о в о г о ввода как это
в и д н о и з его
н а з в а н и я , п р е д н а з н а ч е н для р а б о т ы
с ф а й л а м и ч е р е з потоки ввода и в ы в о д а . Он о б е с п е ч и в а е т п о з и ц и о н и р о в а н и е п о т о к о в ввода и
вывода методом
Seek.
Это п о з в о л я е т в ы п о л н я т ь п р я м о й д о с т у п
к файлам.
466
А В. Фролов, Г. В. Фролов. Язык С#. Самоучитель
Рис.
14.3.
Классы для работы с
потоками
С п о м о щ ь ю класса м о ж н о о р г а н и з о в а т ь ввод и в ы в о д д а н н ы х в з а ш и ф р о в а н н о м виде. Класс п о з в о л я е т с о з д а в а т ь файлы в о п е р а т и в н о й п а м я т и . Д о с т у п к т а к и м ф а й л а м о с у щ е с т в л я е т с я очень б ы с т р о , так как не т р е б у е т с я р а б о т а т ь с мед¬ ленными дисковыми устройствами. Обычно файлы в оперативной памяти создаются д л я в р е м е н н о г о х р а н е н и я д а н н ы х , п о т о м у что п о с л е в ы к л ю ч е н и я к о м п ь ю т е р а содер¬ ж и м о е э т и х ф а й л о в исчезнет. С п о м о щ ь ю класса N e t w o r k s c r e a m м о ж н о о с у щ е с т в л я т ь у д а л е н н ы й в в о д и вы¬ вод д а н н ы х через сетевое с о е д и н е н и е . Класс B u f f о б е с п е ч и в а е т б у ф е р и з а ц и ю при работе с п о т о к а м и ввода и в ы в о д а . Б у ф е р и з а ц и я о п е р а ц и й ввода и в ы в о д а в б о л ь ш и н с т в е случаев з н а ч и т е л ь н о у с к о р я е т р а б о т у п р и л о ж е н и й , так как при ее и с п о л ь з о в а н и и с о к р а щ а е т с я к о л и ч е с т в о о б р а щ е н и й к с и с т е м е для о б м е н а д а н н ы м и с в н е ш н и м и у с т р о й с т в а м и .
Классы для работы с потоками текстовых символов С п е ц и а л ь н о д л я р а б о т ы с п о т о к а м и т е к с т о в ы х с и м в о л о в U N I C O D E в б и б л и о т е к е клас сов Microsoft Framework предусмотрены абстрактные классы S y s t e m . Reader и System. П е р в ы й и з этих к л а с с о в п р е д н а з н а ч е н д л я п о т о к о в ввода, а в т о р о й для п о т о к о в в ы в о д а (рис. 14.4). С
помощью
класса
б а з о в ы м д л я к о т о р о г о я в л я е т с я класс п р о г р а м м а м о ж е т читать о т д е л ь н ы е байты в х о д н о г о по¬
тока символов.
Глава 14. Файлы и потоки
467
Рис.
14.4.
Классы
для работы
А н а л о г и ч н о класс
с
потоками
текстовых
созданный н а базе класса
символов System.
п о з в о л я е т писать байты с и м в о л о в в в ы х о д н о й поток. StringReader и для
и
StringWriter,
созданные
System.
на
базе
классов
Sys
соответственно предназначены
и записи т е к с т о в ы х строк
Перечисления П р и р а б о т е с п о т о к а м и , ф а й л а м и и каталогами вам т а к ж е п о т р е б у ю т с я FileAccess, FileShare и Они с о д е р ж а т о п р е д е л е н и я к о н с т а н т , н у ж н ы х для о п р е д е л е н и я р е ж и м о в с о з д а н и я и о т к р ы т и я ф а й л о в , р е ж и м о в с о в м е с т н о г о д о с т у п а к файлу, а т а к ж е п о з и ц и и при п р о и з в о л ь н о м д о с т у п е к ф а й л а м . Все эти к о н с т а н т ы мы будем р а с с м а т р и в а т ь д а л ь ш е по мере и з л о ж е н и я м а т е р и а л а этой г л а в ы .
Работа со стандартными потоками П р и л о ж е н и ю С# д о с т у п н ы 3 с т а н д а р т н ы х п о т о к а , к о т о р ы е всегда о т к р ы т ы : стандарт¬ ный п о т о к ввода, с т а н д а р т н ы й п о т о к в ы в о д а и с т а н д а р т н ы й п о т о к в ы в о д а с о о б щ е н и й об о ш и б к а х . п е р е ч и с л е н н ы е в ы ш е п о т о к и о п р е д е л е н ы в классе C o n s o l e . В ы м о ж е т е полу чить на них с с ы л к и с п о м о щ ь ю с т а т и ч е с к и х с в о й с т в I n , O u t и E r r o r .
Стандартный поток ввода С т а н д а р т н ы й п о т о к ввода, к о т о р ы й м о ж н о п о л у ч и т ь при п о м о щ и с т а т и ч е с к о г о свой¬ ства I n , о п р е д е л е н как с т а т и ч е с к и й о б ъ е к т класса T e x t R e a d e r . Э т о т класс с о д е р ж и т м е т о д ы , п р е д н а з н а ч е н н ы е для в в о д а о т д е л ь н ы х с и м в о л о в , строк и б л о к о в текста. В о т как п р о г р а м м а м о ж е т п о л у ч и т ь ссылку на с т а н д а р т н ы й п о т о к ввода: TextReader 468
А. В. Фролов, Г. В. Фролов. Язык С#. Самоучитель
Чтобы ввести д а н н ы е из с т а н д а р т н о г о потока ввода (по у м о л ч а н и ю с в я з а н н о г о с к л а в и а т у р о й к о м п ь ю т е р а ) , и с п о л ь з у й т е один из м е т о д о в , о п р е д е л е н н ы х д л я этого в классе T e x t R e a d e r . В предыдущих примерах программ мы использовали метод ReadLine, ввод с и м в о л о в и з с т а н д а р т н о г о потока ввода. А н а л о г и ч н ы й м е т о д п р е д у с м о т р е н и в классе T e x t R e a d e r : string
s
Этот оператор вводит текстовую строку с клавиатуры и сохраняет ее в переменной s. В классе T e x t R e a d e r и м е ю т с я и д р у г и е м е т о д ы , п р е д н а з н а ч е н н ы е для ввода от¬ д е л ь н ы х с и м в о л о в и блока с и м в о л о в . R e a d , не и м е ю щ и й п а р а м е т р о в , читает один с и м в о л и в о з в р а щ а е т его как значение
типа
int.
При в о з н и к н о в е н и и
Если
символов
больше
нет,
о ш и б о к создается и с к л ю ч е н и е
метод
возвращает значение
IOException,
-1.
о б р а б о т к у кото¬
рого н е о б х о д и м о п р е д у с м о т р е т ь .
Стандартный поток вывода С т а н д а р т н ы й п о т о к в ы в о д а O u t создан н а базе класса
предназначенно¬
го для ф о р м а т и р о в а н н о г о в ы в о д а д а н н ы х р а з л и ч н о г о типа с ц е л ь ю их в и з у а л ь н о г о о т о б р а ж е н и я в виде т е к с т о в о й с т р о к и . Для того ч т о б ы п о л у ч и т ь с с ы л к у на с т а н д а р т н ы й п о т о к в ы в о д а , и с п о л ь з у й т е свой¬ ство TextWriter Далее, используя полученную ссылку, можно выводить данные с п о м о щ ь ю одного из м е т о д о в , о п р е д е л е н н ы х в классе T e x t W r i t e r , н а п р и м е р с п о м о щ ь ю х о р о ш о из вестного вам п о п р е д ы д у щ и м п р и м е р а м п р о г р а м м метода W r i t e L i n e : в
стандартный
поток
Этот м е т о д а н а л о г и ч е н м е т о д у C o n s o l e В классе T e x t W r i t e r о п р е д е л е н о н е с к о л ь к о р е а л и з а ц и й м е т о д а W r i t e
с пара¬
м е т р а м и р а з л и ч н ы х т и п о в . В о т н е к о т о р ы е и з них: public public public public public public public public public public public public
virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual
void void void void void void void void void void void void
Write Write Write Write Write Write Write Write
Глава 14. Файлы и потоки
469
Как в и д и т е , вы м о ж е т е записать в с т а н д а р т н ы й поток в ы в о д а т е к с т о в о е представ¬ л е н и е д а н н ы х р а з л и ч н о г о типа, в том числе и класса М е т о д W r i t e L i n e а н а л о г и ч е н методу W r i t e , о т л и ч а я с ь л и ш ь т е м , что о н добав¬ ляет к з а п и с ы в а е м о й в п о т о к строке с и м в о л п е р е х о д а на с л е д у ю щ у ю строку: public public public public public public public public public public public public
virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual
void void void void void void void void void void void void
WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine
Р е а л и з а ц и я м е т о д а W r i t e L i n e без п а р а м е т р о в з а п и с ы в а е т т о л ь к о с и м в о л перехо¬ да на с л е д у ю щ у ю строку.
Стандартный поток вывода сообщений об ошибках С т а н д а р т н ы й п о т о к в ы в о д а с о о б щ е н и й об о ш и б к а х E r r o r , так же как и с т а н д а р т н ы й п о т о к в ы в о д а O u t , создан н а базе класса ний
об
ошибках
вы
можете
использовать
П о э т о м у для записи с о о б щ е только
что
описанные
методы W r i t e
и WriteLine. Вот как п р о г р а м м а м о ж е т п о л у ч и т ь ссылку на с т а н д а р т н ы й п о т о к в ы в о д а сообще¬ ний об о ш и б к а х : TextWriter По у м о л ч а н и ю сообщения об ошибках выводятся на то же самое устройство вывода ( к о н с о л ь ) , на которое в ы в о д я т с я и с т р о к и , з а п и с ы в а е м ы е в с т а н д а р т н ы й поток вывода.
Программа StdStreams Приложение StdStreams демонстрирует способы работы со стандартными ввода, в ы в о д а и в ы в о д а с о о б щ е н и й об о ш и б к а х ( л и с т и н г 14.1). Листинг using using
14.1.
Файл
System;
namespace class
470
потоками
StdStreams StdStreamsApp
A
Фролов, Г. В. Фролов. Язык
Самоучитель
static
void
TextWriter TextWriter
args) twOut
в "Запись
в
стандартный
стандартный поток
поток
сообщений
об
TextReader любую string
строку:
s ввели
строку
s)
П о л у ч и в у п р а в л е н и е , м е т о д M a i n н а ш е й п р о г р а м м ы п о л у ч а е т с с ы л к и н а стандарт¬ ный п о т о к в ы в о д а и с т а н д а р т н ы й п о т о к в ы в о д а с о о б щ е н и й об о ш и б к а х : TextWriter TextWriter
twOut twErr
Д а л е е п р о г р а м м а в ы в о д и т в эти п о т о к и д в е т е к с т о в ы е с т р о к и : в в
стандартный стандартный
поток поток сообщений
об
П о с л е этого н а ш а п р о г р а м м а п о л у ч а е т с с ы л к у на с т а н д а р т н ы й п о т о к ввода, кото¬ рой по у м о л ч а н и ю связан с к л а в и а т у р о й : TextReader О т о б р а з и в на э к р а н е с т р о к у п р и г л а ш е н и я , п р о г р а м м а в в о д и т из с т а н д а р т н о г о пото¬ ка ввода т е к с т о в у ю строку и з а п и с ы в а е т ее в п е р е м е н н у ю s: любую string
строку:
s
Далее введенная строка отображается на консоли следующим образом: ввели
строку
s)
Для з а в е р ш е н и я р а б о т ы п р о г р а м м ы н е о б х о д и м о н а ж а т ь к л а в и ш у Enter. Как в и д и т е , п р и е м ы р а б о т ы со с т а н д а р т н ы м и п о т о к а м и ввода и в ы в о д а н е с л о ж н ы .
Глава 14. Файлы и потоки
471
Создание потоков, связанных с файлами Если вам н у ж н о с о з д а т ь входной или в ы х о д н о й п о т о к , с в я з а н н ы й с л о к а л ь н ы м фай лом,
содержащим
двоичные
Writer и
данные,
следует
воспользоваться
и з б и б л и о т е к и Microsoft
ч т е н и я из файла или записи в файл
классами
Binary-
F r a m e w o r k . Что ж е
д а н н ы х , то для р е ш е н и я этой задачи
о б ы ч н о и с п о л ь з у ю т с я классы В л ю б о м случае, перед тем как читать данные из файла или записывать их в файл, не¬ обходимо
в ы п о л н и т ь о п е р а ц и ю открытия потока, связанного с файлом.
Когда работа
с файлом окончена, соответствующий поток необходимо закрыть я в н ы м образом. О б р а щ а е м в а ш е в н и м а н и е на т о , что система с б о р к и м у с о р а , в с т р о е н н а я в среду и с п о л н е н и я Microsoft
F r a m e w o r k , с л е д и т за и с п о л ь з о в а н и е м т о л ь к о о б ъ е к т о в , на¬
х о д я щ и х с я в о п е р а т и в н о й п а м я т и , т а к и х , как п е р е м е н н ы е , м а с с и в ы , э к з е м п л я р ы объ¬ е к т о в , с о з д а н н ы х на базе классов и т. п. Что же касается п о т о к о в , с в я з а н н ы х с файла¬ м и , то вы д о л ж н ы сами с л е д и т ь за их о т к р ы т и е м и з а к р ы т и е м .
Открытие потока FileStream Для
работы
ток к л а с с а
с двоичными FileStream,
файлами
ваша п р о г р а м м а д о л ж н а вначале создать
воспользовавшись
соответствующим
по¬
конструктором,
например: FileStream
fs
new
В качестве п е р в о г о п а р а м е т р а к о н с т р у к т о р у н е о б х о д и м о п е р е д а т ь п о л н ы й путь к файлу или имя ф а й л а , а в к а ч е с т в е в т о р о г о р е ж и м о т к р ы т и я потока (табл. 14.1). 14.1.
Режимы
открытия
файла
FileMode
Режим
Описание Если файл с у щ е с т в у е т , он о т к р ы в а е т с я . Т е к у щ а я п о з и ц и я уста¬ н а в л и в а е т с я на к о н е ц файла. Если у к а з а н н о г о ф а й л а нет, то он с о з д а е т с я . Этот р е ж и м м о ж н о и с п о л ь з о в а т ь т о л ь к о с о в м е с т н о с режимом доступа F i l e A c c e s s П р и п о п ы т к е ч т е н и я и з файла, о т к р ы т о г о п о д о б н ы м о б р а з о м , возникает исключение
Create
ОС д о л ж н а создать н о в ы й файл. Если у к а з а н н ы й файл уже с у щ е с т в у е т , он б у д е т п е р е з а п и с а н
CreateNew
ОС д о л ж н а
н о в ы й файл. Если у к а з а н н ы й файл уже
существует, возникнет исключение Open
IOException
Т р е б у е т с я о т к р ы т ь с у щ е с т в у ю щ и й файл. Н е о б х о д и м д о с т у п Если т р е б у е м ы й файл не н а й д е н , в о з н и к а е т и с к л ю ч е н и е
-
FoundExcepti o 472
В. Фролов, Г. В. Фролов. Язык С#. Самоучитель
Режим
Описание
OpenOrCreate
Если указанный файл он должен быть открыт. В противном случае ОС должна создать и открыть указанный файл
Truncate
Требуется о т к р ы т ь с у щ е с т в у ю щ и й файл. П о с л е о т к р ы т и я файл обрезается до нулевой д л и н ы , при этом все ранее х р а н и в ш и е с я в нем д а н н ы е п р о п а д а ю т . П р и п о п ы т к е ч т е н и я и з файла, о т к р ы т о г о п о д о б н ы м о б р а з о м , возникает исключение
В з а в и с и м о с т и от т о г о , для какой цели создается файл, вы м о ж е т е в ы б р а т ь тот или иной р е ж и м о т к р ы т и я потока. Н а п р и м е р , если п о т о к , с в я з а н н ы й с ф а й л о м , о т к р ы в а е т ся т о л ь к о для ч т е н и я , в ы б и р а й т е р е ж и м В этом случае будет от¬ крыт с у щ е с т в у ю щ и й файл. Если же файл о т к р ы в а е т с я для з а п и с и , то вы м о ж е т е л и б о о т к р ы т ь с у щ е с т в у ю щ и й ф а й л , п е р е з а п и с а в его с о д е р ж и м о е или д о п и с а в в него н о в ы е д а н н ы е , а т а к ж е создать н о в ы й файл. В классе F i l e S t r e a m с у щ е с т в у е т н е с к о л ь к о к о н с т р у к т о р о в , п о з в о л я ю щ и х о п р е д е л и т ь не т о л ь к о путь к файлу, но и р е ж и м его о т к р ы т и я , д о с т у п к файлу и р е ж и м с о в м е с т н о г о и с п о л ь з о в а н и я файла: FileStream FileStream
new new
О с т а н о в и м с я на р е ж и м е д о с т у п а к файлу, п е р е д а в а е м о м п р и в е д е н н ы м в ы ш е конст¬ р у к т о р а м ч е р е з третий п а р а м е т р (табл. 14.2). Р е ж и м д о с т у п а задается как с т а т и ч е с к а я к о н с т а н т а класса F i l e A c c e s s . Таблица
14.2.
Режимы
доступа
FileAccess
Режим Read
Тип
доступа
Д о с т у п т о л ь к о на чтение Д о с т у п на ч т е н и е и запись
Write
Доступ на запись
При необходимости программа может задать режим совместного использования файла, когда для одного и того же файла одновременно создается несколько потоков. Этот режим задается при помощи статических констант класса F i l e S h a r e (табл. 14.3). Таблица
14.3.
Режимы
Режим
FileShare
совместного Тип
доступа
Inheritable
Идентификатор файла может наследоваться д о ч е р н и м процессом
None
З а п р е т с о в м е с т н о г о и с п о л ь з о в а н и я файла. Л ю б ы е дополни¬ т е л ь н ы е з а п р о с ы на о т к р ы т и е файла будут з а п р е щ е н ы то тех п о р , пока файл не будет закрыт
Read
Д о п у с к а ю т с я д о п о л н и т е л ь н ы е запросы н а о т к р ы т и е файла для чтения
ReadWrite
Д о п у с к а ю т с я д о п о л н и т е л ь н ы е з а п р о с ы на ч т е н и е и запись
Wr i
Д о п у с к а ю т с я д о п о л н и т е л ь н ы е з а п р о с ы на запись
Глава 14. Файлы и потоки
473
Открытие потоков
и BinaryReader
И т а к , в а ш а п р о г р а м м а создала поток на базе файла, в о с п о л ь з о в а в ш и с ь для этого клас сом
Д а л е е н а базе п о л у ч е н н о г о т а к и м о б р а з о м п о т о к а н е о б х о д и м о соз
дать потоки к л а с с о в B i n a r y W r i t e r и B i n a r y R e a d e r , п р е д н а з н а ч е н н ы е соответст¬ венно для з а п и с и в файл и чтения из файла д в о и ч н ы х д а н н ы х : BinaryWriter BinaryReader
br
new new
Р а б о т а с п о т о к а м и B i n a r y W r i t e r и B i n a r y R e a d e r будет о п и с а н а чуть позже.
Закрытие потоков П о с л е того как п р о г р а м м а з а в е р ш и л а работу с п о т о к а м и , она д о л ж н а их з а к р ы т ь . Для з а к р ы т и я п о т о к о в и с п о л ь з у й т е м е т о д C l o s e .
З а к р ы в а й т е потоки в п о р я д к е ,
о б р а т н о м о т к р ы т и ю . Если в ы вначале о т к р ы л и п о т о к F i l e S t r e a m , на его базе потоки класса B i n a r y W r i t e r и нужно в обратном
порядке:
вначале закройте
а затем создали
то з а к р ы в а т ь п о т о к и потоки B i n a r y W r i t e r
и
R e a d e r , а затем п о т о к F i l e S t r e a m : F i l e S t r e a m fs B i n a r y W r i t e r bw B i n a r y R e a d e r br Работа
с
new new new
потоками
Запись двоичных данных Д л я з а п и с и д в о и ч н ы х д а н н ы х в п о т о к B i n a r y W r i t e r п р е д у с м о т р е н о н е с к о л ь к о пе¬ регруженных методов W r i t e , public public public public public public public public public public public public
474
virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual
приведенных ниже:
void void void void void void void void void void void void А. В.
Г.
Язык
Самоучитель
public public public public public
v i r t u a l void virtual void virtual void virtual void Write (byte virtual void
int, int,
int)
С п о с о б п р и м е н е н и я б о л ь ш и н с т в а п е р е ч и с л е н н ы х м е т о д о в и н т у и т и в н о я с е н : для записи д а н н ы х т о г о или иного типа в файл вам н у ж н о п е р е д а т ь ссылку на э к з е м п л я р д а н н ы х в качестве п а р а м е т р а методу W r i t e . В з а в и с и м о с т и от типа д а н н ы х компиля¬ тор в ы б е р е т н у ж н у ю п е р е г р у ж е н н у ю в е р с и ю этого метода. При з а п и с и в д в о и ч н ы й п о т о к т е к с т о в о й строки s t r i n g она будет п р е д в а р я т ь с я п р е ф и к с о м , с о д е р ж а щ и м з н а ч е н и е д л и н ы строки. С т р о к а будет з а п и с а н а в к о д и р о в к е ASCII. П о с л е д н и е два п е р е г р у ж е н н ы х м е т о д а W r i t e п о з в о л я ю т з а п и с а т ь в п о т о к м а с с и в с о о т в е т с т в е н н о д в о и ч н ы х байтов и с и м в о л о в U N I C O D E . В т о р о й п а р а м е т р м е т о д а за¬ д а е т н а ч а л ь н ы й индекс блока д а н н ы х в м а с с и в е , а т р е т и й — р а з м е р блока (соответст¬ венно в байтах и с и м в о л а х ) .
Чтение двоичных данных Чтобы прочитать данные из потока
н у ж н о и с п о л ь з о в а т ь один и з ме
т о д о в , с п е ц и а л ь н о п р е д у с м о т р е н н ы х д л я этого в классе B i n a r y R e a d e r . М ы п р и в е л и к р а т к и й с п и с о к э т и х м е т о д о в в табл.
14.4.
Таблица
данных
14.4.
Методы
Метод
для
чтения
Тип
ReadBoolean
класса
данных,
BinaryReader
считываемых
из
потока
Один о б ъ е к т т и п а B o o l e a n
ReadByte
1 байт д а н н ы х
ReadBytes
Ч т е н и е з а д а н н о г о к о л и ч е с т в а байтов д а н н ы х Один т е к с т о в ы й с и м в о л
ReadChars
Чтение заданного количества текстовых символов Д е с я т и ч н о е з н а ч е н и е р а з м е р о м 16 байт З н а ч е н и е с п л а в а ю щ е й д е с я т и ч н о й т о ч к о й р а з м е р о м 4 байта З н а ч е н и е с п л а в а ю щ е й д е с я т и ч н о й т о ч к о й р а з м е р о м 8 байт
ReadSByte
Ч и с л о со з н а к о м р а з м е р о м 1 байт Ч и с л о со з н а к о м р а з м е р о м 2 байта Ч и с л о со знаком р а з м е р о м 4 байта Ч и с л о со з н а к о м р а з м е р о м 8 байт Ч и с л о без знака р а з м е р о м 2 байта Ч и с л о без знака р а з м е р о м 4 байта Ч и с л о без знака р а з м е р о м 8 байт
ReadString
Глава
Текстовая строка с префиксом длины
14.
Файлы
и
потоки
475
Программа Binary Программа
14.2) д е м о н с т р и р у е т н е к о т о р ы е п р и е м ы р а б о т ы с д в о и ч н ы м и
файлами при п о м о щ и классов
Листинг
14.2.
using using
System;
namespace
Binary
private static
и
const
string
testFile
void
args)
уже
существует",
return;
FileStream
fs
BinaryWriter
bw
for (uint i
0;
fs
string
for
new
i
20;
i++)
new
BinaryReader
476
new
br
new
s
(uint
i
0;
i
20;
i++)
А. В. Ф р о л о в , Г. В Ф р о л о в .
успешно
В самом начале своей работы
программа проверяет существование файла с именем
mydata.dat, и с п о л ь з у я д л я э т о г о м е т о д E x i s t s к л а с с а F i l e :
уже
Класс
существует",
предназначенный для работы с файлами и каталогами, мы рассмотрим
позже. П о к а же мы только з а м е т и м , что д а н н ы й
фрагмент кода проверяет существова
ние файла mydata.dat в каталоге, который был т е к у щ и м при запуске нашей п р о г р а м м ы на выполнение. Если такой д ы д у щ е м запуске этой об
файл там уже существует ( н а п р и м е р , был создан при пре
программы),
программа завершает свою работу с сообщением
ошибке. Если же в т е к у щ е м каталоге нет ф а й л а с и м е н е м
п р о г р а м м а с о з д а е т е г о , вы¬
зывая для этого конструктор класса F i l e S t r e a m с соответствующими параметрами: FileStream Так как мы
new указали
режим
создания
CreateNew,
потока
то
новый
файл будет создан в том случае, если файла с таким именем нет в т е к у щ е м каталоге. После
создания
потока
класса BinaryWriter
FileStream,
связанного
с
предназначенный для записи bw
файлом,
мы
создаем
поток bw
в файл двоичных данных:
new
На с л е д у ю щ е м шаге программа записывает в поток текстовую строку:
Далее п р о г р а м м а з а п и с ы в а е т в п о т о к bw 20 чисел типа for (uint i
0;
i
20;
После завершения записи
i+ + )
оба потока закрываются
методом
Close:
477
Н а с л е д у ю щ е м этапе наша п р о г р а м м а с ч и т ы в а е т с о д е р ж и м о е т о л ь к о что записан¬ ного файла и о т о б р а ж а е т его на к о н с о л и . Для этого она вначале о т к р ы в а е т п о т о к fs класса F i l e S t r e a m , у к а з ы в а я , что файл д о л ж е н быть о т к р ы т т о л ь к о для ч т е н и я : fs
new Режим чтения задается константой Далее н а базе п о т о к а f s п р о г р а м м а с о з д а е т поток
BinaryReader
br
класса
new
Этот п о т о к м о ж е т быть и с п о л ь з о в а н д л я ч т е н и я и з файла д в о и ч н ы х д а н н ы х . П е р в ы м д е л о м п р о г р а м м а читает и о т о б р а ж а е т на консоли т е к с т о в у ю строку, запи¬ с а н н у ю в файл: string
s
Далее она в ц и к л е с ч и т ы в а е т 20 чисел и т а к ж е о т о б р а ж а е т их на к о н с о л и : for
(uint i
0;
i
20;
i+ + )
З а в е р ш и в р а б о т у с п о т о к а м и , п р о г р а м м а з а к р ы в а е т их:
Д а л е е она в ы в о д и т на к о н с о л ь с о о б щ е н и е об у с п е ш н о м с о з д а н и и файла и ж д е т , ко¬ гда п о л ь з о в а т е л ь н а ж м е т к л а в и ш у Enter, ч т о б ы з а в е р ш и т ь с в о ю работу: успешно В о т что п о я в и т с я на консоли после з а п у с к а п р о г р а м м ы : Text string 0 1 2 3 4 5 б 7 8 9 10 11 12 13 14 15 16 17 18 19 Файл у с п е ш н о с о з д а н Содержимое файла, созданного нашей программой, показано на рис.
Рис. 478
14.5.
Содержимое
созданного
двоичного
А В Фролов, Г. В.
14.5.
файла Самоучитель
внимание, что в самом начале файла находится байт со значением ОхВ, вслед за которым идет текстовая строка в кодировке ASCII. Этот байт задает длину строки. В с л е д за строкой идут 20 ч и с е л , к а ж д о е из к о т о р ы х з а н и м а е т 4 байта.
Работа с текстовыми файлами Хотя
рассмотренные
в
предыдущем разделе
потоки
FileStream,
BinaryWriter
и B i n a r y R e a d e r м о ж н о и с п о л ь з о в а т ь для записи в файлы и чтения из ф а й л о в тек¬ с т о в ы х строк, л у ч ш е п р м е н я т ь с п е ц и а л ь н о п р е д н а з н а ч е н н ы е для э т о г о с р е д с т в а . Р е ч ь идет о п о т о к а х классов
и S t r e a m R e a d e r . Эти п о т о к и ч р е з в ы ч а й н о
п р о с т ы в и с п о л ь з о в а н и и и у д о б н ы для р а б о т ы с т е к с т о в ы м и ф а й л а м и . Основные приемы использования потоков S t r e a m W r i t e r и
S t r e a m R e a d e r де¬
м о н с т р и р у ю т с я в п р о г р а м м е , и с х о д н ы й текст которой п р и в е д е н в л и с т и н г е 14.3. Листинг
14.3.
using using
System; System.
namespace class
Файл
TextFile TextFileApp
private static
const
string
testFile
void
args)
уже
StreamWriter
sw
("Число
StreamReader
String if ( s t r
sr
охотник желает \ " П и \ " равно
з н а т ь , где примерно
сидит 3.1415926);
OpenText
str
Глава 14. Файлы и потоки
null)
479
("Файл
успешно
Как и п р е д ы д у щ а я п р о г р а м м а (см. л и с т и н г 14.2), н а ш а новая п р о г р а м м а сразу по¬ сле з а п у с к а п р о в е р я е т с у щ е с т в о в а н и е р а б о ч е г о ф а й л а в т е к у щ е м к а т а л о г е : private
const
string
testFile
if уже
Если файл с и м е н е м m y d a t a . t x t с у щ е с т в у е т в т е к у щ е м к а т а л о г е , п р о г р а м м а завер¬ шает с в о ю р а б о т у с с о о б щ е н и е м об о ш и б к е . В п р о т и в н о м случае п р о г р а м м а создает т е к с т о в ы й файл и о т к р ы в а е т п о т о к для р а б о т ы с ним класса StreamWriter Как в классе
видите, File.
sw эта
CreateText операция
Аналогичного
выполняется эффекта
методом
можно
было
CreateText, бы
достичь
определенным и
с
помощью
с л е д у ю щ е г о к о н с т р у к т о р а класса StreamWriter Первый
sw
параметр
new этого
StreamWriter ( t e s t F i l e , конструктора определяет полный
путь
к открываемому
файлу. Если з н а ч е н и е в т о р о г о п а р а м е т р а р а в н о t r u e , н о в ы е д а н н ы е б у д у т д о б а в л е н ы к файлу, а если
с о д е р ж и м о е файла будет п е р е з а п и с а н о .
Для о т к р ы т и я т е к с т о в о г о файла на запись вы м о ж е т е и с п о л ь з о в а т ь л ю б о й из этих к о н с т р у к т о р о в . З а м е т и м , что в классе р ы , п о з в о л я ю щ и е , н а п р и м е р , задать
имеются и другие конструкто кодировку текстовых символов,
в файл. После того как поток
открыт, программа может записывать в него
текстовые строки, пользуясь х о р о ш о известными вам ("Число
охотник желает знать, где \ " П и \ " равно примерно
и WriteLine: сидит 3.1415926);
К о г д а запись н о в ы х д а н н ы х в п о т о к з а в е р ш е н а , н е о б х о д и м о з а к р ы т ь п о т о к мето дом
Close:
о чтении д а н н ы х из т е к с т о в о г о файла. А В. Фролов. Г В Фролов.
Самоучитель
П р е ж д е всего п р о г р а м м а д о л ж н а о т к р ы т ь поток класса к файлу. Это м о ж н о сделать м е т о д о м StreamReader
привязав
sr
Вы м о ж е т е также о т к р ы т ь поток и п р и в я з а т ь его к файлу с п о м о щ ь ю к о н с т р у к т о р а класса
StreamReader:
StreamReader
sr
new
Далее н а ш а п р о г р а м м а с ч и т ы в а е т т е к с т о в ы е строки из файла, в ы з ы в а я в ц и к л е ме¬ тод R e a d L i n e :
String if str
str null)
Этот метод в о з в р а щ а е т п р о ч и т а н н у ю строку или з н а ч е н и е n u l l при д о с т и ж е н и и к о н ц а файла. П о с л е з а в е р ш е н и я р а б о т ы с п о т о к о м S t r e a m R e a d e r его с л е д у е т з а к р ы т ь м е т о д о м
Таким о б р а з о м , н а ш а п р о г р а м м а з а п и с ы в а е т две строки в файл, а затем ч и т а е т их оттуда и о т о б р а ж а е т на к о н с о л и : Каждый о х о т н и к ж е л а е т з н а т ь , г д е с и д и т Число "Пи" р а в н о п р и м е р н о 3 , 1 4 1 5 9 2 6 . Файл у с п е ш н о с о з д а н
фазан!
Выбор кодировки И с т о р и ч е с к и с л о ж и л о с ь так, что з а в с ю и с т о р и ю с у щ е с т в о в а н и я к о м п ь ю т е р о в для представления текстовых символов использовались самые разные кодировки. Когда ваша п р о г р а м м а создает ф а й л ы , она д о л ж н а в ы б р а т ь к а к у ю - т о к о н к р е т н у ю к о д и р о в к у текстовых символов. Те из в а с , кому, как и а в т о р а м этой к н и г и , п о с ч а с т л и в и л о с ь р а б о т а т ь на « б о л ь ш и х » к о м п ь ю т е р а х серии ЕС (аналоги к о м п ь ю т е р о в I B M 3 6 0 / 3 7 0 ) , з н а к о м ы с так н а з ы в а е мой к о д и р о в к о й E B C D I C . В ней п р е д с т а в л я л и с ь л а т и н с к и е с и м в о л ы , с и м в о л ы кирил¬ л и ц ы , а также знаки п у н к т у а ц и и и у п р а в л я ю щ и е знаки. Что же касается с о в р е м е н н ы х п е р с о н а л ь н ы х к о м п ь ю т е р о в , то для п р е д с т а в л е н и я т е к с т о в ы х с и м в о л о в р а з л и ч н ы х а л ф а в и т о в в них и с п о л ь з у ю т с я кодовые страницы (Code Pages) и к о д и р о в к а U N I C O D E . Р а с с к а ж е м об этом п о д р о б н е е . Глава 16 Язык
Файлы и Самоучитель
481
Кодовые страницы До
ОС Microsoft W i n d o w s 95
и Microsoft W i n d o w s ш и р о к о
применялась
так н а з ы в а е м а я м о д е л ь к о д о в ы х с т р а н и ц . В этой модели к а ж д о м у с и м в о л у ставилось в с о о т в е т с т в и е ч и с л о в д и а п а з о н е от 0 до 2 5 5 . Таким о б р а з о м , для п р е д с т а в л е н и я одно¬ го с и м в о л а и с п о л ь з о в а л с я 1 байт. Каким способом устанавливается соответствие? Для ОС M S D O S было известно по крайней мере н е с к о л ь к о таких с п о с о б о в , осно¬ ванных
на
применении
различных
таблиц
кодировок:
кодировка
ASCII,
основная
и а л ь т е р н а т и в н а я , а т а к ж е н е к о т о р ы е другие. Компания
Microsoft
стандартизовала
таблицы
кодировок,
назвав
их
кодовыми
с т р а н и ц а м и и п р и с в о и в к а ж д о й странице свой н о м е р . П е р в ы е версии M S D O S работа¬ ли с р а с ш и р е н н ы м н а б о р о м с и м в о л о в I B M , в к о т о р о м о т с у т с т в о в а л и с и м в о л ы кирил¬ л и ц ы (рис. 14.6).
ее ее за
es
ez ез о
в
t 1
2
е
й
В
р V
q
к
и X
Ь г
6
а?
С S
с
S 4
t
6
а а
U
5 5
6
7
Е
г
G
и
Я
t
V.
Т
s
о 4
D T
•
it 3
V
У
I V
H X
i
К
F0
J
К
z
В
с
fl т
т
1
р
Г
Рис.
•
л А
-
V
p
1 L 11
к
9
X
N
4-
И
L Ч 1
0 •
t £
V
л
я
t
—
Ш
L I
Г
Г
i
J 0
ft V
т
•
Расширенный набор
r
I
m
ft
•
•
символов
IBM
Для т о г о ч т о б ы и с п р а в и т ь п о л о ж е н и е , в свое в р е м я было р а з р а б о т а н о немало ути¬ лит,
п р е д н а з н а ч е н н ы х д л я загрузки
символов
кириллицы
в
память
видеоадаптера,
а т а к ж е для п е р е к л ю ч е н и я р а с к л а д о к к л а в и а т у р ы . В ОС M S D O S версии 4.01 в п е р в ы е п о я в и л а с ь с т а н д а р т н а я к о д о в а я с т р а н и ц а с но м е р о м 8 6 6 , с о д е р ж а щ а я с и м в о л ы к и р и л л и ц ы (рис. 14.7) ватели
С тех пор р о с с и й с к и е пользо¬
к о м п ь ю т е р о в не м о г у т п о ж а л о в а т ь с я на т о , что к о м п а н и я Microsoft о б х о д и т
их с в о и м в н и м а н и е м , с о з д а в а я н о в ы е ОС и п р и к л а д н ы е п р о г р а м м ы .
482
А В . Фролов, Г.
01
02
о
в
05 •
38
7В
0
1
0 F
Й а
V.
4
Б
Е
я
S I a t А
Е X
с S с
Р й
Б
В
Г
Р
С 6
т
a
•
D t
§
*э в к ь
8Ь
t
-4
1
0?
е и
7 Г и
е
L
1
т
f
к
h
т Е
е
т
Ё
с в
I
Г
и
X
4
Набор
14.7.
9
I
J г
К
i
J z
к
К
X В
И
Й
ч
с и
й
X
1
fl
п
3
1 L
-
'•
к
V I
э
С
1 Л
Л
*
1
a
я
в
•
ч В
•
н э
0
ь и
н
0 J
•
I
ь
ч
символов
н 1
1
ft J
1
а
•
страницы
п
•
э
• •
D
кодовой
П Я
я
866
З а м е т и м , что для п р е д с т а в л е н и я с и м в о л о в к и р и л л и ц ы в ОС OS/2 применяется к о д и р о в к а , с о о т в е т с т в у ю щ а я к о д о в о й с т р а н и ц е 866. П о л ь з о в а т е л и ОС U n i x и L i n u x отдают предпочтение кодировке а пользователи компьютеров Apple Macin своей с о б с т в е н н о й к о д и р о в к е , н е с о в м е с т и м о й ни с о д н о й из п е р е ч и с л е н н ы х в ы ш е . В И н т е р н е т е есть н е м а л о у т и л и т , п р е д н а з н а ч е н н ы х для п е р е к о д и р о в а н и я тек¬ стовых файлов, подготовленных в самых разных кодировках. п о я в и л а с ь ОС Microsoft W i n d o w s 3.1, в ней т а к ж е были п р е д у с м о т р е н ы к о д о в ы е с т р а н и ц ы . К с о ж а л е н и ю , к о д о в ы е с т р а н и ц ы A N S I Microsoft W i n d o w s 3.1 были л и ш е н ы с и м в о л о в к и р и л л и ц ы (рис. 14.8). В1
ez
В7
j за
1
z
ft
в л ъ
ее аа 90
*3 с S с я
1 4 Т
а
е и
| в
*
F У
7
В
9
G
н И
I V
f
1 ¥
| I
i
Ъ
s
ь 14.8.
0
i
S
1
Кодовая
страница
t
J Е J
К
L Ч
к
•
1
Л
а
t |
t е
01
t
Ё
Ё
Ё
0
0 о 0 в
И
6
\ \
-
5 Е
t
0i
1
а
Ё
1
I В
t
I
а
ANSI
й
й
без
и
символов
кириллицы
Д л я ОС Microsoft W i n d o w s 3.1 было создано несколько популярных программ русифи¬ кации, устанавливающих ш р и ф т ы с символами кириллицы и переключатели клавиатур¬ ных раскладок. При этом применялась кодировка A NS I , показанная на рис. 14.9.
14. Файлы и
483
ее
06
07
7
Ос
1
10 га
в
1
р
А 0
10
Ч
г
*
4
5
6
С S с
F
Г
в
D Т А t
Е
в ь
н к
f
и
в
9
V
1
А Р а Р
ев
Рис. Программы
в
Ё Б С
с
14.9.
MSDOS,
в т
д
*
А
Ч и
звание
страниц
Е X е
И
N
0
| в X
1 Ж ц ж
э ч а ч
И III
К ъ
й
Л ы л
ъ
страница
запущенные
оригинальных
L
0
ANSI с
под
0 ю а
П Я п
э
символами
14.6 и
производителей
н э
ь м ь
управлением
пользовались кодовыми страницами DOS Equipment
J Z J
1 1 S
У
Кодовая
У 1
X
\
ав
V.
3
кириллицы
ОС
Microsoft W i n d o w s ,
14.7), к о т о р ы е п о л у ч и л и на
оборудования
OEM
(Original
Manufacturer).
С
р у с с к о й версии ОС Microsoft W i n d o w s 3.1
дартной. Соответствующая кодовая страница получила номер
эта к о д и р о в к а стала стан 1251.
К этому моменту было разработано огромное количество с символами
кириллицы,
коды
которых
находились
ш р и ф т о в True
в диапазоне
от СО до
FF.
И м е н н о эти ш р и ф т ы и с т а л и п о п у л я р н ы , р а с п р о с т р а н я я с ь в о с н о в н о м п и р а т с к и м и с п о с о б а м и в м е с т е с п р о г р а м м а м и р у с и ф и к а ц и и M i c r o s o f t W i n d o w s 3.1
или по от¬
дельности.
Недостатки модели кодовых страниц Х о т я на п е р в ы й в з г л я д м о ж е т п о к а з а т ь с я , что 1 байта в п о л н е д о с т а т о ч н о для п р е д с т а в л е н и я всех с и м в о л о в , это не всегда так. П р е д с т а в ь т е себе, что вам н е о б х о д и м о п о д г о т о в и т ь д о к у м е н т , с о д е р ж а щ и й п о м и м о л а т и н с к и х с и м в о л о в и с и м в о л о в к и р и л л и ц ы , н а п р и м е р , г р е ч е с к и е и н е м е ц к и е симво¬ л ы , а т а к ж е д о п о л н и т е л ь н ы е знаки. О ч е в и д н о , ч т о , о с т а в а я с ь в р а м к а х к о д о в о й стра¬ н и ц ы 1251 или к а к о й л и б о д р у г о й , т а к у ю задачу р е ш и т ь н е в о з м о ж н о . Ч т о б ы както выйти и з д а н н о й с и т у а ц и и , п р и х о д и т с я д о п о л н и т е л ь н о п р и м е н я т ь с п е ц и а л ь н ы е шриф¬ т ы , что у с л о ж н я е т п р о ц е с с о ф о р м л е н и я д о к у м е н т а . З а м е т и м т а к ж е , что в н е к о т о р ы х а л ф а в и т а х с л и ш к о м м н о г о с и м в о л о в , ч т о б ы и х коды м о ж н о было представить 1 байтом. В азиатских версиях Microsoft W i n d o w s п р и м е н я ю т с я м н о г о б а й т о в ы е н а б о р ы с и м в о л о в , где для п р е д с т а в л е н и я о д н о г о с и м в о л а и с п о л ь з у е т с я н е с к о л ь к о б а й т о в . Н а п р и м е р , в я п о н с к о й , к о р е й с к о й и ки¬ т а й с к о й в е р с и я х M i c r o s o f t W i n d o w s в ы м о ж е т е с т о л к н у т ь с я с 2 б а й т о в ы м и симво¬ л а м и ( н а б о р с и м в о л о в do uble-byte character set, или DBCS), к о т о р ы е не с л е д у е т пу тать с с и м в о л а м и U N I C O D E .
484
А В. Фролов, Г.
Язык
Самоучитель
Стандарт UNICODE С т а н д а р т U N I C O D E был п р е д л о ж е н н е к о м м е р ч е с к о й о р г а н и з а ц и е й U N I C O D E C o n s o r о б р а з о в а н н о й в 1991 г. В этом с т а н д а р т е для п р е д с т а в л е н и я к а ж д о г о с и м в о л а ис¬ пользуется 16 бит, т. е. 2 байта. С п о м о щ ь ю U N I C O D E м о ж н о з а к о д и р о в а т ь очень б о л ь ш о е к о л и ч е с т в о с и м в о л о в и з р а з н ы х а л ф а в и т о в , б л а г о д а р я чему о т п а д а е т необхо¬ д и м о с т ь п р и м е н я т ь к о д о в ы е с т р а н и ц ы . В д о к у м е н т а х U N I C O D E могут в с т р е ч а т ь с я одновременно латинские и греческие символы, символы кириллицы и технические З н а ч е н и я кодов U N I C O D E р а з д е л е н ы н а н е с к о л ь к о о б л а с т е й . Область с кодами от 0000 до 007F п р и м е н я е т с я т а к и м же о б р а з о м , как и п е р в а я по¬ л о в и н а р а с ш и р е н н о г о набора с и м в о л о в I B M , п о к а з а н н а я на рис. 14.6. Далее идут об¬ ласти, в которых расположены греческие символы, символы кириллицы, символы п у н к т у а ц и и и т е х н и ч е с к и е с и м в о л ы , а т а к ж е з а р е з е р в и р о в а н н ы е о б л а с т и . Для симво¬ лов к и р и л л и ц ы , н а п р и м е р , в ы д е л е н ы коды в д и а п а з о н е от 0 4 0 0 до 0451.
U n i c o d e в Microsoft W i n d o w s Я д р о Microsoft W i n d o w s г р а ф и ч е с к и й и н т е р ф е й с GDI этих ОС и ф а й л о вая система NTFS р е а л и з о в а н ы с и с п о л ь з о в а н и е м U N I C O D E . Тем не менее п р и л о ж е ния, з а п у щ е н н ы е в среде Microsoft W i n d o w s N T / 2 0 0 0 / X P , могут работать и с одной ко¬ д о в о й с т р а н и ц е й A N S I . Она в ы б и р а е т с я а в т о м а т и ч е с к и в з а в и с и м о с т и от т о г о , какая страна была в ы б р а н а п о л ь з о в а т е л е м при у с т а н о в к е О С . Приложения, применяющие кодовую страницу ANSI, перед вызовом некоторых ф у н к ц и й п р о г р а м м н о г о и н т е р ф е й с а Microsoft W i n d o w s N T в ы п о л н я ю т п р е о б р а з о в а н и е к о д и р о в к и в U N I C O D E с у ч е т о м номера к о д о в о й с т р а н и ц ы A N S I . Для того ч т о б ы та¬ кое п р е о б р а з о в а н и е в ы п о л н я л о с ь без о ш и б о к , п о л ь з о в а т е л ь д о л ж е н п р а в и л ь н о у к а з а т ь в п р и л о ж е н и и Regional Settings, п и к т о г р а м м а к о т о р о г о н а х о д и т с я в п а п к е Con trol Panel. Это т а к ж е н е о б х о д и м о и для п р а в и л ь н о й р а б о т ы п р о г р а м м M S - D O S , запу¬ щ е н н ы х п о д у п р а в л е н и е м Microsoft W i n d o w s N T / 2 0 0 0 / X P и и с п о л ь з у ю щ и х к о д о в у ю страницу O E M . Очень в а ж н о , что для р а б о т ы с д о к у м е н т а м и U N I C O D E т р е б у ю т с я ш р и ф т ы U N I C O D E . О д и н файл т а к о г о ш р и ф т а с о д е р ж и т н е с к о л ь к о н а б о р о в с и м в о л о в , соответ¬ ствующих разным областям кодов U N I C O D E . Разработчик шрифта может разместить в файле не все, а только н е к о т о р ы е н а б о р ы с и м в о л о в .
U N I C O D E в Microsoft W i n d o w s 95 В о т л и ч и е от Microsoft W i n d o w s я д р о и г р а ф и ч е с к и й и н т е р ф е й с Microsoft W i n d o w s 95 не и с п о л ь з у ю т U N I C O D E , а р а б о т а ю т с к о д о в ы м и с т р а н и ц а м и . Тем не м е нее в Microsoft W i n d o w s 95 п р е д у с м о т р е н а в о з м о ж н о с т ь д и н а м и ч е с к о г о п е р е к л ю ч е н и я н а б о р о в с и м в о л о в , а т а к ж е к л а в и а т у р н ы х р а с к л а д о к . Это п о з в о л я е т создавать доку¬ менты, содержащие одновременно символы из разных наборов. В составе Microsoft W i n d o w s 95 п о с т а в л я е т с я набор ш р и ф т о в U N I C O D E . Вы т а к ж е у с т а н о в и т ь в этой ОС и кириллические шрифты, разработанные для Microsoft W i n d o w s
глава 14. Файлы и
485
Кодировка текстовых потоков П о с л е т а к о г о к р а т к о г о в в е д е н и я в к о д и р о в к и с и м в о л о в мы р а с с м о т р и м с п о с о б ы выбо¬ ра к о д и р о в к и для т е к с т о в ы х п о т о к о в , с о з д а в а е м ы х в п р о г р а м м а х С#. К о д и р о в к а с и м в о л о в задается в к о н с т р у к т о р е , с о з д а ю щ е м в ы х о д н о й п о т о к д а н н ы х . Н а п р и м е р , при с о з д а н и и п о т о к а т е к с т о в ы х с т р о к S t r e a m W r i t e r м ы м о ж е м указать в т р е т ь е м п а р а м е т р е к о н с т р у к т о р а н у ж н у ю нам к о д и р о в к у : StreamWriter
Кодировка coding.
sw
new
указывается
false,
как
Возможные варианты
статическое кодировки
свойство
класса
с именами соответствующих статических
свойств п е р е ч и с л е н ы в табл. 14.5. Таблица
14.5.
Кодировки
текстовых
потоков
Кодировка
Описание
ASCII
К о д и р о в к а A S C I I без с и м в о л о в к и р и л л и ц ы , в которой для пред¬ с т а в л е н и я т е к с т о в ы х с и м в о л о в и с п о л ь з у ю т с я м л а д ш и е 7 бит байта
UNICODE
К о д и р о в к а U N I C O D E . Для п р е д с т а в л е н и я с и м в о л о в и с п о л ь з у е т с я бит (т. е. 2 байта) К о д и р о в к а U C S Transformation F o r m a t . П р и м е н я е т с я для представ¬ л е н и я с и м в о л о в U N I C O D E . В ней и с п о л ь з у ю т с я м л а д ш и е 7 бит данных То ж е , но для п р е д с т а в л е н и я с и м в о л о в U N I C O D E в ней использует¬ ся 8 бит д а н н ы х к о д и р о в к а A N S I (не п у т а й т е ее с к о д и р о в к о й ASCII). В этой к о д и р о в к е для п р е д с т а в л е н и я к а ж д о г о с и м в о л а и с п о л ь з у е т с я 8 бит д а н н ы х
З а м е т и м , что к о д и р о в к а с и м в о л о в м о ж е т быть задана т о л ь к о при с о з д а н и и потока. Если п о т о к у ж е с о з д а н , п р о г р а м м а м о ж е т о п р е д е л и т ь т е к у щ у ю к о д и р о в к у симво¬ л о в , в о с п о л ь з о в а в ш и с ь для э т о г о с в о й с т в о м E n c o d i n g : файла: Это с в о й с т в о д о с т у п н о т о л ь к о для ч т е н и я . В л и с т и н г е 14.4 мы п р и в е л и п р и м е р п р о г р а м м ы , с о з д а ю щ е й т е к с т о в ы й файл в ко¬ дировке UNICODE. Листинг using using
14.4. System;
namespace
486
Файл
Encoding
А В Фролов, Г В Фролов Язык
Самоучитель
class
EncodingApp
private static
const
string
testFile
void
args)
уже
StreamWriter
sw
new
существует",
StreamWriter ( t e s t F i l e ,
файла:
("Число
StreamReader
String if(str
о х о т н и к ж е л а е т з н а т ь , где \ " П и \ " равно примерно
сидит 3.1415926);
sr
str null)
Console
("Файл
успешно
От п р о г р а м м ы , и с х о д н ы й т е к с т к о т о р о й был п р и в е д е н р а н е е в она о т л и ч а е т с я т о л ь к о н а л и ч и е м д в у х с т р о к , п р и в е д е н н ы х н и ж е : StreamWriter
sw
new
глава 14. Файлы и потоки
StreamWriter
14.3,
false,
487
из этих с т р о к задает к о д и р о в к у с и м в о л о в U N I C O D E , а вторая ст к о д и р о в к у с и м в о л о в на к о н с о л и : Кодировка
отобража-
файла:
Каждый охотник желает знать, где сидит ф а з а н ! Число "Пи" равно примерно 3 , 1 4 1 5 9 2 6 . Файл успешно создан На рис.
14.10 мы п о к а з а л и
с о з д а н н о г о файла в виде д а м п а .
-
14.10.
Созданный
файл
в
кодировке
UNICODE
Обращаем ваше в н и м а н и е , что в самом начале файла находятся байты OxFF и OxFE. Это
п р е д н а з н а ч е н н а я для о п р е д е л е н и я п о р я д к а с л е д о в а н и я байтов в
16-
разрядных символах U N I C O D E . Д л я чего она н у ж н а ? С т а н д а р т U N I C O D E п р е д н а з н а ч е н д л я р а б о т ы н а р а з л и ч н ы х к о м п ь ю т е р н ы х плат ф о р м а х . О д н а к о р а з н ы е п р о ц е с с о р ы п р и м е н я ю т р а з л и ч н ы й п о р я д о к с л е д о в а н и я байтов в слове:
наиболее
значимый
байт м о ж е т
быть
расположен
как по
старшему,
так
и по м л а д ш е м у а д р е с у . Для правильной обработки текстового файла U N I C O D E на л ю б о й платформе прило¬ жение д о л ж н о знать порядок следования байтов, использованный в этом файле. Для обо¬ значения такого порядка и применяется 2-байтовая
Ее значение может быть
равно OxFEFF для прямого порядка следования байтов или OxFFEF для обратного.
Кодировка текстовых строк в двоичных потоках Как мы уже г о в о р и л и , д в о и ч н ы е потоки создаются с п о м о щ ь ю классов F i l e S t r e a m , BinaryWriter и BinaryReader.
С п о м о щ ь ю метода W r i t e программа может запи
сывать в д в о и ч н ы й поток не только числа, но и строки класса s t r i n g . С о о т в е т с т в у ю щ и й пример п р о г р а м м ы был приведен в разделе «Программа
488
(см. листинг 14.2).
А В Фролов. Г, В. Фролов. Язык
Самоучитель
Н а п о м н и м , что при записи с т р о к s t r i n g в д в о и ч н ы й поток они с н а б ж а ю т с я пре¬ ф и к с о м д л и н ы , вслед за к о т о р ы м идут байты с и м в о л о в . По у м о л ч а н и ю для представ¬ ления с и м в о л о в п р и м е н я е т с я к о д и р о в к а о д н а к о при н е о б х о д и м о с т и п р о г р а м м а м о ж е т ее и з м е н и т ь . При создании в ы х о д н о г о потока класса руктору кодировку текстовых символов: F i l e S t r e a m fs B i n a r y W r i t e r bw
t e r м ы м о ж е м указать конст
new F i l e S t r e a m ( t e s t F i l e , new
В д а н н о м случае мы у к а з а л и к о д и р о в к у U N I C O D E . П о с л е э т о г о запись строк в п о т о к в ы п о л н я е т с я как о б ы ч н о :
Для того ч т о б ы п р а в и л ь н о п р о ч и т а т ь т е к с т о в ы е строки из д в о и ч н о г о п о т о к а , при с о з д а н и и в х о д н о г о п о т о к а B i n a r y R e a d e r м ы т о ж е д о л ж н ы указать к о д и р о в к у : fs new F i l e S t r e a m ( t e s t F i l e , B i n a r y R e a d e r br new
FileMode.
П о с л е э т о г о м о ж н о будет п р о ч и т а т ь строку и з в х о д н о г о д в о и ч н о г о п о т о к а м е т о д о м string
s
Эта т е х н и к а д е м о н с т р и р у е т с я в л и с т и н г е 14.5. Листинг using using
14.5.
в
программе,
исходный
Файл
System;
namespace class private static
const
string
testFile
void
args)
уже
FileStream
fs
BinaryWriter
глава 14. Файлы и потоки
new bw
new B i n a r y W r i t e r
текст
которой
приведен
bw. for (uint i
fs
new
0;
for
20;
i+ + )
FileStream(testFile,
BinaryReader
string
i
br
new Encoding
s
(uint
i
0;
i
20;
i+ + )
успешно
К а к в ы г л я д и т т е к с т о в а я с т р о к а U N I C O D E в с о з д а н н о м нами д в о и ч н о м файле? В з г л я н и т е на р и с . где мы п о к а з а л и д а м п этого файла.
Рис.
490
14.11.
Текст
в
двоичном
потоке
имеет
кодировку
UNICODE
А В. Фролов, Г. В. Фролов. Язык
Самоучитель
О б р а т и т е в н и м а н и е , что п е р в ы й байт файла с о д е р ж и т д л и н у т е к с т о в о й с т р о к и . В с л е д за этим байтом идут 2 - б а й т о в ы е с и м в о л ы U N I C O D E . Если при чтении т а к о г о файла класса в ы з а б у д е т е ука зать к о д и р о в к у с и м в о л о в , результат м о ж е т быть н е о ж и д а н н ы м : T e x t s t r i n g О 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Файл у с п е ш н о с о з д а н В с а м о м д е л е , так как с и с т е м а не знает з а р а н е е , в какой к о д и р о в к е н а х о д я т с я сим¬ в о л ы , она будет и с п о л ь з о в а т ь к о д и р о в к у по у м о л ч а н и ю , т. е. A N S I . В р е з у л ь т а т е вме¬ сто о д н о г о с и м в о л а т е к с т о в о й строки мы у в и д и м с и м в о л и п р о б е л . Если же к о д и р о в к а у к а з а н а п р а в и л ь н о , п р о г р а м м а в ы в е д е т на к о н с о л ь с т р о к у без о ш и б о к в и с х о д н о м виде: Text string 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Файл у с п е ш н о с о з д а н
Буферизация потоков Для у с к о р е н и я о п е р а ц и й о б м е н а д а н н ы м и с д и с к о м часто п р и м е н я е т с я м е х а н и з м бу¬ ф е р и з а ц и и . Он основан на т о м , что прежде п о п а с т ь на д и с к , д а н н ы е п р е д в а р и т е л ь н о н а к а п л и в а ю т с я в б у ф е р е , р а с п о л о ж е н н о м в о п е р а т и в н о й п а м я т и . З а т е м , когда к диску нет о б р а щ е н и й , д а н н ы е из буфера п е р е п и с ы в а ю т с я на диск. У с к о р е н и е д о с т и г а е т с я за счет т о г о , что ф а к т и ч е с к и п р о г р а м м а п и ш е т д а н н ы е не на д и с к , а в о п е р а т и в н у ю п а м я т ь , о б л а д а ю щ у ю в ы с о к и м б ы с т р о д е й с т в и е м . Во в р е м я про¬ стоев к о м п ь ю т е р а , когда он не з а н я т д р у г о й р а б о т о й , д а н н ы е п е р е п и с ы в а ю т с я на диск. Р а з у м е е т с я , б у ф е р и з а ц и я дает у с к о р е н и е р а б о т ы п р о г р а м м не всегда, а т о л ь к о в тех с л у ч а я х , когда п р о г р а м м а п е р и о д и ч е с к и о б р а щ а е т с я к о д н и м и тем же ф р а г м е н т а м файла или в ы п о л н я е т п о с л е д о в а т е л ь н о е чтение или з а п и с ь файла.
Буферизация двоичных потоков Н а п о м н и м , что д в о и ч н ы е потоки с о з д а ю т с я при п о м о щ и классов F i l e S t r e a m , B i n a r y W r i t e r и B i n a r y R e a d e r . Потоки F i l e S t r e a m п о умолчанию являются б у ф е р и з о в а н н ы м и . Для з а д а н и я размера б у ф е р а м ы д о л ж н ы и с п о л ь з о в а т ь соответст¬ вующий перегруженный FileStream
fs
new 10000);
Через п е р в ы й п а р а м е т р этому к о н с т р у к т о р у п е р е д а е т с я путь к файлу, с к о т о р ы м с в я з ы в а е т с я поток. В т о р о й п а р а м е т р задает р е ж и м о т к р ы т и я файла. Третий и четвер¬ тый п а р а м е т р о п р е д е л я ю т с о о т в е т с т в е н н о р е ж и м д о с т у п а к файлу и р е ж и м совместно¬ го и с п о л ь з о в а н и я файла. Что же касается р а з м е р а буфера, то он задастся п о с л е д н и м , п я т ы м п а р а м е т р о м и у к а з ы в а е т с я в байтах. Глава 14. Файлы
491
С о з д а в т а к и м с п о с о б о м поток класса основе поток bw
о б р а з о в а т ь н а его
new
Д а л е е этот п о т о к м о ж н о и с п о л ь з о в а т ь для записи как о б ы ч н о , д о б а в л я я в него дан¬ ные м е т о д о м W r i t e . Если вам нужен в ы х о д н о й б у ф е р и з о в а н н ы й д в о и ч н ы й п о т о к , то он создается так: fs
new В д а л ь н е й ш е м на базе э т о г о п о т о к а м о ж н о создать в х о д н о й поток класса B i n a r y -
BinaryReader
br
new
BinaryReader
Работа с буферизованными двоичными потоками данных демонстрируется в про г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и в е д е н в л и с т и н г е 14.6. Мы о с т а в л я е м ее вам на с а м о с т о я т е л ь н о е и з у ч е н и е . Листинг using using
14.6.
Файл
System;
namespace class private static
const
string
testFile
void
args)
Console
"Файл
уже
return;
FileStream
492
fs
new
BinaryWriter
bw
for ( u i n t
0;
i
new
i
20;
BinaryWriter
i+ + )
А. В Фролов, Г.
Фролов. Язык
Самоучитель
fs
fs
new 10000);
BinaryReader string
for
br
new
s
(uint i
i
20;
i+ + ) br R e a d U I n t 3 2
успешно
Буферизация текстовых потоков Потоки,
связанные с текстовыми
файлами, также
нужно воспользоваться соответствующими
можно
буферизовать.
Для
этого
п е р е г р у ж е н н ы м и к о н с т р у к т о р а м и в клас¬
сах S t r e a m W r i t e r и S t r e a m R e a d e r . В о т как создается в ы х о д н о й б у ф е р и з о в а н н ы й т е к с т о в ы й поток: StreamWriter
Первые 3
sw
new
StreamWriter
false,
п а р а м е т р а к о н с т р у к т о р а класса S t r e a m W r i t e r з а д а ю т с о о т в е т с т в е н н о
путь к файлу, флаг д о б а в л е н и я н о в ы х д а н н ы х в файл и к о д и р о в к у д а н н ы х . Р а з м е р бу¬ фера о п р е д е л я е т с я в байтах при п о м о щ и ч е т в е р т о г о п а р а м е т р а . В х о д н о й б у ф е р и з о в а н н ы й п о т о к д а н н ы х создается при п о м о щ и с л е д у ю щ е г о конст¬ р у к т о р а класса StreamReader
sr
new
StreamReader ( t e s t F i l e , UNICODE, t r u e ,
Размер буфера задается п о с л е д н и м , ч е т в е р т ы м п а р а м е т р о м к о н с т р у к т о р а . О б р а т и т е в н и м а н и е н а третий п а р а м е т р к о н с т р у к т о р а класса S t r e a m R e a d e r . Ес¬ ли этот параметр равен t r u e , при о т к р ы т и и файла его тип будет а в т о м а т и ч е с к и опре¬ д е л я т ь с я по с и г н а т у р е , т. е. по н а ч а л ь н ы м байтам файла. Н а п о м н и м , что т е к с т о в ы е глава 14. Файлы и потоки
493
файлы в к о д и р о в к е U N I C O D E с о д е р ж а т 2 - б а й т о в у ю с и г н а т у р у , о п р е д е л я ю щ у ю док
о с т а л ь н ы х байтов файла. Об этом мы р а с с к а з ы в а л и в ы ш е в разделе
«Кодировка текстовых потоков». П о л н ы й п р и м е р п р о г р а м м ы , р а б о т а ю щ е й с б у ф е р и з о в а н н ы м и т е к с т о в ы м и потока¬ м и , п р и в е д е н в л и с т и н г е 14.7. Листинг using using
14.7.
Файл
System; System.
namespace
f
class private static
const
string
testFile
void
args)
уже
StreamWriter
sw
new
("Число
sr
существует",
StreamWriter ( t e s t F i l e ,
о х о т н и к ж е л а е т з н а т ь , где \ " П и \ " равно примерно
false,
сидит 3.1415926);
new true,
String str if(str break;
null)
("Файл
494
успешно
А.
Фролов, Г.
Фролов. Язык
Самоучитель
Принудительный сброс буферов Е щ е один в а ж н ы й м о м е н т связан с б у ф е р и з о в а н н ы м и п о т о к а м и . Как мы у ж е г о в о р и л и , б у ф е р и з а ц и я у с к о р я е т работу п р и л о ж е н и й с п о т о к а м и , так как при ее и с п о л ь з о в а н и и сокращается о б р а щ е н и й к с и с т е м е в в о д а - в ы в о д а . Вы м о ж е т е п о с т е п е н н о , в течение д н я , д о б а в л я т ь в п о т о к д а н н ы е 1 байту, и только к вечеру эти д а н н ы е бу¬ д у т ф и з и ч е с к и з а п и с а н ы в файл на д и с к е . Во м н о г и х с л у ч а я х , о д н а к о , п р и л о ж е н и е д о л ж н о , не о т к а з ы в а я с ь совсем от буфери¬ з а ц и и , в ы п о л н я т ь п р и н у д и т е л ь н у ю запись б у ф е р о в в файл. Это м о ж н о с д е л а т ь с по¬ мощью метода F l u s h . Д а н н ы й м е т о д о п р е д е л е н во всех к л а с с а х , и м е ю щ и х о т н о ш е н и е к б у ф е р и з о в а н н ы м п о т о к а м . В о т как он и с п о л ь з у е т с я с б у ф е р и з о в а н н ы м и т е к с т о в ы м и п о т о к а м и д а н н ы х : StreamWriter
sw
new
StreamWriter 1000
false,
("Каждый о х о т н и к ж е л а е т з н а т ь , ("Число \ " П и \ " р а в н о п р и м е р н о
где
сидит
В этом ф р а г м е н т е кода мы создали в ы х о д н о й б у ф е р и з о в а н н ы й п о т о к д а н н ы х , запи¬ сали
в
него две строки
методом W r i t e L i n e ,
а затем
сбросили
буферы методом
F l u s h . З а к о н ч и в работу с п о т о к о м , м ы его з а к р ы л и м е т о д о м C l o s e .
Потоки в оперативной памяти ОС Microsoft W i n d o w s 95 и Microsoft W i n d o w s п р е д о с т а в л я ю т на у р о в н е своего п р о г р а м м н о г о и н т е р ф е й с а W i n 3 2 в о з м о ж н о с т ь р а б о т а т ь с о п е р а т и в н о й памя¬ т ь ю как с ф а й л о м . Это очень у д о б н о во м н о г и х с л у ч а я х , в ч а с т н о с т и , ф а й л ы , отобра¬ ж а е м ы е н а п а м я т ь , м о ж н о и с п о л ь з о в а т ь для п е р е д а ч и д а н н ы х между о д н о в р е м е н н о р а б о т а ю щ и м и з а д а ч а м и и п р о ц е с с а м и . П о д р о б н е е об этом вы м о ж е т е п р о ч и т а т ь в 2 7 - м томе системного к о т о р ы й называется « О п е р а ц и о н н а я с и с т е м а Microsoft W i n d o w s NT для п р о г р а м м и с т а . Часть 2» [12]. П р и с о з д а н и и п р о г р а м м С# вы т а к ж е м о ж е т е р а б о т а т ь с о б ъ е к т а м и о п е р а т и в н о й памяти как с ф а й л а м и , а точнее г о в о р я , как с п о т о к а м и . Ранее мы о т м е ч а л и , что в б и б л и о т е к е классов Microsoft F r a m e w o r k есть класс с п е ц и а л ь н о п р е д н а з н а ч е н н ы й для с о з д а н и я п о т о к о в в о п е р а т и в н о й памяти. Р а с с м о т р и м о с н о в н ы е п р и е м ы его и с п о л ь з о в а н и я .
Создание потока Для создания п о т о к о в в о п е р а т и в н о й п а м я т и в классе предусмотрено несколько конструкторов. П р о с т е й ш и й к о н с т р у к т о р не имеет п а р а м е т р о в и п о з в о л я е т создать поток с буфе¬ р о м , и м е ю щ и м нулевой н а ч а л ь н ы й new Глава 14
По мерс данных в этот поток размер буфера автоматически увеличивается. Вы м о ж е т е создать поток на базе м а с с и в а б а й т о в , как это показа но ниже: new b y t e О,
1,
2,
3,
4,
MemoryStream
5,
б,
7,
8,
9
new
С о з д а н н ы й т а к и м с п о с о б о м п о т о к будет с о д е р ж а т ь д а н н ы е массива. З а м е т и м , что размер т а к о г о п о т о к а не м о ж е т быть у в е л и ч е н (однако при н е о б х о д и м о с т и его м о ж н о уменьшить методом S e t L e n g t h ) . П р е д у с м о т р е н т а к ж е в а р и а н т к о н с т р у к т о р а , п о з в о л я ю щ и й задать н а ч а л ь н ы й р а з м е р б у ф е р а п о т о к а , к о т о р ы й будет у в е л и ч и в а т ь с я при н е о б х о д и м о с т и п о м е р е д о б а в л е н и я в поток новых данных: M e m o r y S t r e a m ms
new
Здесь мы создали п о т о к с н а ч а л ь н ы м р а з м е р о м буфера, р а в н ы м 1000 байт. П р и н е о б х о д и м о с т и м о ж н о создать п о т о к , д о с т у п н ы й т о л ь к о д л я ч т е н и я . Это мож¬ но с д е л а т ь с п о м о щ ь ю с л е д у ю щ е г о к о н с т р у к т о р а : data 0,
1,
2,
new b y t e 3,
4,
5,
б,
7,
MemoryStream
8,
9
new M e m o r y S t r e a m
false)
В д а н н о м с л у ч а е п о т о к m s R e a d O n l y будет д о с т у п е н т о л ь к о д л я ч т е н и я . П р и н е о б х о д и м о с т и м о ж н о создать п о т о к не из всего м а с с и в а , а т о л ь к о из его час¬ ти. Для этого н у ж н о у к а з а т ь к о н с т р у к т о р у н а ч а л ь н ы й и н д е к с и р а з м е р блока д а н н ы х , из к о т о р о г о б у д е т о б р а з о в а н м а с с и в : data 0,
1,
2,
new b y t e 3,
4,
5,
6,
7,
8,
9
M e m o r y S t r e a m ms new M e m o r y S t r e a m 1, MemoryStream msReadOnly new M e m o r y S t r e a m н а базе части массива d a t a м ы создаем два П е р в ы й из них д о с т у п е н для ч т е н и я и з а п и с и , а
5) 1,
5,
false)
и msReadOnly. только для чтения.
Чтение данных Если в ы с о з д а л и п о т о к M e m o r y S t r e a m н а базе п р о и н и ц и а л и з и р о в а н н о г о б а й т о в , т о м о ж е т е читать и з него д а н н ы е м е т о д а м и R e a d и R e a d B y t e .
массива
Первый из них читает блок д а н н ы х , заданных смещением от начала потока и размером, возвращая прочитанные д а н н ы е в виде массива. Текущая позиция продвигается вперед на количество байтов, равное размеру прочитанного блока. Второй метод ( R e a d B y t e ) чита¬ ет из потока 1 байт, продвигая т е к у щ у ю п о з и ц и ю вперед на 1 байт.
496
В Фролов, Г. В Фролов,
С# Самоучитель
Техника
данных из потока M e m o r y S t r e a m демонстрируется в программе,
и с х о д н ы й т е к с т к о т о р о й приведен в л и с т и н г е 14.8. Листинг
14.8.
using using
Файл
ch
System;
namespace class static
void data 0,
1,
byte[]
new b y t e
2,
3,
4,
5,
dataCopy
6,
new
MemoryStream
8,
9
byte
new 0,
foreach
7,
(byte
bt
in
9); dataCopy)
М а с с и в d a t a , н а базе к о т о р о г о будет с о з д а в а т ь с я п о т о к M e m o r y S t r e a m , п р о и н и ц и а л и з и р о в а н ц е л ы м и ч и с л а м и от 0 до 9, как это п о к а з а н о н и ж е : byte[] 0,
data 1,
2,
new b y t e 3,
4,
5,
б,
7,
8,
9
К р о м е т о г о , в п р о г р а м м е определен н е и н и ц и а л и з и р о в а н н ы й м а с с и в байтов d a t a C o p y р а з м е р о м в 10 ячеек: byte
dataCopy
new b y t e
В н а ч а л е наша п р о г р а м м а создает поток M e m o r y S t r e a m ms 14.
н а базе м а с с и в а d a t a :
new M e m o r y S t r e a m Файлы
и
потоки
497
Далее она читает содержимое всего потока в массив d a t a C o p y :
Вслед з а этим с о д е р ж и м о е массива d a t a C o p y , проинициализированного содер жимым потока, отображается на консоли: foreach
(byte
bt
in
dataCopy)
Завершив работу с потоком m s , программа закрывает его методом C l o s e :
Запись данных Для записи данных в поток M e m o r y S t r e a m вы можете использовать методы W r i t e и W r i t e B y t e . Первый из этих методов записывает в поток 1 байт данных, а — массив байтов или часть этого массива. В листинге 14.9 мы привели пример программы, которая сначала записывает д а н ные в поток по байтам, а затем читает данные из этого потока и ото¬ бражает их на консоли. 14.9. using using
Файл
System;
namespace class static
void
args)
M e m o r y S t r e a m ms for (byte i
int
data
0;
new M e m o r y S t r e a m i
10;
i+ + )
ms -1)
break;
498
В.
Г. В. Фролов. Язык
Самоучитель
П е р в ы м д е л о м программа создает поток с п о м о щ ь ю п р о с т е й ш е г о к о н с т р у к т о р а без параметров: MemoryStream
new
Как мы у ж е г о в о р и л и , для х р а н е н и я д а н н ы х такого п о т о к а в о п е р а т и в н о й п а м я т и с о з д а е т с я буфер нулевой д л и н ы . Р а з м е р этого б у ф е р а будет а в т о м а т и ч е с к и увеличи¬ ваться по мере записи в п о т о к н о в ы х д а н н ы х . Д а л е е н а ш а п р о г р а м м а в ц и к л е з а п и с ы в а е т в п о т о к 10 ч и с е л , п о л ь з у я с ь для этого методом for (byte i
0;
i
10;
i+ + )
Д а л е е , д л я того чтобы из п о т о к а м о ж н о было ч и т а т ь , нам н е о б х о д и м о с б р о с и т ь бу¬ фер м е т о д о м F l a s h , а т а к ж е п е р е м е с т и т ь т е к у щ у ю п о з и ц и ю в начало потока:
П о д р о б н е е о п о з и ц и о н и р о в а н и и внутри п о т о к а мы р а с с к а ж е м п о з ж е , а с е й ч а с заме¬ тим т о л ь к о , что методу S e e k п е р е д а ю т с я д в а п а р а м е т р а . П е р в ы й п а р а м е т р у к а з ы в а е т с м е щ е н и е в байтах, а второй место в п о т о к е , от к о т о р о г о о т с ч и т ы в а е т с я это с м е щ е ние. К о н с т а н т а задает с м е щ е н и е о т н о с и т е л ь н о н а ч а л а п о т о к а . З а м е т и м , ч т о , если бы мы просто з а к р ы л и п о т о к , его д а н н ы е были бы для нас поте¬ р я н ы , и мы не смогли бы их п р о ч и т а т ь . О д н а к о после с б р о с а буфера и у с т а н о в к и те¬ к у щ е й п о з и ц и и н а н а ч а л о п о т о к а д а н н ы е д о с т у п н ы для ч т е н и я . Н а ш а п р о г р а м м а с ч и т ы в а е т и х п о б а й т н о , в ы з ы в а я д л я этого м е т о д R e a d B y t e :
int
data
if (data
-1)
Этот метод в о з в р а щ а е т з н а ч е н и е с ч и т а н н о г о байта, п р е о б р а з о в а н н о е в тип i n t , или з н а ч е н и е если был д о с т и г н у т к о н е ц потока. П о с л е о т о б р а ж е н и я всех д а н н ы х , х р а н я щ и х с я в п о т о к е , мы з а к р ы в а е м п о т о к мето¬ дом C l o s e :
14. Файлы и
499