Obróbka pliku w PHP

0

Witajcie,
Jest plik. W pliku bloki po 56 bajtów.

  1. Teraz trzeba odczytać plik blokowo do PackedRecord (tak to się w Delphi nazywa, a w php będzie to chyba tylko tablica 56 elementowych tablic).
  2. Następnie (również w PHP) złożyć 8 pierwszych bajtów do postaci umożliwiającej odczytanie z niej wartości DateTime, a resztę po 4 bajty do postaci zmiennych typu Single i/lub LongWord.

Jak w PHP najprościej i jak najmniej obciążając serwer, odczytać plik blokowo?

Czy takie złożenie jest poprawne, czy może czegoś brakuje (?):

$DAT_path = "C:\history.dat";
$DAT_plik = fopen($DAT_path,'r');

//Na próbę odczyt pierwszych 56 bajtów...
$paczka = fread ($DAT_plik,56);

//Chyba nie dobrze / nie potrzebne ord() ...
$sk[0] = ord($paczka[8]);
$sk[1] = ord($paczka[9]);
$sk[2] = ord($paczka[10]);
$sk[3] = ord($paczka[11]);

$l = $sk[0] + ($sk[1] << 8) + ($sk[2] << 16) + ($sk[3] << 24);

Pozdro!
Michał

0

Paczka to string, odwołujesz się do niego jak do tablicy. Jeśli chcesz skonwertować na tablicę, to użyj str_split.
Z typów zmiennych są: integer, float, string, bool, tablice, zasoby, funkcje i klasy więc z typowaniem masz ułatwioną sprawę.
Dodatkowo: W PHP plus to znak dodawania. Zawsze. Jeśli chcesz połączyć typy konwertowalne używaj kropek. Przykład:

$a = 1;
$b = 2;
$c = 'aaa';

$result = $a + $b; //3
$result = $a + $b + $c; //4 (string dłuższy niż 0 znaków konwertuje się do (integer)1 jak pamiętam)
$result = $a . $b; //12
$result = $c . $a . $b; //aaa12
0
$string = file_get_contents($DAT_path);
$paczka = str_split($string,56);

$bajt[0] = ord($paczka[0][8]);
$bajt[1] = ord($paczka[0][9]);
$bajt[2] = ord($paczka[0][10]);
$bajt[3] = ord($paczka[0][11]);

Tak lepiej?

Teraz - jak z 4 bajtów złożyć liczbę zmiennoprzecinkową?

0

Nie.

$DAT_path = "C:\history.dat";
$DAT_plik = fopen($DAT_path,'r');

//Na próbę odczyt pierwszych 56 bajtów...
$paczka = fread($DAT_plik,56);
$paczka = str_split($paczka);

$sk[0] = $paczka[8];
$sk[1] = $paczka[9];
$sk[2] = $paczka[10];
$sk[3] = $paczka[11];

$sklejamy = $sk[0] . $sk[1] . $sk[2] . $sk[3];
$sklejamy = $sklejamy / 1000;

fread jest wporzo, bo można potem odczytać kolejne 56 bajtów bez problemu. Za pomocą kropek się skleja, tutaj akurat nie uwzględniłem przecinka i potem podzieliłem przez tysiąc, w każdym razie możesz zrobić tak:

$sklejamy = $sk[0] . $sk[1] . ',' . $sk[2] . $sk[3];
$sklejamy = (float)$sklejamy;
0
unpack
0

Lekko mnie przymuliło, ale już mam !
Dzięki [browar]

$paczka = fread($DAT_plik,56);

$sk[0] = ($paczka[40]);
$sk[1] = ($paczka[41]);
$sk[2] = ($paczka[42]);
$sk[3] = ($paczka[43]);

$l = $sk[0].$sk[1].$sk[2].$sk[3];

$b = unpack(f,$l);
$b = round($b[1],1);

Miało być 16.9 i jest :)

0

$b = unpack('f', substr($paczka, 40, 4));
Zamiast jakichś cudacznych kombinacji.
Albo nawet lepiej, fseek + fread tylko tego co potrzeba. Do unpack'a można podać więcej niż 1 znak w formacie - może Ci ładnie rozpakować cały rekord do tablicy, wysatrczy podać kolejne typy do rozpakowania.

0

Kombinacje zostały w wyniku powolnego dochodzenia do prawdy. I tak zamierzałem to uprościć i ubrać w funkcję bo tych danych będzie z czasem parę MB do przerzucenia :>.

Pewnie tak to się robi...?

    $blok_format = 
            'f4/'. # Get the first 4 bytes
            'f4';  # Get the next 4 bytes

$sese = unpack($blok_format, substr($paczka, 36, 8) );

Powinno wyrzucić dwie liczby typu float, a nie działa... pewnie znów coś źle robię

0

"f4/f4" oznacza:

  • 4 floaty
  • / --- prawdopodobnie warning?
  • 4 floaty

Daje to w sumie 4xfloat_size bajtów, które mają się raczej nijak do 8 bajtów które podajesz funkcji.

Polecam czytanie ostrzerzeń, błędów itd a nie wyciszanie ich przez @, error_reporting czy display_errors.

0

No tak... komentarz na pewnej stronce mnie zmylił - myślałem że:
f4 / f4 = float (4 bajty) / float (4 bajty), a nie to co piszesz.

Ehh...

$blok = unpack ('f1Pierwsza/'.'f1Druga', substr($paczka,36,8));

echo '<br>', Round($blok['Pierwsza'],1);
echo '<br>', Round($blok['Druga'],1);

Zostało jeszcze przeformatowanie daty i czasu...

$paczka = fread($DAT_plik,56);

$ukryta_data = substr($paczka, 1, 8);
echo '<br>'.$ukryta_data;
echo '<br>';

$dataczas = unpack('l1Data/l1Czas',$ukryta_data);
echo '<br>'.(date('d.m.y',$dataczas['Data']));
echo '<br>'.(date('H:i:s',$dataczas['Czas']));
0

Czytaj błedy PHP. W powyższym kodzie masz niezdefiniowane stałe, nieznane indeksy. Jak nic się nie pojawia, to polecam error_reporting(E_ALL) na początek skryptu + ewnetualnie ini_set('display_errors', 'on')

0

Teraz rzeczywiście warningi się pojawiają.
I widzę, że zapomniałem o ' ' i jest l1Data i l1Czas zamiast 'Data' i 'Czas'. [wstyd]

Poprawione, ale nie jest to właściwa data i czas.
Zapisane jest to w Delphi-owym TDateTime, a ten zawiera liczbę (typ double) a nie jak myślałem dwa 4 bajtowe longi.

Trzeba to przekonwertować na PHPowy format:

$paczka = fread($DAT_plik, 56);

$delphi_data = unpack('d1DDT', substr($paczka, 0, 8));
$DniOd_30121899 = ($delphi_data['DDT']);

$mnoznik = 24*($DniOd_30121899 - intval($DniOd_30121899));

$godzina = intval($mnoznik);
$minuty =  intval(60*($mnoznik-$godzina));
$sekundy = round(60*(60*($mnoznik-$godzina)-$minuty));

if ($sekundy == 60)
{
$sekundy = 0;
++$minuty;
}

//echo '<br>'.$godzina.':'.$minuty.':'.$sekundy;

$DataCzas = mktime ($godzina,$minuty,$sekundy,1,$DniOd_30121899-1,1900);
echo '<br>'.date('d.m.y H:i:s',$DataCzas);
0

Kolejne zagadnienie - czy da się wywołać funkcję:

function odczyt_pliku ($IROK)
/* IROK - Ilosc rekordow od konca */
{
//global $paczka;
$DAT_path = "C:\HW\history.dat";
$DAT_plik = fopen($DAT_path,'r');

$IBOK = -($IROK * 56);
/* IBOK - Ilosc Bajtow Od Konca */

fseek ($DAT_plik, $IBOK, SEEK_END);
$licznik = 0;

while (!feof($DAT_plik))
{
$licznik++;
$paczka[$licznik] = fread($DAT_plik,56);
echo '<br>'.$licznik.' ... '.$paczka[$licznik];
}
--$licznik;
Return ($paczka);
}

W sposób podobny do tego:
dekoduj_date(odczyt_pliku(1)[1]);

Czy tylko poprzez zrobienie $paczka zmienną globalną i:

odczyt_pliku(1);
dekoduj_date($paczka[1]);

1 użytkowników online, w tym zalogowanych: 0, gości: 1