Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Подскажите как правильно


Автор: ilya_cska 20.3.2008, 14:08
Скачиваю HTML и перевожу в String. Сам он довольно здоровый (~50кб). Подскажите как будет привильнее всего сделать (я в принцепе сделал, но больно плохо), чтоб вытащить с HTML определенные данные. Ниже я привел кусок HTML , как вы поняли мне нужно вытащить баланс, фамилию и т.д.

Код

 <td class="index_block_content_wide"><img src="/images/box_purple.gif">&nbsp;<b>Здравствуйте, Фамилия И.О</b>
        <br><br><img src="/images/arrow.gif">&nbsp;&nbsp;<b>Ваш баланс равен <span OnMouseMove="move_tip(tip4,event.clientX,event.clientY)" OnMouseOut="hide_tip(tip4)" style='font-size:30px;color:#af2525;'><u><nobr>-81.7</nobr></u></span> рублей.</b><span class="tip" id="tip4">Ваш актуальный баланс на данный момент.<br>Обновляется каждые 15 минут.</span><br><br><img src="/images/arrow.gif">&nbsp;&nbsp;<b>Кредит равен <span OnMouseMove="move_tip(tip5,event.clientX,event.clientY)" OnMouseOut="hide_tip(tip5)"><u>100</u></span> рублей.</b>

Автор: Freak_FB 20.3.2008, 14:47
Я бы использовал регулярные выражения.

Код

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Fam
{
    class Program
    {
        static void Main(string[] args)
        {
            string str = "<td class=\"index_block_content_wide\"><img src=\"/images/box_purple.gif\">&nbsp;<b>Здравствуйте, Фамилия И.О</b><br><br><img src=\"/images/arrow.gif\">&nbsp;&nbsp;<b>Ваш баланс равен <span OnMouseMove=\"move_tip(tip4,event.clientX,event.clientY)\" OnMouseOut=\"hide_tip(tip4)\" style='font-size:30px;color:#af2525;'><u><nobr>-81.7</nobr></u></span> рублей.</b><span class=\"tip\" id=\"tip4\">Ваш актуальный баланс на данный момент.<br>Обновляется каждые 15 минут.</span><br><br><img src=\"/images/arrow.gif\">&nbsp;&nbsp;<b>Кредит равен <span OnMouseMove=\"move_tip(tip5,event.clientX,event.clientY)\" OnMouseOut=\"hide_tip(tip5)\"><u>100</u></span> рублей.</b>";

            string pattern = "Здравствуйте, (?<name>.*)</b>(.*)Ваш баланс равен (.*)<u><nobr>(?<balance>.*)</nobr></u>(.*)Кредит равен(.*)<u>(?<kredit>.*)</u>";
            
            Regex re = new Regex(pattern);
            Match m = re.Match(str);

            Console.WriteLine(m.Groups["name"].Value);
            Console.WriteLine(m.Groups["balance"].Value);
            Console.WriteLine(m.Groups["kredit"].Value);
        }
    }
}

Автор: ilya_cska 22.3.2008, 10:38
Freak_FB так как ты написал, скорее всего не получится, потому что я получаю строку во время выполнения программы. И весь HTML занимает 1500 строчек.


Автор: marcusmae 22.3.2008, 11:56
ilya_cska, а почему не получится? = Единственно, HTML-код нужно получить в переменную str в любой момент выполнения программы, ессесно, до того, как прорабатывают регулярные выражения. Или сделать функцию :

Код

        static void GetUserInfo(string str, out string name, out string balance, out string credit)
        {
            string pattern = "Здравствуйте, (?<name>.*)</b>(.*)Ваш баланс равен (.*)<u><nobr>(?<balance>.*)</nobr></u>(.*)Кредит равен(.*)<u>(?<kredit>.*)</u>";
            
            Regex re = new Regex(pattern);
            Match m = re.Match(str);

            name = m.Groups["name"].Value;
            balance = m.Groups["balance"].Value;
            credit = m.Groups["kredit"].Value;
        }


Строчка HTML-кода, подаваемого функции должна лишь содержать тег <td>, либо же в одну строчку можно положить ВЕСЬ код (50кб - не так уж и много), но тогда выходные параметры имя-баланс-кредит должны стать массивами ( и из групп match-а значения доставать в массивы ).

Автор: ilya_cska 22.3.2008, 13:36
Тут происходит как бы наложение строки на строку?

Автор: marcusmae 22.3.2008, 14:35
Цитата(ilya_cska @  22.3.2008,  13:36 Найти цитируемый пост)
Тут происходит как бы наложение строки на строку?


где? = в аргументе с out? = несовсем. Это - "аргумент на выход", который должен объявить вызывающий, а инициализировать вызываемый. Есть ещё ref - "аргумент по ссылке", который должен объявить и инициализировать вызывающий, а вызываемый _может_ его изменить, причём если изменит, то вызывающий об этом узнает. Подробнее - в MSDN.

Если не то имели ввиду, то дайте сюда какой-нибудь полный текст HTML (из тех, с кот. Вы работаете) - может тогда удастся показать нагляднее.

Автор: ilya_cska 22.3.2008, 14:50
Если не то имели ввиду, то дайте сюда какой-нибудь полный текст HTML (из тех, с кот. Вы работаете) - может тогда удастся показать нагляднее.

Вот, ссылка на HTML. http://artem-bazar.110mb.com/qwer.txt
Если открыть файл с помощью Notpad++ то информация, которую мне нужно вытащить, находится с 1275 строчке.

Автор: marcusmae 23.3.2008, 21:08
ilya_cska, в классе User описана необходимая функциональность, связанная с пользователем. Помимо полей-только-для-чтения Name, Balance и Credit, есть конструктор, вывод сводной информации Summary и статическая функция для извлечения информации из текста HTML :

Код

        /// <summary>
        /// Try to get the user info from the specified string text.
        /// </summary>
        /// <param name="text">The string text.</param>
        /// <param name="user">The user instance.</param>
        /// <returns>
        /// The value indicating if the user info presents in the specified text.
        /// </returns>
        public static bool TryGetUser(string text, out User user)
        {
            string pattern =
                "Здравствуйте,(?<name>.*)</b>(.*)Ваш баланс равен(.*)<u><nobr>(?<balance>.*)</nobr></u>(.*)<b>Кредит равен(.*)<u>(?<credit>.*)</u></span> рублей.</b><span class=\"tip\" id=\"tip5\">";

            user = null;

            Match match = new Regex(pattern).Match(text);

            if ((match.Groups["name"].Success) &&
                (match.Groups["balance"].Success) &&
                (match.Groups["credit"].Success))
            {
                decimal balance, credit;
                if (Decimal.TryParse(match.Groups["balance"].Value, out balance) &&
                    Decimal.TryParse(match.Groups["credit"].Value, out credit))
                {
                    user = new User(match.Groups["name"].Value.Trim(), balance, credit);
                    return true;
                }
            }

            return false;
        }


Оригинальный паттерн регулярного выражения Freak_FB пришлось слегка подредактировать для того, чтобы кредит по признакам не путался со следующими за ним записями. Рекомендую тщательно перепроверить его переносимость, протестировав на большом множестве реальных страниц.

Рубли из строки переводятся в численное выражение, что может быть излишне, если не нужно с ними делать никакой арифметики.

Вызов мог бы выглядеть так :

Код

        static void Main(string[] args)
        {
            string filename = "qwer.txt";

            // Read from file to string variable.
            string htmlText;
            using (StreamReader streamReader = new StreamReader(filename,
                Encoding.GetEncoding("windows-1251")))
            {
                htmlText = streamReader.ReadToEnd().Replace(Environment.NewLine, "");
            }

            // Search with specified pattern.
            User user;
            if (User.TryGetUser(htmlText, out user))
            {
                Console.WriteLine("Информация о пользователе :");
                Console.WriteLine(user.Summary);
            }
            else
            {
                Console.WriteLine("В указанном файле информации о пользователе не найдено.");
            }
            Console.ReadKey();
        }
    }


Открыли файл читателем и слили весь текст в одну строку. Далее обращаю внимание на крайне нежелательную (например, в плане скорострельности) операцию удаления из текста символов перевода каретки :

Код

.Replace(Environment.NewLine, "");


Компьютеры, конечно, железные, но могли бы - вздохнули бы горько smile Если её не делать, то паттерн в нынешнем виде не будет работать, то есть, нужно либо очень аккуратно его модифицировать, либо сразу иметь текст без многострочности.

Полный код программы присоединён.

Автор: ilya_cska 24.3.2008, 08:46
marcusmae, мож глупый вопрос, а ты файл qwer.txt не исправлял? А то я код твой скачал, а программа выполняет условие
Код

Console.WriteLine("В указанном файле информации о пользователе не найдено.");

Автор: marcusmae 24.3.2008, 13:29
ilya_cska, нет, я ничего не исправлял. Изменим метод загрузки так, чтобы файл получался напрямую из урл (собсна, так сразу нужно было сделать) :

Код

        static void Main(string[] args)
        {
            string uriString = "http://artem-bazar.110mb.com/qwer.txt";

            // Read from file to string variable.
            string htmlText;
            using (StreamReader streamReader = new StreamReader(
                new WebClient().OpenRead(uriString), Encoding.GetEncoding("windows-1251")))
            {
                htmlText = streamReader.ReadToEnd().Replace(Environment.NewLine, "");
            }

            // Search with specified pattern.
            User user;
            if (User.TryGetUser(htmlText, out user))
            {
                Console.WriteLine("Информация о пользователе :");
                Console.WriteLine(user.Summary);
            }
            else
            {
                Console.WriteLine("В указанном файле информации о пользователе не найдено.");
            }
            Console.ReadKey();
        }


Результат :

Код

Информация о пользователе :
Имя : Фамилия !
Баланс : -11.55 рублей
Кредит : 100 рублей

Автор: ilya_cska 24.3.2008, 14:34
marcusmae скинь пожалуйста весь код, а то компилятор ругается.

Автор: ilya_cska 24.3.2008, 14:51
Все сделал, только все равно пишет В указанном файле информации о пользователе не найдено
полный код
Код

using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;

namespace HTMLRegex
{
    class Program
    {
       /* static void Main(string[] args)
        {
            string filename = "qwer.txt";

            // Read from file to string variable.
            string htmlText;
            using (StreamReader streamReader = new StreamReader(filename,
                Encoding.GetEncoding("windows-1251")))
            {
                htmlText = streamReader.ReadToEnd().Replace(Environment.NewLine, "");
            }

            // Search with specified pattern.
            User user;
            if (User.TryGetUser(htmlText, out user))
            {
                Console.WriteLine("Информация о пользователе :");
                Console.WriteLine(user.Summary);
            }
            else
            {
                Console.WriteLine("В указанном файле информации о пользователе не найдено.");
            }
            Console.ReadKey();
        }*/

        static void Main(string[] args)
        {
            string uriString = "http://artem-bazar.110mb.com/qwer.txt";
            
            // Read from file to string variable.
            string htmlText;

            WebClient myWebClient = new WebClient();
            using (StreamReader streamReader = new StreamReader(
                myWebClient.OpenRead(uriString), Encoding.GetEncoding("windows-1251")))
            {
                htmlText = streamReader.ReadToEnd().Replace(Environment.NewLine, "");
            }

            // Search with specified pattern.
            User user;
            if (User.TryGetUser(htmlText, out user))
            {
                Console.WriteLine("Информация о пользователе :");
                Console.WriteLine(user.Summary);
            }
            else
            {
                Console.WriteLine("В указанном файле информации о пользователе не найдено.");
            }
            Console.ReadKey();
        }
    }

    /// <summary>
    /// Defines the user class.
    /// </summary>
    class User
    {
        /// <summary>
        /// Try to get the user info from the specified string text.
        /// </summary>
        /// <param name="text">The string text.</param>
        /// <param name="user">The user instance.</param>
        /// <returns>
        /// The value indicating if the user info presents in the specified text.
        /// </returns>
        public static bool TryGetUser(string text, out User user)
        {
            string pattern ="Здравствуйте,(?<name>.*)</b>(.*)Ваш баланс равен(.*)<u><nobr>(?<balance>.*)</nobr></u>(.*)<b>Кредит равен(.*)<u>(?<credit>.*)</u></span> рублей.</b><span class=\"tip\" id=\"tip5\">";

            user = null;

            Match match = new Regex(pattern).Match(text);

            if ((match.Groups["name"].Success) &&
                (match.Groups["balance"].Success) &&
                (match.Groups["credit"].Success))
            {
                decimal balance, credit;
                if (Decimal.TryParse(match.Groups["balance"].Value, out balance) &&
                    Decimal.TryParse(match.Groups["credit"].Value, out credit))
                {
                    user = new User(match.Groups["name"].Value.Trim(), balance, credit);
                    return true;
                }
            }

            return false;
        }

        string name;
        /// <summary>
        /// Get the user name.
        /// </summary>
        public string Name { get { return this.name; } }

        decimal balance;
        /// <summary>
        /// Get the user balance.
        /// </summary>
        public decimal Balance { get { return this.balance; } }

        decimal credit;
        /// <summary>
        /// Get the user credit.
        /// </summary>
        public decimal Credit { get { return this.credit; } }

        /// <summary>
        /// Initialize a new instance of the user class.
        /// </summary>
        /// <param name="name">The user name.</param>
        /// <param name="balance">The user balance.</param>
        /// <param name="credit">The user credit.</param>
        public User(string name, decimal balance, decimal credit)
        {
            this.name = name;
            this.balance = balance; this.credit = credit;
        }

        public string Summary
        {
            get
            {
                return
                    "Имя : " + this.Name + Environment.NewLine +
                    "Баланс : " + this.Balance + " рублей" + Environment.NewLine +
                    "Кредит : " + this.credit + " рублей" + Environment.NewLine;
            }
        }
    }
}



Автор: marcusmae 24.3.2008, 16:45
Уф, ну это мистификация smile Мне лично достаточно того, что она работает, и я Вас не обманываю :

http://ipicture.ru/Gallery/Viewfull/1033940.html

Посмотрите, какие у Вас могут возникнуть проблемы. На строковую переменную с текстом через отладчик, например, проверьте, что там всё в порядке с кириллицей и вообще с кодировкой. Успешно перенёс программку на другой компьютер, правда Program.cs пришлось пересохранить в UTF-8... Да вообще, за полдня-то уж можно было всю программу вдоль и поперёк отладчиком излазить - было бы на то Ваше желание. smile А ещё лучше, если бы кто-нибудь ещё сделал независимое тестирование smile 

Автор: ivashkanet 24.3.2008, 17:02
Может кому понадобиться: http://www.eggheadcafe.com/articles/20030317.asp.

Автор: ilya_cska 25.3.2008, 11:32
Все, спасибо marcusmae.  smile Не знаю в чем была проблема, не стал разбираться,  я на основе Вашего кода сделел свою функцию. Просто до этого не было времени.  smile 

Автор: ilya_cska 25.3.2008, 14:20
Появился еще один вопрос. Вот код 
Код

 using (StreamReader streamReader = new StreamReader(filename,
                Encoding.GetEncoding("windows-1251")))
            {
                htmlText = streamReader.ReadToEnd().Replace(Environment.NewLine, "");
            }

Происходиту даления из текста символов перевода каретки.
 Как сделать, чтоб с типом String так же происходило удаление  символов перевода каретки.

Автор: marcusmae 25.3.2008, 15:12
Цитата(ilya_cska @  25.3.2008,  14:20 Найти цитируемый пост)
Происходиту даления из текста символов перевода каретки. Как сделать, чтоб с типом String так же происходило удаление  символов перевода каретки.


Совершенно непонятный вопрос smile 

Автор: ilya_cska 26.3.2008, 01:45
Как удалить  перевод каретки из типа String?

Автор: marcusmae 26.3.2008, 02:30
Цитата(ilya_cska @  26.3.2008,  01:45 Найти цитируемый пост)
Как удалить  перевод каретки из типа String?


Из типа нельзя ничего удалить, тип можно создать. Переменная htmlText как раз имеет тип String.
Что Вы имеете в виду? = Вы хотите сузить множество возможных символов?

Автор: ilya_cska 26.3.2008, 09:41
Происходит вот что. Если я читаю с файла, то он удаляет во всем тексте  перевод каретки. Если я задаю переменную адресом сайта, то удаляются не все символы перевода каретки(не удаляются в тех местах, где расположены скрипты), из-за чего не могу вытащить нужные данные. Вот я и подумал, если весь HTML код сначала перевести в статическую строку, а потом удалить перевод каретки.
Код

string uriString = "http:/url.ru/";

Автор: marcusmae 26.3.2008, 13:52
ilya_cska, я понял это так, что при том, что Вы берёте разные источники текста, в одном случае - файл, в другом - урл, у Вас получается разный результат. Так вот это возможно только если тексты неидентичны. Никаких багов компилятора и/или полтергейстов с привидениями здесь не замечено. А значит, Вам нужно у себя искать ошибку или искать ошибку в понимании того, о чём Вы говорите. Например, не померещился ли перевод строки из-за того, что блокнот сделал WordWrap для текста?

Автор: ilya_cska 26.3.2008, 14:45
Дело в том, что в первом случае - файл, в котором весь текст статичен. В url ,я проверял, он удаляет перевод каретки, но не везде,  в местах где есть скрипты он не удаляет.
Ниже я добавлю три файла
1. Взятый прямо с сайта с помощью Opera - Исходный текст
2. Выполнение программы с файлом 1
3. Выполнение программы с url

Автор: ilya_cska 26.3.2008, 14:49
2 файл

Автор: ilya_cska 26.3.2008, 14:49
3 файл

Автор: Optimus 26.3.2008, 18:22
Цитата(ilya_cska @  26.3.2008,  01:45 Найти цитируемый пост)
Как удалить  перевод каретки из типа String? 

из переменной типа String, да ?

из msdn
Цитата

public static string NewLine { get; }
A string containing "\r\n" for non-Unix platforms, or a string containing "\n" for Unix platforms.


тогда, IMHO,  надо сделать так:
Код

  string str = "qwerty\r\nasdfg\n\naswdef\n";

  Console.Write(str);

  string ss = str.Replace(Environment.NewLine, "").Replace("\n", "");

  Console.Write(ss);

Автор: ilya_cska 27.3.2008, 10:09
Спасибо Optimus, marcusmae. Все работает

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