Strings::matchAll/substring – UTF-8
- Kcko
- Člen | 470
EDIT:
Řešení: https://stackoverflow.com/…5329/1519236 (délka řetezce vs
bajty)
Ale stejně mě zajímá jak to řešíte vy?
===
Ahoj,
potřebuji najít pozici slova v textu (a pak ukázat kousek textu před a po, resp víc slov hledám, ale pro demonstraci, stačí 1 slovo).
Mám tedy nějaká text, zkusím vyhledat třeba slovo „Richter“ (potřebuji aby to umělo s diakritikou i bez), takže jsem použil regexp a zjištuju kde je zase problém.
- funkce strpos samozřejmě nefunguje dobře (díky multi-byte kódování ukáže jinou pozici než by měla)
- funkce mb_strpos se zachová správně (kdo by to byl čekal ;-))
- preg_match_all se chová stejně „hloupě“ jako strpos
- našel jsem v komentářích starou funkci upravenou o „MB“ chování a ta funguje dobře (jako mb_strpos) více na php.net (https://www.php.net/…atch-all.php#…)
- vyzkoušel jsem si matchAll z balíčku Strings a funguje stejně špatně jako preg_match_All
Zde dump https://bit.ly/3E4zxZA jako důkaz.
Použití: ( ve $wordsJoin mám [rřRŘ][iíIÍ][cčCČ][hH][tťTŤ][eéEÉ][rřRŘ] )
\Tracy\Debugger::barDump(Strings::matchAll($editedString, '~'.$wordsJoin.'~iu', PREG_OFFSET_CAPTURE), 'matchAll');
Jako je mi jasné, že to je díky MB a že např. písmeno „Ř“ zabírá víc než 1 znak (víc bajtů), ale pak mi prostě Strings::substring nefunguje korektně.
Můžu tam nechat tu upravnenou funkci mb_preg_match_all … ale rád bych to vyřešil jen s Nette.
Editoval Kcko (22. 10. 2021 20:23)
- Kcko
- Člen | 470
Milo napsal(a):
Mohlo by to jít s obyčejným preg_match_all() s unicode escape sekvencí
\p{L}
a modifikátoremu
.
modifikátor u tam mám, a jak mám zapsat konkretní výčet zmaků s
\p{L}
která má představovat Unicode znak?
Tohle je můj pattern
[rřRŘ][iíIÍ][cčCČ][hH][tťTŤ][eéEÉ][rřRŘ]
Editoval Kcko (23. 10. 2021 14:12)
- Kcko
- Člen | 470
David Grudl napsal(a):
Strings::lenght(substr($s, 0, $offset)) by mělo pro převod fungovat
Ano, to funguje, je to ekvivalent toho řešení ze SO co jsem házel.
Já nejsem nějak znalý kódování a problémů s tím spojením, pro někoho
kdo je, by tohle problém asi nebyl, ale mě to chování preg_match_all a mb_*
funkcí překvapilo.
Nešlo by to nějak; nejaký parametrem znormalizovat, aby to bud vždy bralo bajty nebo znaky, nebo si to nějakým dalším parametrem uvnitř samo „srovnalo“?
- Milo
- Nette Core | 1283
@Kcko Myslel jsem to jako řešení celého problému: „Chci najít
slovo a část před a za“. Že by ti ty části před a za rovnou vrátil
regulár a nemusel bys je potom z toho textu vytahovat. Například takhle pro
slovo pozici
:
/(^|(?:\p{L}+\P{L}+){1,3})(p[oó][zž][ií][cč][ií])($|(?:\P{L}+\p{L}+){1,3})/uigm
Tři části:
- začátek, nebo jeden až tři slova před
- slovo
pozici
s, nebo bez diakritiky - konec, nebo jeden až tři slova za
https://regex101.com/r/37Fy2i/1
Je to zjednodušené, například slovo = pouze písmena.
- Kcko
- Člen | 470
Milo napsal(a):
@Kcko Myslel jsem to jako řešení celého problému: „Chci najít slovo a část před a za“. Že by ti ty části před a za rovnou vrátil regulár a nemusel bys je potom z toho textu vytahovat. Například takhle pro slovo
pozici
:
/(^|(?:\p{L}+\P{L}+){1,3})(p[oó][zž][ií][cč][ií])($|(?:\P{L}+\p{L}+){1,3})/uigm
Tři části:
- začátek, nebo jeden až tři slova před
- slovo
pozici
s, nebo bez diakritiky- konec, nebo jeden až tři slova za
https://regex101.com/r/37Fy2i/1
Je to zjednodušené, například slovo = pouze písmena.
Díky, vypadá to zajímavě. Já v tom mám trošku hlubší logiku, ten text, ze kterého uřezávám, může mít třeba 2000 znaků to slovo tam může být víckrát (což ani není problém), ale můžu napsat třeba jarní pozice 2021, což jsou 3 slova a 2021 může být až na konci a jarni pozice někde na začátku a ja z toho substringem a vlastní logikou, ten text uřezávám tak, aby to dávalo vždy cca stejný počet znaků, aby ve výsledku nebyl jeden blok s 2000 znaky a podruhé s 300 znaky.
Ale mrknu na to ještě, vyzkouším a třeba to upravím, díky za rozšíření obzoru.
- Milo
- Nette Core | 1283
Zní to trochu, jako bys implementovat full-text search :o)
Kdybys stál o to pročíst, co vše to „správně“ obnáší, tak na https://www.postgresql.org/…tsearch.html je hezký ucelený přehled.
- Kcko
- Člen | 470
Milo napsal(a):
Zní to trochu, jako bys implementovat full-text search :o)
Kdybys stál o to pročíst, co vše to „správně“ obnáší, tak na https://www.postgresql.org/…tsearch.html je hezký ucelený přehled.
No v podstatě to tak je :-).
Měl jsem na výběr, bud tam vrazit
- ElasticSearch (který neumím ještě zcela dobře)
- Nechat tam tupý
LIKE
- Použít MySQL FullText, který funguje relativně dobře.
Vybral jsem si poslední možnost s tím, že jsem chtěl na výpisech vyhledávání, ještě hezky obarvit hledané slovo či slova a poskytnout pro lepší přehled výsek textu s danou frází, což se mi povedlo (tohle je samozřejmě věc už PHP, s MySQL fulltextem to už nemá nic společného).
Způsobem, kterým jsem k tomu došel, nepovažuji za zcela optimální a
relativně komplikovaný :-), proto jsem ještě hledal něco smysluplnějšího
viz moje předchozí dotazy (ale tady se už jedná jen o ty pozice slov
v textu viz opět předchozí dotazy).
V podstatě mě jako člověka, co kódování a znaky nikdy moc neřešil
právě překvapil ten problém znak vs bajt.
Jinak to funguje docela hezky :-)
(Když bude zájem, poslu do PM URL na vyzkoušení, ale předpokládám, že
máš svojí práce dost ;-)).
PS. Vyhledávání není až zas tak důležitá část webu, je to obecní web, jsou tam důležitejší sekce, jen jsem to chtěl mít lepší než předchozí autor :-), což se určitě povedlo.
Díky Milo!
Editoval Kcko (25. 10. 2021 11:09)
- Milo
- Nette Core | 1283
Rozumím.
Trochu si tu zapropaguju… Kdybys použil PostgreSQL, stačily by ti 4 věci:
- nahrát do PostgreSQL český slovník
- přidat tabulce autogenerovaný sloupec s text-search vektorem (bez triggeru)
- při hledání použít proceduru ts_headline(), která ti výsledek rovnou obarví
- něco pro zábavu, protože bys to měl tak rychle, že bys nevěděl co s časem ;o)
- Kcko
- Člen | 470
Milo napsal(a):
Rozumím.
Trochu si tu zapropaguju… Kdybys použil PostgreSQL, stačily by ti 4 věci:
- nahrát do PostgreSQL český slovník
- přidat tabulce autogenerovaný sloupec s text-search vektorem (bez triggeru)
- při hledání použít proceduru ts_headline(), která ti výsledek rovnou obarví
- něco pro zábavu, protože bys to měl tak rychle, že bys nevěděl co s časem ;o)
:-D
OK někdy nabuduce. Hele CMSko máme na Nette 3, používáme MySQL, takže PostgreSQL nelze (neumím jej a upravovat CMS kvůli tomuhle nedává smysl).
Btw data ze starého (ještě současného webu) jsem převáděl z PostgreSQL do MySQL, málem jsem se u toho rozbrečel (naštěstí tam nejsou procedury, triggery), takže jsem si s tím nakonec poradil :o) (poloruční práce, pár regexpů kvůli odlišené vyexportované syntaxi, ale dal sem to).
:) dík