[C#] Kan geen toegang krijgen tot het bestand

Pagina: 1
Acties:

Onderwerpen


  • Leon-
  • Registratie: Juli 2005
  • Laatst online: 20:30
Beste Tweakers,

Al een aantal dagen worstel ik met het volgende probleem. Ik voer de methode ValidateXML uit om te kijken of een XML file valide is. Ik lees een tekstveld uit, en heb de waarde daarvan in de variabele content gezet. Vervolgens sla ik het op als tijdelijk bestand en lees het uit met XMLReader.

De manier waarop werkt in principe goed, er zijn vast makkelijkere manieren om te kijken of een XML bestand valide is. Wanneer de methode bij het deleten van de file aankomt, bij de finally, krijg ik de melding dat ik geen toegang kan krijgen tot het bestand. Naar mijn weten heb ik het bestand en de streams gesloten om zonder problemen toegang te mogen krijgen tot het bestand, helaas is dit niet het geval. Ik krijg de melding dat ik geen toegang tot het bestand kan krijgen.


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
   public void ValidateXML()
        {
            //Dir + bestandsnaam
            String validate = @"data/validate.xml";  
            //Maak XML bestand
            TextWriter tw = new StreamWriter(validate);
            // Schrijf content weg, String met XML inhoud
            tw.WriteLine(content);
            //Sluit bestand
            
            tw.Close();
            tw = null;
            try
            {
                //Open de XML file
                XmlReader reader = new XmlTextReader(validate);
                ///Doorloop, is de file valide zet test = true
                while (reader.Read())
                { }
                test = true;
                //de reader sluiten
                reader.Close();
                reader = null;

            }
            catch (XmlException)
            {
                //Wanneer er een XMLException optreedt, zet test = false
                test = false;
            }
            finally
            {
                FileInfo temp = new FileInfo(validate);
                //als laatste, de temp file verwijderen
                temp.Delete();
            }
            
        }


Mijn doel is uiteraard ook om hiervan te leren, veel over deze fout gelezen via google maar niet tot een goed werkende oplossing kunnen komen. Wat zie ik over het hoofd?

  • dusty
  • Registratie: Mei 2000
  • Laatst online: 15-09 18:24

dusty

Celebrate Life!

Zet eens "c:/" voor de filename, en kijk dan eens of het werkt.

Back In Black!
"Je moet haar alleen aan de ketting leggen" - MueR


Verwijderd

Ervan uitgaand dat Leon niet met Mono onder linux/unix werkt:
Windows gebruikt backslashes i.p.v. slashes, en dan wordt 't
C#:
1
String validate = @"data\validate.xml";

  • BalusC
  • Registratie: Oktober 2000
  • Niet online

BalusC

Carpe diem

Ik heb geen idee hoe C# ermee omgaat, maar in Windows werken forward slashes net zo prima als backslashes. In ieder geval hebben backslashes in Java het nadeel dat je ze moet escapen in Strings.

  • Leon-
  • Registratie: Juli 2005
  • Laatst online: 20:30
dusty schreef op donderdag 18 september 2008 @ 19:09:
Zet eens "c:/" voor de filename, en kijk dan eens of het werkt.
Dit werkt helaas niet, ik blijf de melding krijgen dat Windows heb bestand niet kan benaderen omdat het gebruikt wordt door een ander proces.
Verwijderd schreef op donderdag 18 september 2008 @ 19:17:
Ervan uitgaand dat Leon niet met Mono onder linux/unix werkt:
Windows gebruikt backslashes i.p.v. slashes, en dan wordt 't
C#:
1
String validate = @"data\validate.xml";
Ik werk onder Windows idd ;) Windows kan met beide slashes overweg. Helaas lost dit het probleem niet op.
BalusC schreef op donderdag 18 september 2008 @ 19:42:
Ik heb geen idee hoe C# ermee omgaat, maar in Windows werken forward slashes net zo prima als backslashes. In ieder geval hebben backslashes in Java het nadeel dat je ze moet escapen in Strings.
C# gaat er naar mijn weten precies hetzelfde mee om als java, het voordeel aan C# is het @ voor een String zodat je niet meer hoeft te escapen.

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 16-09 15:42

Sebazzz

3dp

Probeer Visual Studio eens te herstarten. Dit heb ik wel eens gehad en door Visual Studio te herstarten opgelost. Het hosting process blijft namelijk op de achtergrond meedraaien zolang je je project aan hebt en kan bepaalde filelocks erop houden.

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Ik zou de textwriter sowieso in een using statement zetten; dan hoef je je geen zorgen te maken over het disposen (je zet 'm nu expliciet op null) en datzelfde ook voor de (xml)textreader. Een een file deleten kan makkelijk(er) met File.Delete dan eerst een FileInfo object te instantiëren etc.

Je hoeft overigens backslashes niet te escapen als je de string prefixed met een @ (dus @"blah\blah") en als je dan toch op een Windows omgeving zit zou ik gewoon netjes de gangbare manier aanhouden met backslashes; ook al werken de forward slashes toevallig.

[ Voor 30% gewijzigd door RobIII op 18-09-2008 20:11 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Jewest
  • Registratie: Juni 2007
  • Laatst online: 16-09 09:53
Hele stomme vraag, maar waar krijg je deze melding?
ook als je het programma 2 keer uitvoert? dus eerste keer bij het lezen tweede keer bij het schrijven?

Flickr
Canon 7D + Glas + Licht
Komt het rot over dan bedoel ik het anders en taalfouten zijn inbegrepen.


  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
RobIII schreef op donderdag 18 september 2008 @ 20:09:
Ik zou de textwriter sowieso in een using statement zetten; dan hoef je je geen zorgen te maken over het disposen (je zet 'm nu expliciet op null)
Disposen != null zetten. :P

Verder ben ik het wel met je eens.

Trouwens, wat versta je onder 'checken of je xml valid is' ? Gewoon checken of de xml 'wellformed' is, of als hij voldoet aan een bepaald schema ?
Volgens mij, als je die xml content inlaadt in een XmlDocument, zal dat XmlDocument wel een exceptie gooien als je content niet 'wellformed' is.
Checken of je xml aan een schema voldoet, kan je doen mbhv een XmlReader die SchemaSettings meekrijgt.

[ Voor 38% gewijzigd door whoami op 18-09-2008 20:19 ]

https://fgheysels.github.io/


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Dat was mijn point :P Het op null zetten is useless (in dit voorbeeld) en ik nam aan dat 'ie het object wil disposen met het op null zetten.
whoami schreef op donderdag 18 september 2008 @ 20:17:

Trouwens, wat versta je onder 'checken of je xml valid is' ? Gewoon checken of de xml 'wellformed' is, of als hij voldoet aan een bepaald schema ?
Volgens mij, als je die xml content inlaadt in een XmlDocument, zal dat XmlDocument wel een exceptie gooien als je content niet 'wellformed' is.
Checken of je xml aan een schema voldoet, kan je doen mbhv een XmlReader die SchemaSettings meekrijgt.
:Y Met stom

[ Voor 50% gewijzigd door RobIII op 18-09-2008 20:20 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Hij closed het eerst. Dus, het is gedisposed. :)

Is je variable 'content' een Stream ? Zoja, close je die ook ?

https://fgheysels.github.io/


  • Leon-
  • Registratie: Juli 2005
  • Laatst online: 20:30
RobIII schreef op donderdag 18 september 2008 @ 20:09:
Ik zou de textwriter sowieso in een using statement zetten; dan hoef je je geen zorgen te maken over het disposen (je zet 'm nu expliciet op null) en datzelfde ook voor de (xml)textreader. Een een file deleten kan makkelijk(er) met File.Delete dan eerst een FileInfo object te instantiëren etc.

Je hoeft overigens backslashes niet te escapen als je de string prefixed met een @ (dus @"blah\blah") en als je dan toch op een Windows omgeving zit zou ik gewoon netjes de gangbare manier aanhouden met backslashes; ook al werken de forward slashes toevallig.
File.Delete heb ik idd wat over gelezen, ik zal het gaan bekijken en gaat implementeren :) Werk helaas nog maar een aantal week in C#. Hiervoor wel redelijk veel met JAVA gewerkt.
Jewest schreef op donderdag 18 september 2008 @ 20:13:
Hele stomme vraag, maar waar krijg je deze melding?
ook als je het programma 2 keer uitvoert? dus eerste keer bij het lezen tweede keer bij het schrijven?
Wat ik vergeten ben te vertellen is dat ik de exceptie alleen krijg wanneer het programma een exceptie 'catched'. De finally wordt wel uitgevoerd wanneer alles binnen de try met succes wordt uitgevoerd.
Sebazzz schreef op donderdag 18 september 2008 @ 20:01:
Probeer Visual Studio eens te herstarten. Dit heb ik wel eens gehad en door Visual Studio te herstarten opgelost. Het hosting process blijft namelijk op de achtergrond meedraaien zolang je je project aan hebt en kan bepaalde filelocks erop houden.
Zojuist de .exe getest op verschillende pc's, helaas geen succes.
whoami schreef op donderdag 18 september 2008 @ 20:17:
[...]
Disposen != null zetten. :P

Verder ben ik het wel met je eens.

Trouwens, wat versta je onder 'checken of je xml valid is' ? Gewoon checken of de xml 'wellformed' is, of als hij voldoet aan een bepaald schema ?
Volgens mij, als je die xml content inlaadt in een XmlDocument, zal dat XmlDocument wel een exceptie gooien als je content niet 'wellformed' is.
Checken of je xml aan een schema voldoet, kan je doen mbhv een XmlReader die SchemaSettings meekrijgt.
Ik wil gewoon heel simpel controleren of de XML 'wellformed' is. Wat voor schema er gebruikt wordt is in eerste instantie niet belangrijk.
whoami schreef op donderdag 18 september 2008 @ 20:22:
Hij closed het eerst. Dus, het is gedisposed. :)

Is je variable 'content' een Stream ? Zoja, close je die ook ?
Content is een lange string, de content wordt uit een textarea gehaald. Zo ziet de string er uit:
code:
1
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n   <uml>\n\t<class>\n           <leon>test</leon>\n\t   <id>leon</id>\n\t   <positionx>100</positionx>\n\t   <positiony>34</positiony>\n\t   <width>23</width>\n\t   <height>2333333</height>\n                   <test>333</test\n\t</class> \n\n  </uml>\n\n\n"

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Even getest: het probleem is dat .Close() niet gebeurd bij een exception, zoals bij deze textstring gebeurd. Oplossing: een extra try-finally block...
(Daarnaast is een temporary-file hier niet nodig natuurlijk, gebruik een stream.. :))

[ Voor 8% gewijzigd door pedorus op 18-09-2008 21:09 ]

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 23:59

TeeDee

CQB 241

pedorus schreef op donderdag 18 september 2008 @ 21:07:
Even getest: het probleem is dat .Close() niet gebeurd bij een exception, zoals bij deze textstring gebeurd. Oplossing: een extra try-finally block...
(Daarnaast is een temporary-file hier niet nodig natuurlijk, gebruik een stream.. :))
Of, imho netter, een .Close in de finally.

Iets als:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
try 
{
// doe stuff
}
catch(XmlException xmlex)
{
    //doe iets met de error (schrijf 'm desnoods naar de debugger om te kijken
    //of er echt niks fout gaat
}
finally 
{
    tw.Close(); //of reader of of of 
}

[ Voor 9% gewijzigd door TeeDee op 18-09-2008 21:12 ]

Heart..pumps blood.Has nothing to do with emotion! Bored


  • pedorus
  • Registratie: Januari 2008
  • Niet online
TeeDee schreef op donderdag 18 september 2008 @ 21:11:
Of, imho netter, een .Close in de finally.
Dat lijkt me ook ja.
C#:
1
    tw.Close(); //of reader of of of 
Je bedoelt:
C#:
1
    reader.Close();

Maar goed, ik denk dat je toch niet zo netjes hoeft te zijn bij deze contructie:
C#:
1
XmlReader reader = XmlReader.Create(new StringReader(content));

(Close() achterwege laten, en alles overlaten aan de garbage collector lijkt me)

[ Voor 4% gewijzigd door pedorus op 18-09-2008 21:42 ]

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


  • dominic
  • Registratie: Juli 2000
  • Laatst online: 14-09 14:42

dominic

will code for food

pedorus schreef op donderdag 18 september 2008 @ 21:29:
[...]

Dat lijkt me ook ja.

[...]

Je bedoelt:
C#:
1
    reader.Close();

Maar goed, ik denk dat je toch niet zo netjes hoeft te zijn bij deze contructie:
C#:
1
XmlReader reader = XmlReader.Create(new StringReader(content));

(Close() achterwege laten, en alles overlaten aan de garbage collector lijkt me)
Mijn advies: Altijd closen en disposen indien mogelijk..

Download my music on SoundCloud


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
pedorus schreef op donderdag 18 september 2008 @ 21:29:
(Close() achterwege laten, en alles overlaten aan de garbage collector lijkt me)
Dan krijg je juist filehandles die langer dan nodig blijven bestaan etc. (mogelijk tot het sluiten van de applicatie zelfs). En dat is nou net de clou in dit topic.

Die Close() method is er niet voor niets; resources geef je vrij waar nodig en zo spoedig mogelijk

[ Voor 4% gewijzigd door RobIII op 18-09-2008 22:31 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • pedorus
  • Registratie: Januari 2008
  • Niet online
dominic schreef op donderdag 18 september 2008 @ 22:11:
Mijn advies: Altijd closen en disposen indien mogelijk..
Wacht, ik bedenk me dat me try-finally opmerking niet klopte... De 'juiste' oplossing is natuurlijk een using-blok, dus het had moeten zijn:
C#:
1
2
3
4
5
using (XmlReader reader = XmlReader.Create(validate))
{
    while (reader.Read()) ;
    return true;
}


Enkel in het geval van een StringReader zijn er volgens mij geen unmanaged resources, dus heeft closen/disposen toch geen zin. In het voorbeeld bij StringReader staat het ook niet. (Hoewel in de voorbeelden ook 'using' vaak vergeten wordt, waarschijnlijk omdat VB het niet kent.)
RobIII schreef op donderdag 18 september 2008 @ 22:31:
Dan krijg je juist filehandles die langer dan nodig blijven bestaan etc. (mogelijk tot het sluiten van de applicatie zelfs). En dat is nou net de clou in dit topic.
StringReader heeft volgens mij dus geen handles :)

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 23:59

TeeDee

CQB 241

RobIII schreef op donderdag 18 september 2008 @ 22:31:
[...]
Die Close() method is er niet voor niets; resources geef je vrij waar nodig en zo spoedig mogelijk
Volledig mee eens, maar volgens mij wordt er niks geclosed als er een Exception gegooid wordt. Dus is het imo wel zo netjes om in de finally alsnog het e.e.a. af te sluiten.
pedorus schreef op donderdag 18 september 2008 @ 22:44:
[...]

Wacht, ik bedenk me dat me try-finally opmerking niet klopte... De 'juiste' oplossing is natuurlijk een using-blok, dus het had moeten zijn:
RobIII in "[C#] Kan geen toegang krijgen tot het be..."

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
TeeDee schreef op vrijdag 19 september 2008 @ 08:56:
[...]

Volledig mee eens, maar volgens mij wordt er niks geclosed als er een Exception gegooid wordt. Dus is het imo wel zo netjes om in de finally alsnog het e.e.a. af te sluiten.
vandaard dat RobIII zegt om een using statement te gebruiken. Dat komt nl. overeen met:
code:
1
2
3
4
5
6
7
8
try
{
...
}
finally
{
    obj.Dispose();
}
Wat ik vergeten ben te vertellen is dat ik de exceptie alleen krijg wanneer het programma een exceptie 'catched'. De finally wordt wel uitgevoerd wanneer alles binnen de try met succes wordt uitgevoerd.
finally wordt altijd uitgevoerd, ook als er een exceptie optreedt.

[ Voor 24% gewijzigd door whoami op 19-09-2008 09:19 ]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
pedorus schreef op donderdag 18 september 2008 @ 22:44:
[...]

Enkel in het geval van een StringReader zijn er volgens mij geen unmanaged resources, dus heeft closen/disposen toch geen zin.
eh ?
Met een Dispose geef je juist de managed resources vrij.
Trouwens, als een type IDisposable implementeert, dan dispose (of Close) je het object zelf zo spoedig mogelijk. Het maakt niet uit hoe het object verder zelf intern werkt, of als het wel of niet unmanaged resources heeft.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
whoami schreef op vrijdag 19 september 2008 @ 09:22:
Met een Dispose geef je juist de managed resources vrij.
Zie http://msdn.microsoft.com....idisposable.dispose.aspx :
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
Close naast using/Dispose gebruiken heeft niet zoveel zin staat er ook in:
Classes that have such conventions might choose to implement a public method with a customized name, such as Close, which calls the Dispose method.
Nu heeft StringReader geen unmanaged resources volgens mij, dus zal Dispose niks doen, en kun je het dus overslaan. Daarnaast heeft XMLReader behalve die stream ze ook niet volgens mij. En anders is er altijd nog het backup-mechanisme, want dit is toch allemaal in-memory:
Because the Dispose method must be called explicitly, objects that implement IDisposable must also implement a finalizer to handle freeing resources when Dispose is not called.
Aan de andere kant breek je dan inderdaad de regels ( http://msdn.microsoft.com/en-us/library/yh598w02.aspx ), en is het toch een micro-optimalisatie om using over te slaan.
As a rule, when you use an IDisposable object, you should declare and instantiate it in a using statement.
(Nog net iets sterker dan Robill het al zei dus: Je zou het moeten gebruiken.)

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Met Dispose geef je ze eigenlijk beide vrij, zowel de managed als de unmanaged.
De finalizer geeft enkel de unmanaged vrij. (Check het Dispose pattern).
Close naast using/Dispose gebruiken heeft niet zoveel zin staat er ook in:
Had ik idd ook al vermeld.
Nu heeft StringReader geen unmanaged resources volgens mij, dus zal Dispose niks doen , en kun je het dus overslaan.
So ? Als het IDisposable implementeert, dan roep je die aan. Je hoeft nl. niet te weten of die iets doet of niet. Het object dat je gebruikt kan je aanzien als een black-box. Dispose als het Disposable is. Dispose geeft nl. ook de managed resources vrij.
Daarnaast heeft XMLReader behalve die stream ze ook niet volgens mij. En anders is er altijd nog het backup-mechanisme, want dit is toch allemaal in-memory:
Als je daarmee de GC bedoelt ... Je weet nooit wanneer die GC aangeroepen wordt.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • BramFokke
  • Registratie: Augustus 2007
  • Laatst online: 04-10-2024
Deze code garandeert helemaal niet dat de XmlReader al gesloten is als je in het finally blok komt. Want als de reader.Read() een exceptie gooit, dan kom je nooit bij de code waar je de XmlReader sluit. Je moet dit dus ook in de finally zetten maar beter is om gewoon using() {} te gebruiken, zoals enkele andere lezers al opmerken.

Overigens is er geen tijdelijk bestand nodig. Een XmlTextReader kan gewoon een stream accepteren in de constructor dus ook gewoon een MemoryStream. Dan heb je het probleem met de open files helemaal niet. En last but not least ben je niet echt aan het valideren, maar alleen checken of het allemaal nette XML is. Als je in plaats van new XmlReader() XmlReader.Create() gebruikt, kan je ook een schema meesturen om de data tegen te valideren.

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
whoami schreef op vrijdag 19 september 2008 @ 09:51:
Met Dispose geef je ze eigenlijk beide vrij, zowel de managed als de unmanaged.
De finalizer geeft enkel de unmanaged vrij. (Check het Dispose pattern).
Voor de discussie had ik even het voorbeeld eronder niet meegenomen. En misschien zijn er ook wel andere implementaties ;)
So ? Als het IDisposable implementeert, dan roep je die aan. Je hoeft nl. niet te weten of die iets doet of niet. Het object dat je gebruikt kan je aanzien als een black-box. Dispose als het Disposable is.
Ok, je hebt me overtuigt. Daarnaast is het goed mogelijk dat het in machinecode toch niets uitmaakt. Kortom:
C#:
1
2
3
4
5
6
using (StringReader sr = new StringReader(content))
using (XmlReader reader = XmlReader.Create(sr))
{
    while (reader.Read()) ;
    return true;
}

(Opmaak is van VS.NET, ik had verwacht dat de 2e using ingesprongen zou worden.)
Kleine opmerking op een verder goed verhaal: Dan moet je constructies gebruiken als
C#:
1
new MemoryStream(Encoding.ASCII.GetBytes(content))

Of zelf iets implementeren. En vervolgens moet je opgeven dat je encoding X hebt gebruikt om autodetectie te voorkomen. Toch minder mooi.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • BramFokke
  • Registratie: Augustus 2007
  • Laatst online: 04-10-2024
[...]

Kleine opmerking op een verder goed verhaal: Dan moet je constructies gebruiken als
C#:
1
new MemoryStream(Encoding.ASCII.GetBytes(content))

Of zelf iets implementeren. En vervolgens moet je opgeven dat je encoding X hebt gebruikt om autodetectie te voorkomen. Toch minder mooi.
Goede opmerking. Gelukkig is in de meeste domeinen één encoding afgesproken (Ik heb eigenlijk voornamelijk UTF-8 gezien).

Acties:
  • 0 Henk 'm!

  • Ilmar
  • Registratie: Maart 2006
  • Laatst online: 01-08 17:29
Of je vervangt alleen de file, mag niks uitmaken of naar memory of een file geschreven wordt:
C#:
1
2
3
4
5
6
7
8
            
MemoryStream validate = new MemoryStream();   
//Maak XML bestand 
TextWriter tw = new StreamWriter(validate); 
// Schrijf content weg, String met XML inhoud 
tw.WriteLine(content);

// etc. etc.
Pagina: 1