Autor Zpráva
unlucky
Profil
Mam soubor o velikosti 15Mb. Uvnitr je struktura:

@slovo /vyslovnost/
*  slovni druh
- vyznam1
- vyznam 2
=mozne slovni spojeni+ vyznam
- dalsi vyznam
=mozne slovni spojeni2+ dalsi vyznam2
!chytak
- vyznam

Dalsi...

zatim mam toto:
$soubor=file_get_contents("soubor.txt");
$lines = explode("\n", $soubor);
foreach ($lines as $line) {
  $pos = strpos($line, "@");
  if($pos===false){
    echo "<br />vyznam: $line";  
  }else{
    echo "<br />slovo: $line";
  }
}

Takto pres podminky zjistil, jestli je to vyznam, chytak ci dalsi mozne slovni spojeni. (pomoci stristr nebo strpos). Soubor bych nejspise rozdelil na mensi casti. Chci se zeptat, zda je s kodem vse v poradku a nelze ho nejak vylepsit
Tori
Profil
Řádek 6 se týká toho slovního spojení z ř.5, nebo samotného slova?
Totéž co file_get_contents + explode dělá i funkce file. Paměťově úspornější by bylo sekvenční čtení funkcí fgets. Nerozlišujete typ záznamu (první znak na řádku) - teda kromě slovo/ostatní. Místo strpos by šlo použít i pozici znaku v řetězci:

  $text = ltrim($line, '@*-=! ');
  if($line{0} === '@')
    echo "<br />slovo: $text";  
  elseif ($line{0} === '!')
    echo "<br />pozor, chyták: $text";
  elseif ($line{0} === '*')
    echo "<br /><i>$text</i>";
  else
    echo "<br />význam: $text";
A čím větší bude datový soubor, tím by bylo rychlejší použít databázi, navíc byste mohl najít slovo i při zadání bez diakritiky.
unlucky
Profil
Prave, ze jsem chtel tyto data hodit do databaze. Zatim jsem to musel rozdelit na mensi soubory o velikosti 3Mb (zhruba 100 tisic radku). Struktura by vypadala takto:

tab word id, text
tab interpretation word_id, meaning_id
tab meaning id, text, type

Tech jazyku bude daleko vice. A -> B, B -> A, A-> C, C->A atd.. Hlavnim jazykem bude A. Premyslim, zda nepouzit pro kazdy jazyk vlastni tabulky nebo jeden univerzalni (pridat sloupec smer do tab interpretation?), kdyz 1 jazyk/smer ma zhruba 250tis radku (vcetne vyznamu)= 12tis slov. Celkem souboru mam 9, tedy dohromady asi 2mil zaznamu, coz je celkem velke cislo. Nevim, co ocekavat a cemu se vyvarovat.

Jeste se chci zeptat, jak ukladat tento radek:
=mozne slovni spojeni2+ dalsi vyznam2
Za rovna se je slovo pouzito v nejake vete a za plusem vyznam, nekdy je pod tim jeste dalsi vyznam dane vety.
Nekdy ma slovo vice vyznamu z hlediska slovniho druhu:

@show
* podst. jmeno
- predstaveni
=show neco+ vyznam
* sloveso
- ukazat
=show me+ ukaz mi
Jak to tedka ukladat?
Tori
Profil
A chcete to dělat spíš ve stylu slovniky.centrum.cz (cizí slovo -- jednoslovný ekvivalent), anebo jako TheFreeDictionary (cizí slovo -- delší popis významu)? V prvním případě by se to snad dalo řešit jednou velkou tabulkou slov ze všech jazyků, a vazební tabulkou, která propojí ekvivalenty. V druhém případě by se musely navíc rozlišovat i jednotlivé varianty významů (např. 1.tabule ve smyslu svislá deska + varianty: tabule na kterou se píše, okenní tabule; 2.tabule ve smyslu prostřeného stolu), protože pravděpodobně se v jiném jazyku řeknou každá jinak.
unlucky
Profil
Kdyz se na to tak divam, tak format dat ze souboru vypada jako freedict. Kazdopadne to budu ukladat jako 1 velkou tabulku se vsemi slovicky a pak kazdy vyznam/radek ukladat do druhe tabulky. Bude to jako to ma centrum. Jak jsem jiz psal, nevim, jak moc to bude problem s sql dotazy, kdyz tam bude zhruba 2 miliony radku v tabulce s vyznamy a 150 000 slovicek (zde pouziju naseptavac).

@show
* podst. jmeno
- predstaveni
=show neco+ vyznam
* sloveso
- ukazat
=show me+ ukaz mi
Zde nevim, jestli ukladat

show
show neco
show me

jako 3 ruzna na sobe nezavisla slova nebo ty posledni 2 hodit do vyznamu
unlucky
Profil
Nakonec se mi podarilo dat dohromady tenhle kod. Soubor sice ma 500 tis. radku, ale v easyphp jsem nastavil exec timeout na par hod takze, snad za par hod bude vse ok.

<?php
header("Content-Type: text/html; charset=utf-8");


$db=@mysqli_connect("localhost","root","","xxx");
mysqli_query($db,"SET NAMES utf8");

date_default_timezone_set('Europe/Prague');  
SetLocale(LC_ALL, "Czech");
$lines=file("testdata.txt");
$from_lang='en';
      $to_lang='cs';
function insertni($text,$type,$word_id,$db,$add=''){
      $from_lang='en';
      $to_lang='cs';
      $meaning=mysqli_real_escape_string($db,trim(strip_tags($text)));
      
      mysqli_query($db,"insert into means (meaning,type,additional_meaning) values('$meaning','$type','$add')");
      $meaning_id=@mysqli_insert_id($db);
      mysqli_query($db,"insert into interpretation(word_id,meaning_id,from_lang,to_lang)
      values('$word_id','$meaning_id','$from_lang','$to_lang')");
}

foreach ($lines as $line_nr => $line) {
  //if($line_nr>185642){
    $line=trim($line); //trim pro podminky
    $text = ltrim($line, '@*-=! '); //trim prvni znaky pro pouziti
    
    if(!empty($line)){
      if($line{0} === '@'){//kdyz je slovo
      
        $vyslovnost=explode("/",$text);
           
        if(!isset($vyslovnost[1])){//kdyz neni vyslovnost
          $vyslov='';
        }else{
          $vyslov=trim($vyslovnost[1]);
          $text=trim($vyslovnost[0]);
        }
        
        //echo "<br /><br /><strong>$text</strong> vyslovnost: ".$vyslov;
        $sql_text=mysqli_real_escape_string($db,strip_tags($text));
        $check_word=mysqli_fetch_assoc(mysqli_query($db,"select id from word where word='$sql_text'"));
        if($check_word['id']!=''){//kdyz existuje
          $word_id=$check_word['id']; //ulozime id do promenne        
        }else{//kdyz neexistuje pridame a ulozime
          $word=$sql_text;
          $spelling=mysqli_real_escape_string($db,trim(strip_tags($vyslov)));
          mysqli_query($db,"insert into word(word,lang,spelling) values('$word','$from_lang','$spelling')");
          $word_id=@mysqli_insert_id($db);      
        }
      }
      elseif ($line{0} === '*'){
        //echo "<br />&nbsp;slovni druh: $text";
        $type='sldruh';
        insertni($text,$type,$word_id,$db);
      }           
      elseif ($line{0} === '!'){//fraze
        $type='fraze';
        //echo "<br />&nbsp;&nbsp;&nbsp;&nbsp;fraze: $text";
        insertni($text,$type,$word_id,$db);
      }
      elseif ($line{0} === '-'){
        $type='vyznam';
        //echo "<br />&nbsp;&nbsp;&nbsp;&nbsp;vyznam: $text";
        insertni($text,$type,$word_id,$db);
      }   
      elseif ($line{0} === '='){
        $type='vysvetleni';
        
        $rozdel_vyznam=explode("+",$text);
           
        if(!isset($rozdel_vyznam[1])){//kdyz neni vyslovnost
          $add='';
        }else{
          $add=trim($rozdel_vyznam[1]);
          $text=trim($rozdel_vyznam[0]);
        }
        //echo "<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dalsi vyznam: $text";
        
        insertni($text,$type,$word_id,$db,$add);
      } 
  
    }  
  //}//konec line nr
}


?>

Zaimalo by me radek s "//if($line_nr>185642){"

Skript skoci primo na ten radek, nebo projde znovu od jednicky? Rozdelit tedy velky soubor na mensi tim, ze nastavim radky nebo cely soubor najednou a zvetsit akorat exec time?
Alphard
Profil
Nepřejde hned na daný řádek (což by v tomto případě nebyl problém zařídit), ale vynechá zpracování, takže menší hodnoty "procyklí" řádově rychleji.
unlucky
Profil
ajo, blba otazka..je tam foreach. Kazdopadne 500tis radku, zpracovat cely soubor ci nejak zmensit?
Alphard
Profil
Tohle je jednorázové zpracování doma? Zkusil bych pro pár řádků, jestli to funguje, a pokud ano, tak to pustil a čekal. Proč si přidávat práci zmenšováním souboru...
unlucky
Profil
Pustil jsem skript, ktery ma ukladat data ze souboru do databaze. Soubor ma 500 000 radku. Na netbooku (1.6ghz, 512 ram) se mi podarilo za 3000sek dojit az k 1/3. Na PC (nejakych dual 2.4 2mbram). Vse mam stejne nastavene a stejny skript.

Nyni uz je to 20minut a jsem teprve na radku 7500. Jak toto cele urychlit?
DJ Miky
Profil
To se dá jen hádat, když k tomu není více informací. Osobně bych si tipnul, že ti to na PC brzdí disk, přičemž v netbooku máš rychlejší SSD.

Obecně to můžeš zrychlit třeba neposíláním insertů do databáze po jednom, ale posílání více řádků naráz v jednom insertu (INSERT INTO ... VALUES (...), (...), (...);). Tím se zmenší počet přepínání kontextu a overhead v podobě řízení transakcí v databázi (každý samostatně vložený řádek spustí transakci, vloží data a commitne transakci).
Tori
Profil
Dá se napsat lepší algoritmus. Neprovádíte žádné složité operace s daty, takže nejpomalejší část jsou zřejmě dotazy do DB. Dá se snížit jejich počet mnoha způsoby - jednak viz [#11]. Potom místo dohledávání ID ke každému slovu (ř.43) můžete prostě každé slovo, které vkládáte do DB, zároveň hodit do pole (slovo => id) a místo SELECT dohledávat fcí isset.

Taky se dají nejdřív poukládat všechna data do tabulky word a ostatní si odkládat do jiného pole pod word_id. Po uložení všech jednotlivých slov potom z toho druhého pole (významů) poskládáte hromadné inserty, třeba po 50 - 100 záznamech. V tom případě by bylo potřeba místo file načítat datový soubor přes fgets, ať se nezahlcuje paměť.
unlucky
Profil
Napada me akorat, ze na netbooku mam windows 7 a na PC, sice novejsi/rychlejsi/lepsi, ale stary XP

Jenom pro porovnani, NTB zpracuje 10 000 radku za 3 min, na PC hodina
Alphard
Profil
Jestli se v tom chcete vrtat a hledat příčinu, začal bych měřením času, jak dlouho trvají dotazy do databáze.
Pokud jde čistě o optimalizaci, viz kolegové výše.

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: