Выборка данных по дате из файла на PHP

Форум для тех кто начинает осваивать язык php.
Аватара пользователя
pessimist
Сообщения: 47
Благодарил (а): 55 раз
Поблагодарили: 6 раз

Выборка данных по дате из файла на PHP

Сообщение pessimist » Сб сен 03, 2016 8:57 pm

Здравствуйте, уважаемые форумчане!

Я только-только начал знакомиться с PHP и поэтому мне нужна Ваша помощь в решении следующей задачи:

1) Есть текстовый файл с ценами, которые соответствуют торговому дню;
2) Дата в этом текстовом файле в формате без разделителей, например 2 сентября сего года там записано как 20160902
3) От пользователя получается дата в форме строки 02.09.2016
4) Не все дни являются торговыми, в файле данных нет дат с выходными и праздниками
5) Пользователю нужно вывести цены на введенную им дату, а если это не торговый день, то вывести данные ближайшего торгового дня, который был до введенной даты и сообщить пользователю, что введенный день не являлся торговым и ему представлены данные предыдущего дня.

Текстовый файл имеет следующий формат:

Код: Выделить всё

TATN    20011207    14.7300000    14.7300000    14.7300000    14.7300000    30000
TATN    20011210    15.3500000    15.3500000    14.7100000    14.8000000    71700
TATN    20011211    14.9000000    15.1000000    14.8800000    15.0900000    348300
TATN    20011213    15.0400000    15.0400000    14.3500000    14.3500000    681300
TATN    20011214    14.5000000    14.5000000    14.2400000    14.3500000    156700
TATN    20011217    14.5800000    14.6000000    14.1600000    14.5400000    171800
TATN    20011218    14.5500000    14.7200000    14.3500000    14.4100000    396300
TATN    20011219    14.5900000    14.8500000    14.3600000    14.6000000    402300
TATN    20011220    14.8900000    14.8900000    14.4800000    14.6000000    352400
TATN    20011221    14.6900000    14.8400000    14.4800000    14.8000000    185300
TATN    20011224    14.8400000    15.1000000    14.7500000    14.9000000    427000
TATN    20011225    15.0000000    15.0500000    14.7600000    14.8000000    221300
TATN    20011226    14.9000000    14.9000000    14.7500000    14.8000000    85000
TATN    20011227    14.9000000    15.0300000    14.8500000    15.0000000    397800

Написал следующий код по чтению данных, но не понимаю как грамотно организовать циклы сравнения и выборки. А также нет понимания как работать с датой в формате 20160902

Код: Выделить всё

<?php
/*Выборка цен по акции Татнефть по указанной дате*/
$data_file = file_get_contents('tatn_price.txt');
$data_file_out = split ("\n", $data_file);
$data_string = split ("\t", $data_file_out[0]);
echo $data_string[1];
?>

Буду признателен за помощь и советы, особенно связанные с алгоритмом решения моей учебной задачи.
Реклама
cefp
Сообщения: 332
Поблагодарили: 3 раза

Re: Выборка данных по дате из файла на PHP

Сообщение cefp » Сб сен 03, 2016 9:23 pm

На ум приходит два алгоритма.

1. Преобразовать файл в двумерный массив с разделителями в виде пробелов и переводов строк. Первый столбец можно сделать столбцом с датой и обозначения строк вести по тексту даты. То есть использовать текстовый ключ.

2. Преобразовать дату введенную пользователем к такому же формату как в файле. Допустим, 20011221.
Тогда в файле можно с помощью функции отыскать текст "TATN 20011221" и по ближайшим символам перевода строки выделить нужную строку, а из неё нужные цены.

Сами даты можно еще к одному промежуточному формату перевести - к формату времени PHP. Он выражается в секундах с 1970 года. Тогда проще проверять дату днём ранее и днём позже.
d o h o d - s - n u l y a .ru - идеи бизнеса.
Аватара пользователя
Slash
Администратор
Сообщения: 2028
Поблагодарили: 62 раза

Re: Выборка данных по дате из файла на PHP

Сообщение Slash » Вс сен 04, 2016 8:35 am

Если число, например: 20011214 - это дата: 14.12.2001 (двенадцатое декабря две тысячи первого года).
А значит изменить формат даты, можно так:

Код: Выделить всё

$date = '14.12.2001';                # Получаем необходимую дату
$num  = explode('.', $date);         # Разбиваем дату на массив
$date = $num[2] . $num[1] . $num[0]; # Составляем число в нужном порядке        

Теперь, используя переменную $date будем искать совпадение по данным из файла tatn_price.txt. Для этого мы получим файл в виде массива, каждая новая строка - это будет один элемент массива. Затем через цикл foreach будем искать совпадения:

Код: Выделить всё

$data = file('tatn_price.txt'); # Получаем файл в виде массива

foreach ($data as $d)           # Прогоняем через массив
{
    if (strstr($d, $date))      # Ищем совпадение по дате
    {
        echo $d;                # Выводим на экран, если совпадение было найдено
    }

Ну, как то так. Вместо вывода на экран, можно делать, что то другое, продолжать обработку данных, например.

Вообще, думаю будет лучше переводить метку времени в Unix, мне кажется работать так будет удобнее. Сделать это можно так:

Код: Выделить всё

$date = '14.12.2001';
$date = strtotime($date); 

Значение переменной из примера будет 1008288000. Если это число перевести обратно в дату (Как перевести Unix в дату на PHP (time в date)), то получим:

Код: Выделить всё

0:00:00 14 December 2001 Friday

Теперь будем думать, как найти ближайшею дату, если нет выбранной...
Аватара пользователя
Slash
Администратор
Сообщения: 2028
Поблагодарили: 62 раза

Re: Выборка данных по дате из файла на PHP

Сообщение Slash » Вс сен 04, 2016 11:41 am

В общем набросал код для поиска ближайшего дня, в случаи, если указанного дня нет в базе. Для понимания, что происходит, прокомментировал весь код:

Код: Выделить всё

$date     = '09.10.2001';     # Получаем необходимую дату
$date     = strtotime($date); # Переводим формат времени
$date     = trim($date);      # Удаляем пробелы в начале и в конце
$position = true;             # Переменная для контроля над функцией
$days     = 432000;           # Количество дней для поиска ближайшей даты (в секундах)

$data = file('tatn_price.txt'); # Получаем файл в виде массива

foreach ($data as $line)      # Прогоняем через массив
{
    if (strpos($line, $date)) # Ищем совпадение 
    {
        echo $line;           # Выводим на экран, если совпадение было найдено
        $position = false;    # Запрет выполнения функции
    }
}

# Функция для поиска ближайшей даты
function search_nearest($data, $date, $days, $future, $past)
{
    $future = isset($future) ? $future : $date + $days; # Создаем дату до X дней больше
    $past   = isset($past)   ? $past   : $date - $days; # Создаем дату до X дней меньше

    $position = true; # Переменная для контроля над функцией (рекурсия)

    foreach ($data as $line)                                 # Прогоняем через массив
    {
        $element = explode('    ', $line);                   # Разбиваем элемент массива на массив
        if ($element[1] <= $future && $element[1] >= $past)  # Сравниваем время
        {
            echo $line . '<br />';                           # Если есть совпадение, выводим на экран   
            $position = false;                               # Отключаем рекурсию, так как совпадение было найдено
        }
    }

    if ($position)                                           # Проверяем, нужен ли выполнять поиск повторно
    {
        $future = $future + $days;                           # Прибавляем еще X дней
        $past   = $past   - $days;                           # Отнимаем X дней

        search_nearest($data, $date, $days, $future, $past); # Повторно запускаем поиск совпадения
    }
}

# Если указанного дня нет в базе, запускаем поиск ближайшего дня
if ($position)
{
    search_nearest($data, $date, $days, null, null);
}

Для работы данного сценария, необходимо перевести формат даты в Unix, для примера, код файла tatn_price.txt, из первого поста, должен выглядеть так:

Код: Выделить всё

TATN    1007683200    14.7300000    14.7300000    14.7300000    14.7300000    30000
TATN    1007942400    15.3500000    15.3500000    14.7100000    14.8000000    71700
TATN    1008028800    14.9000000    15.1000000    14.8800000    15.0900000    348300
TATN    1008201600    15.0400000    15.0400000    14.3500000    14.3500000    681300
TATN    1008288000    14.5000000    14.5000000    14.2400000    14.3500000    156700
TATN    1008547200    14.5800000    14.6000000    14.1600000    14.5400000    171800
TATN    1008633600    14.5500000    14.7200000    14.3500000    14.4100000    396300
TATN    1008720000    14.5900000    14.8500000    14.3600000    14.6000000    402300
TATN    1008806400    14.8900000    14.8900000    14.4800000    14.6000000    352400
TATN    1008892800    14.6900000    14.8400000    14.4800000    14.8000000    185300
TATN    1009152000    14.8400000    15.1000000    14.7500000    14.9000000    427000
TATN    1009238400    15.0000000    15.0500000    14.7600000    14.8000000    221300
TATN    1009324800    14.9000000    14.9000000    14.7500000    14.8000000    85000
TATN    1009411200    14.9000000    15.0300000    14.8500000    15.0000000    397800

Функция поиска ближайшего дня работает рекурсивно, до тех пор, пока не найдет совпадение, поиск может сильно затянуться, если ближайший день будет через год, например. Конечно это не хорошо, возможно придется допилить код, чтобы рекурсия выполнялась не более нескольких раз. Возможно, чуть позже займусь этим "допилом".

Надеюсь код подойдет для Вашей идеи.
Сильно не ругайте за такой сценарий, если что!
Аватара пользователя
Slash
Администратор
Сообщения: 2028
Поблагодарили: 62 раза

Re: Выборка данных по дате из файла на PHP

Сообщение Slash » Вс сен 04, 2016 2:31 pm

Немного допилил функцию search_nearest, теперь можно задать лимит выполнения функции. Код самой функции:

Код: Выделить всё

# Функция для поиска ближайшей даты
function search_nearest($data, $date, $days, $future, $past, $lap)
{
    $future = isset($future) ? $future : $date + $days; # Создаем дату до X дней больше
    $past   = isset($past)   ? $past   : $date - $days; # Создаем дату до X дней меньше
    $lap    = isset($lap)    ? $lap    : 1;             # Сколько раз выполняется функция

    $position = true; # Переменная для контроля над функцией (рекурсия)

    foreach ($data as $line)                                 # Прогоняем через массив
    {
        $element = explode('    ', $line);                   # Разбиваем элемент массива на массив
        if ($element[1] <= $future && $element[1] >= $past)  # Сравниваем время
        {
            echo $line . '<br />';                           # Если есть совпадение, выводим на экран   
            $position = false;                               # Отключаем рекурсию, так как совпадение было найдено
        }
    }

    if ($position) # Проверяем, нужен ли выполнять поиск повторно
    {
        if ((int) $lap <= 3) # Проверяем лимит количества выполнения функции 
        {
            $future = $future + $days; # Прибавляем еще X дней
            $past   = $past   - $days; # Отнимаем X дней
            $lap++;                    # Считаем, сколько раз выполняется функция

            search_nearest($data, $date, $days, $future, $past, $lap); # Повторно запускаем поиск совпадения
        }
        else
        
{
            echo 'Ни чего не найдено.';
        }
    }
}

Эту функцию необходимо заменить на функцию из предыдущего сообщения. Так же надо заменить код:

Код: Выделить всё

# Если указанного дня нет в базе, запускаем поиск ближайшего дня
if ($position)
{
    search_nearest($data, $date, $days, null, null);

На:

Код: Выделить всё

# Если указанного дня нет в базе, запускаем поиск ближайшего дня
if ($position)
{
    search_nearest($data, $date, $days, null, null, null);

На данный момент, функция выполняется три раза (или меньше, если найдет совпадение раньше), если надо сделать больше или меньше, измените цифру 3 на нужное вам количество раз, в условии:

Код: Выделить всё

        if ((int) $lap <= 3) # Проверяем лимит количества выполнения функции    
 
Аватара пользователя
pessimist
Сообщения: 47
Благодарил (а): 55 раз
Поблагодарили: 6 раз

Re: Выборка данных по дате из файла на PHP

Сообщение pessimist » Вс сен 04, 2016 4:44 pm

Здравствуйте, уважаемые форумчане!

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

А теперь - новые вопросы :)

Какой код лучше принять и почему, то есть в чем разница между функциями explode и split в данном конкретном случае?

Такой:
Slash писал(а):

Код: Выделить всё

$date = '14.12.2001';                # Получаем необходимую дату
$num  = explode('.', $date);         # Разбиваем дату на массив
$date = $num[2] . $num[1] . $num[0]; # Составляем число в нужном порядке    
 

Или такой:
pessimist писал(а):

Код: Выделить всё

$data_user='10.08.2010';                        # Получаем дату от пользователя с помощью календаря
$data = split ("\.", $data_user);               # Создаем массив из текстовых строк $data[0]=10; $data[1]=08; $data[2]=2010
$data_string = $data[2] . $data[1] . $data[0];  # Получаем строку с датой пользователя, такой же как и в файле $data_string = 20100810  

Вот, что мне еще пришло в голову:
Стал думать, на фига в текстовом файле с данными такой необычный формат даты? И обнаружил следующее: при такой записи каждая дата является упорядоченной как целое число. То есть более поздняя дата всегда больше более ранней и наоборот. То есть осуществлять поиск по массиву можно используя именно этот формат даты, не переводя его в формат Unix.

Или я не прав? Т.е. неверно думаю...
Аватара пользователя
Slash
Администратор
Сообщения: 2028
Поблагодарили: 62 раза

Re: Выборка данных по дате из файла на PHP

Сообщение Slash » Вс сен 04, 2016 5:59 pm

explode - Разбивает строку в массив, по разделителю. Работает в PHP 4, PHP 5 и PHP 7.
split - Разбивает строку в массив по регулярному выражению. Работает в PHP 4 и PHP 5. Функция считается устаревшей и удалена из PHP 7.
Так, что я бы использовал функцию explode, чего и Вам советую.
pessimist писал(а):Стал думать, на фига в текстовом файле с данными такой необычный формат даты?

Да, нет, он вполне обычный, хотя кому как. Если Вам удобнее работать с меткой времени, как написано у Вас, то конечно делайте, так как удобно. В принципе, разницы нет.
Аватара пользователя
pessimist
Сообщения: 47
Благодарил (а): 55 раз
Поблагодарили: 6 раз

Re: Выборка данных по дате из файла на PHP

Сообщение pessimist » Пн сен 05, 2016 2:39 am

Получился у меня таки свой собственный кучерявый код :) Решил сэкономить на полном переборе массива, в котором почти 4000 строк.
Делю массив пополам и сравниваю дату. Если дата юзера меньше - то делю половину массива еще пополам и иду вверх по массиву. Если дата юзера больше - то делю половину массива пополам и иду вниз по массиву. Цикл продолжается до тех пор, пока не найдено примерное место в массиве, где должна располагаться дата юзера.

Затем есть три варианта:

1) На найденном месте дата совпадает с датой юзера - тогда можно идти на выход
2) Найденное место выше даты юзера, тогда построчно опускамся по массиву до совпадения или предела
3) Наденное место ниже даты юзера, тогда построчно поднимаемся по массиву до совпадения или предела

Соответственно если даты совпали - день был торговый. Если достигнут предел, то день неторговый и берется индекс предыдущего дня.

В общем, как-то так пока получилось:

Код: Выделить всё

<meta charset="UTF-8" />
<?php
/*Выборка цен по акции Татнефть по указанной дате*/
$data_user=$_POST["zodiak_in_out"];             # Получаем дату от пользователя с помощью календаря
$data explode('.'$data_user);         # Создаем массив из текстовых строк $data[0]=10; $data[1]=08; $data[2]=2010
$data_string $data[2] . $data[1] . $data[0];     # Получаем строку с датой пользователя, такой же как и в файле $data_string = 20100810
$data_int =(int)$data_string;            # Переводим дату пользователя в целое число из строки $data_string
$data_file file('tatn_price.txt');        # Получаем строковый массив данных из файла tatn_price.txt
$kdelta count ($data_file);            # Получаем число элементов в строковом массиве из tatn_price.txt
$kindex 0;                    # Устанавливаем стартовый элемент строкового массива для поиска
$flag 0;                    # Флаг для цикла, 0 - сравнение не производилось; 1 - элементы совпадают; 2 - элемент юзера больше; 3 - элемент юзера меньше;
while ($kdelta != 0)                # Цикл продолжается до тех пор, пока не будет найден совпадающий элемент или найдено место элемента юзера
{
    
$data_pole explode ('    '$data_file[$kindex]); # Получаем первую строку массива и раскладываем ее по полям
    
if (($flag != 1) and ((int)$data_pole[1] > $data_int)) # Если элементы не равны и дата юзера меньше даты в строке то делаем
    
{
        
$flag 3# Устанавливаем флаг элемент юзера меньше
        
$ktrend 1# Устанавливаем направление поиска для следующего дробления массива
    
}
        if ((
$flag != 1) and ((int)$data_pole[1] < $data_int)) # Если элементы не равны и дата юзера больше даты в строке то делаем
    
{
        
$flag 2# Устанавливаем флаг элемент юзера больше
        
$ktrend 1# Устанавливаем направление поиска для следующего дробления массива
    
}
    
$kdelta floor ($kdelta 2); # Определяем следующую новую половину для просмотра строкового массива
    
$kindex $kindex $kdelta*$ktrend# Определяем индекс следующей половины для просмотра строкового массива
}
$data_pole explode ('    '$data_file[$kindex]); # Раскладываем найденную строку по полям, используя в качестве разделителя символ табуляции
if ((int)$data_pole[1] == $data_int){$flag 1;} # Если найденная дата совпадает с датой юзера устанавливаем флаг торгового дня
if ($flag == 3# Если дата юзера была меньше, чем дата с торговыми данными, то делаем цикл
{
    while (
$flag == 3# Делаем цикл до тех пор, пока дата юзера не совпадет с датой из файла или не станет больше, чем дата из файла
    
{
        
$kindex -= 1;
        
$data_pole explode ('    '$data_file[$kindex]); # Достаем из строки массива дату
        
if ((int)$data_pole[1] == $data_int){$flag 1;} # Если даты юзера и в файле совпали - устанавливаем флаг торгового дня
        
if ((int)$data_pole[1] < $data_int){$flag 4;} # Если дата стала больше, чем в файле - устанавливаем флаг неторгового дня
    
}
}
if (
$flag == 2)  # Если дата юзера была больше, чем дата с торговыми данными, то делаем цикл
{
    while (
$flag == 2# Делаем цикл до тех пор, пока дата юзера не совпадет с датой из файла или не станет меньше, чем дата из файла
    
{
        
$kindex += 1;
        
$data_pole explode ('    '$data_file[$kindex]); # Достаем из строки массива дату
        
if ($data_pole[1] == $data_string){$flag 1;} # Если даты юзера и в файле совпали - устанавливаем флаг торгового дня
        
if ((int)$data_pole[1] > $data_int){$flag 4;$kindex -= 1;} # Если дата стала меньше, чем в файле - устанавливаем флаг неторгового дня, и устанавливаем индекс строк на предыдущий торговый день
    
}
}
$data_pole explode ('    '$data_file[$kindex]); # Форматируем и выводим данные соответствующей строки из файла, отдельно для неторгового дня и отдельно для торгового дня
$data_torg substr($data_pole[1],6,2) . '.' substr($data_pole[1],4,2) . '.' substr($data_pole[1],0,4);
if (
$flag == 4) {echo 'Выбранная Вами дата: '$data_user' является неторговым днем<br>Ближайший торговый день был: ',$data_torg'<br>&nbsp<br>Цена на открытии торгов: 'substr($data_pole[2],0,6), ' р.<br>''  Минимальная цена: 'substr($data_pole[3],0,6), ' р.<br>''  Максимальная цена: ',  substr($data_pole[4],0,6), ' р.<br>''  Цена закрытия: 'substr($data_pole[5],0,6), ' р.<br>''  Объем торгов составил: '$data_pole[6], ' р.';}
if (
$flag == 1) {echo 'Выбранная Вами дата: '$data_user' является торговым днем<br>&nbsp<br>Цена на открытии торгов: 'substr($data_pole[2],0,6), ' р.<br>''  Минимальная цена: 'substr($data_pole[3],0,6), ' р.<br>''  Максимальная цена: ',  substr($data_pole[4],0,6), ' р.<br>''  Цена закрытия: 'substr($data_pole[5],0,6), ' р.<br>''  Объем торгов составил: '$data_pole[6], ' р.';}
?>

В общем, как всегда - огромное спасибо Вам Slash! Без Вас и Вашего форума - даже не представлял как подступиться к решению подобной задачи. Код получился рабочим и я его уже встроил в статью "Акции Татнефть". Теперь пользователь может узнать цену на акцию в любой день за период с 2002 по 2016 год. Это мой первый в жизни код на PHP, который для меня оказался реально полезным. Прыгаю от счастья как слон после трехведерной клизмы :lol:

Код работает только в пределах имеющихся в файле дат. Ввод дат пользователем ограничил с помощью календаря jquery. Впрочем, об этом календаре я написал подробный пост на этом форуме : WordPress: выполнение PHP кода в статьях

P.S. С удовольствием продолжаю свое обучение на этом форуме. Форум - настоящий кладезь информации для начинающего вебмастера. Особенно радует, что его администратор, создатель, редактор и писатель в одном лице ежедневно присутствует на форуме и готов помогать всем страждущим форумчанам.

Вернуться в «PHP»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость