FileUtilities, část 2

Tomáš Holan       03.02.2011       I/O operace       10565 zobrazení

Toto je dokončení první části příspěvku.

Minule jsme si připravili metodu pro čtení textového souboru po jednotlivých řádcích a metodu pro umožnění načtení hlavičky souboru ještě předtím, než začneme procházet a zpracovávat řádky ostatní. Pro vyřešení našeho konkrétního zadání potřebujeme ještě umět to samé, ale pro soubor umístěný někde na webu.

Začneme nejprve pouze zjištěním co a jak je potřeba všechno udělat pro vlastní download souboru:

var request = WebRequest.Create(url);
//If required by the server, set the credentials.
request.Credentials = CredentialCache.DefaultCredentials;

using (var response = request.GetResponse())
{
    using (var reader = new StreamReader(response.GetResponseStream(), encoding))
    {
        //Read response stream
    }
}

V zásadě potřebujeme vytvořit objekt WebRequest, pro předané url. Pak případně nastavit potřebná práva (zde by jsme mohli předpokládat pouze taková použití naší třídy, kdy nejsou pro stažení souboru žádné certifikáty, hesla a podobné záležitosti vyžadovány – v takových případech by jsme asi použili objekt WebRequest přímo). Získáme response a dále jeho obsah v podobě streamu, který pro určený encoding načteme. Po ukončení čtení je potřeba stream i response uzavřít.

Část pro čtení obsahu souboru pro předaný objekt StreamReader (implementaci enumerátoru) již máme připravenou z minula, nyní ale potřebujeme, aby byl kromě readeru uzavřen i objekt WebResponse. K tomu si napíšeme jednoduchou pomocnou třídu, do které zároveň zapouzdříme použití web requestu, a kterou podědíme ze třídy TextReader. Třída bude private a umístěna uvnitř třídy FileUtilities.

#region member types definition
private sealed class WebResponseReader : TextReader, IDisposable
{
    #region member varible and default property initialization
    private WebResponse response;
    private StreamReader reader;
    #endregion

    #region constructors and destructors
    public WebResponseReader(WebRequest request, Encoding encoding)
    {
        this.response = request.GetResponse();
        try
        {
            this.reader = new StreamReader(response.GetResponseStream(), encoding);
        }
        catch
        {
            response.Close();
            throw;
        }
    }
    #endregion

    #region action methods
    public override string ReadLine()
    {
        return this.reader.ReadLine();
    }
    #endregion

    #region private member functions
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            reader.Close();
            response.Close();
        }
    }
    #endregion
}
#endregion

Kromě metody Dispose() zde potřebujeme pouze jedinou další metodu a sice ReadLine(), protože ta bude jako jediná využívána při “podhození” tohoto objektu pomocné metodě ReadLinesInternal() (která má právě vstupní argument typu TextReader, vzpomínáte?).

Tím máme asi již vše připravené a můžeme dokončit implementace naší třídy. Nejprve metodu ReadLines() (doplníme nové dvě varianty se vstupním argumentem typu Uri):

public static IEnumerable<string> ReadLines(Uri url, Encoding encoding)
{
    if (url == null)
    {
        throw new ArgumentNullException("url");
    }
    if (encoding == null)
    {
        throw new ArgumentNullException("encoding");
    }

    return ReadLinesInternal(new WebResponseReader(WebRequest.Create(url), encoding));
}

public static IEnumerable<string> ReadLines(Uri url)
{
    if (url == null)
    {
        throw new ArgumentNullException("url");
    }

    return ReadLinesInternal(new WebResponseReader(WebRequest.Create(url), Encoding.UTF8));
}

A také metodu ReadHeaderAndLines():

public static IEnumerable<string> ReadHeaderAndLines(Uri url, Encoding encoding, out string header)
{
    if (url == null)
    {
        throw new ArgumentNullException("url");
    }
    if (encoding == null)
    {
        throw new ArgumentNullException("encoding");
    }

    var reader = new WebResponseReader(WebRequest.Create(url), encoding);
    try
    {
        header = reader.ReadLine();
        if (header == null)
        {
            throw new EndOfStreamException("Header row not found");
        }
    }
    catch
    {
        reader.Dispose();
        throw;
    }

    return ReadLinesInternal(reader);
}

public static IEnumerable<string> ReadHeaderAndLines(Uri url, out string header)
{
    return ReadHeaderAndLines(url, Encoding.UTF8, out header);
}

Tím je třída FileUtilities dokončena, zde naleznete její kompletní implementaci ve finální podobě.

A jak třídu použít, resp. musíme ještě splnit naše původní zadání pro stažení a zpracování (výpis) kurzů:

public static void Main()
{
    const string url = "http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt";

    string header;
    var lines = FileUtilities.ReadHeaderAndLines(new Uri(url), out header);

    DateTime datum = DateTime.ParseExact(header.Substring(0, 10), "dd.MM.yyyy", System.Globalization.CultureInfo.InvariantCulture);

    foreach (var kurz in from line in lines.Skip(1) //Vynechání prvního řádku
                         let values = line.Split('|')
                         select new
                         {
                             Datum = datum,
                             KodMeny = values[3],
                             Zeme = values[0],
                             Mena = values[1],
                             Mnozstvi = int.Parse(values[2], System.Globalization.CultureInfo.InvariantCulture),
                             Kurz = double.Parse(values[4], new System.Globalization.CultureInfo("cs-CZ", false))
                         })
    {
        Console.WriteLine(kurz);
    }
}

Ukázka výstupu:

{ Datum = 3.2.2011 0:00:00, KodMeny = AUD, Zeme = Austrálie, Mena = dolar, Mnozstvi = 1, Kurz = 17,74 }
{ Datum = 3.2.2011 0:00:00, KodMeny = BRL, Zeme = Brazílie, Mena = real, Mnozstvi = 1, Kurz = 10,512 }
{ Datum = 3.2.2011 0:00:00, KodMeny = BGN, Zeme = Bulharsko, Mena = lev, Mnozstvi = 1, Kurz = 12,317 }
...

(Pozn.: Všimněte si jak pro parsování hodnoty kurzu konstruujeme konkrétní kulturu “cs-CZ”. Druhý parametr v konstruktoru říká, že se nepoužijí žádné lokální nastavení z OS v případě, že je v OS nastavena stejná tj. česká kultura.)

Doufám že tento příspěvek alespoň trochu splní svůj účel a sice ukázat, že i u relativně jednoduché úlohy je dobré k řešení přistupovat zodpovědně a s rozmyslem a snažit se o dosažení kvalitního výsledku.

(Podobným tématem se také zabývá např. i tento článek, kterým byl ten můj volně inspirován.)

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback