Egy kis szkript segítség kellene

Egy kis szkript segítség kellene

Hozzászólások

Hogyan?

Magát a szkriptet (felolvas.uj.sh) még egy-két napig megtalálod itt:
http://maildotmeeidothu/files/ttskieg.tar.bz2

A csomag kb. 700k, mert benne van a szkript által használt english/hunglish szószedet is.

Ez a csomag egyébként már csak egy utólagos kiegészítés az eredeti felolvasó csomaghoz:
http://maildotmeeidothu/files/ttsinst.tar.bz2

A fő gondot egyébként az okozta, hogy az ispell-en pipe-al átszűrve a szöveget, csak a szavak jutottak át, míg a számok és az írásjelek elvesztek.

Ezt végül két trükkel oldottam meg:

A) a számokat szöveggé alakítottam még mielőtt az ispell-be kerültek volna

B) a szöveg felolvasáskor a hangsúly miatt lényeges írásjeleket (-!?.,) szintén még az ispell előtt bekódoltam a sed-el ilyenekké: "@aaaa" meg "@aaab". A szóeleji @ hatására az ispell automatikusan helyes szóként fogadja el őket, az ispell-en átjutva pedig visszaalakítom őket írásjelekké (szintén a sed-el). A többi írásjelet viszont veszni hagyom, mert a szöveg felolvasásakor amúgy sincs semmi szerepük.

Adva van ez a szöveg:
John mondta: "Happy new year", ami azt jelenti hogy boldog új évet.

Kellene egy szkript, ami az angol szavait "hunglish"-ba tenné át, azaz a kimenete ez lenne:
John mondta: hepi nyú jír, ami azt jelenti hogy boldog új évet

A szkriptet az ispell és egy english/hunglish szószedet (hunglish.txt) segítheti, ami kb. 140000 soros, és a sorai így néznek ki:
...
happy hepi
new nyú
year jír
...

Már írtam is erre valamit, ami majdnem működik:
echo "John mondta: \"Happy new year\", ami azt jelenti \"boldog új évet\"" | ispell -a -d hungarian | grep -v -E "^@" | awk '$1~/^[*+]$/ {print $2 }; $1~/^[#&]$/ {print $2 | "xargs -i\"[]\" egrep -m 1 -i ^[] hunglish.txt | sed \"s/.* //\""}' | tr "\n" " "

Egy komoly gond van a fenti szkripttel: felcseréli a szavakat.
Nyilván azért, mert az awk-n a szavak nem érkezési sorrendben, hanem aszerint jutnak át, hogy melyikkel mennyit kell dolgoznia (vagy esetleg az awk-n belüli pipe miatt?), úgyhogy a hunglish szavak, amiket az awk-nak a szótárban kellett kikeresnie mindig a szöveg végére csúsznak:
mondta ami azt jelenti hogy boldog új évet dzson hepi nyú jír

Ki lehetne valahogy úgy javítani a fenti awk parancsot, hogy az awk-n a szavak az eredeti sorrendben jussanak át, és közben az angol szavakat a megfelelő hunglish szóra cserélje a szótár segítségével?

Biztos ki lehet. De elvbol meg se nezem. A "hunglish" ahogy te hivod, az egyik legidiotabb otlet amit valaha hallottam. Es a fo oka, hogy a legtobb magyar ugy beszel angolul, hogy hanyni lehet tole. Csak azert, mert a magyar nyelv hangvilaga eleg gazdag, hogy halvany hasonlosaggal meg lehet kozeliteni vele nemely angol szavak kiejteset, meg nem azt jelenti, hogy azt ugy is kell csinalni.
Egy helyesebb megoldas lenne, ha az angol szavakat angol felolvasoval olvastatnad. Es meg tanulnal is vele. Mindez szvsz...

Én pedig szintén ELVBŐL nem vitatkozok veled, mert nem akarok egy tisztán programozási kérdésből egy újabb flame topic-ot csinálni.

Ráadásul tudom, hogy a végeredmény engem fog igazolni :)

Erre már inkább javasolnám a perl-t:
[code:1:9e82e9cae4]
#!/usr/bin/perl -w
use strict;
my (%dict, @words, $FD, $key, $value);

# olvassuk be a szotarat a %dict hash-be
open($FD, "hunglish.txt") || die("Can't open dict file");
while (<$FD>)
{
chomp; # sorvegjel eltakaritasa
($key, $value) = split(); # whitespace menten vagjuk ketfele
# ha minden rendben, tegyuk a hash-be
defined($key) && defined($value) && ($dict{$key} = $value);
}
close($FD);

while (<>) # vegig az stdin sorain eof-ig
{
@words = split(); # csapjuk szet a tombbe szavankent
# vegig a szavakon: ha szerepel a szotarban, helyettesitunk
# ha nem, akkor hagyjuk, ahogy van
for my $word (@words) { defined($dict{$word}) && ($word = $dict{$word}); }
# osszefuzzuk a szavakat szokozzel elvalasztva, sorjelet a vegere, es kiirni
print join(' ', @words)."\n";
}
[/code:1:9e82e9cae4]
A hunglish-t illetően egyetértek az 1aca kollégával, de technikailag nézve a dolgot, jó a feladat :).

Sajnos közben rájöttem, hogy a bash szkriptem nem felel meg a célkitűzésemnek: már nem real-time, azaz meg kell várni amíg az egész könyvet feldolgozza, mielőtt a felolvasás elkezdődik.

Mivel az egész szkript csupa pipe, arra jutottam, hogy a késedelmet csak ez a konstrukció okozhatja benne:

(for szo in $(cat);
szótárazás (vagy nem, attól függően, hogy a szót az ispell helyesnek jelölte-e)
done) |

Úgyhogy a bash szkript ugrott. (Azazhogy lehet hogy mégis megmarad, de akkor írok hozzá egy wrapper szkriptet is, ami kissebb darabokra bontva olvastatja fel a szöveget, hogy ne legyen hosszú késedelem, mielőtt a szöveg felolvasása elkezdődik. Ebből még előnyt is lehetne kovácsolni: elvileg egy így könnyen lehetne előre/hátra léptetni a felolvasott szövegben, míg egy real/time kivitelben nem).

Viszont mindenféleképpen szeretnék egy valóban real-time szkriptet is készíteni, úgyhogy azt hiszem ideje felfrissíteni szerény perl tudásomat.

üdv gsimon :)
szintén perl, szintén egyetértés, kicsit másképp (talán kicsit érthetőbben, igyekeztem nem kihagyni a $_-ket, nem tudom mennyire értesz a perlhez ugyanis):

[code:1:5f7ea4b3b1]
#!/usr/bin/perl

# regexp miatt kell:
use locale;
use POSIX qw(locale_h);
setlocale(LC_CTYPE, "hu_HU.ISO8859-2");

my $dict = {}; # szotar
my $fdict = "words.txt"; # szotar file
my $fstory = "story.txt"; # szoveg file

lddict();
trstory();

sub lddict {
open(F, $fdict);
while(<F>) {
my($ew,$hw) = split(/\s/,$_);
$dict->{$ew} = $hw;
}
close(F);
}

sub trstory {
open(F, $fstory);
while(<F>) {
$_ =~ s[(\w+)] [ sprintf( $dict->{$1} ? $dict->{$1} : $1 ); ]eg;
print $_;
}
close(F);
}
[/code:1:5f7ea4b3b1]

Hoppá: már majdnem megvan a megoldás!
Méghozzá az eredeti awk-s parancsom egy kicsit megmódosítva:

echo "\`"$'\n'"John mondta: \"Happy new year\", ami azt jelenti \"Boldog új évet\"" | ispell -a -d hungarian | awk 'BEGIN {parancs = "xargs -i\"[]\" egrep -m 1 -i ^\"[]\" hunglish.txt"} $1~/^[*+]$/ {print $2} $1~/^[#&]$/ { print $2 |& parancs; parancs |& getline eredmeny; print eredmeny} END {close(parancs)}' | sed 's/.* //'| tr "\n" " "

Az awk parancs elején (BEGIN) nyitok egy kétirányú pipe-ot egy külső "xargs -i\"[]\" egrep -m 1 -i ^\"[]\" hunglish.utf8.txt"" parancshoz, amit a végén (END) lezárok. Közben pedig sorról-sorra beküldöm a pipe-ba a szótárazandó szavakat (és csak azokat), és vissza is olvasom a pipe-ból az eredményt.

Csak egy gond van ezzel: a stream elakad, ha a grep nem talál meg egy szót a szótárban :(.
Úgy néz ki, hogy ha a grep nem talál semmit, akkor az awk az idők végezetéig várja az eredményt a pipe-ból. Eszerint vagy a grep nem ad vissza még egy vacak newline-se ha nincs találat, vagy az awk readline parancsa nem tekinti a pipe-ból érkező newline-t érvényes sornak, és tovább vár.
Mindenesetre nem tűnik nagy gubancnak, szerinten rövidesen ezt is megoldom valahogy.

Köszi a válaszokat, de sajnos a dolog ennél jóval bonyolultabb; ha csak ennyi lenne, akkor egy sima egrep "^$szo " | awk '{ print $2}' is megtenné :(

A gondot az ispell-en való átszűrés okozza; egy részről végtelenül hasznos, mert töredésére csökkenti a szótárban keresendő szavak számát (csak azokat kell szótárazni, amiket a magyar helyesírásellenőrző hibásnak jelzett, tehát lehet, hogy angol szavak); más részről viszont sajnos az ispell:

- minden szót külön sorba tör, és a sor első karaktere jelzi, hogy a szó helyes-e (helyes: "*" vagy "+" karakterek; helytelen: "#", "&", "?" karakterek)

- mindent elhagy a bemenetére érkező szövegbőll, ami nem betű. Azaz a szövegben lévő számok és írásjelek nem jutnak át az ispell-en, és ez nagyon nagy bukta :(

Persze, ha nem ragaszkodnék ahhoz, hogy a szöveget egy stream-ként egyetlen pipe-al szűrjem át az ispell-en, nem lenne semmi gond gond; dehát mégse indíthatok minden ellenőrzendő szóhoz egy ispell processzt?

Van erre perl-ben valami megoldás?

Meg is van a végleges megoldás:
echo "\`"$'\n'"John modta: \"Happey new year\", ami azt jelenti \"Boldog új évet\"" | ispell -a -d hungarian | awk 'BEGIN {parancs = "xargs -i\"[]\" egrep -m 1 -i ^\"[]\" hunglish.txt"} $1~/^[*+]$/ {print $2} $1~/^[#&]$/ { print $2 |& parancs; close(parancs, "to"); if ((parancs |& getline eredmeny) > 0) print eredmeny ; else print $2; close(parancs,"from")} END {close(parancs)}' | sed 's/.* //'| tr "\n" " "

Az ok az volt, hogy a grep nem zárta le az output buffer-ét, ha nem talált semmit. Most minden adatsoron lezárom és megnyitom a pipe-ot, és így már működik.

Már csak egy dolog van hátra: ha nincs az angol szótárban a szó, megnézni, hogy nincs-e a végén egy magyar toldalék (a fontosabb toldalékok egy sztringben vannak megadva), és ha van, csakja le a végéről, és a szótövet keresse újra a szótárban.

Flame: Ahogy én magamat ismerem, meglesz ez is nemsokára. :)

A problémát közben megoldottam bash-ben.
Köszi a segítséget.

[quote:fc20ba7662="j_szucs"]Én pedig szintén ELVBŐL nem vitatkozok veled, mert nem akarok egy tisztán programozási kérdésből egy újabb flame topic-ot csinálni.

Ráadásul tudom, hogy a végeredmény engem fog igazolni :)

Flame? Én elmondtam a véleményemet a felvetett problemáról, és ajánlottam helyette egy más megoldást. Hol van itt a flame?

Amúgy meg igazán érdekelne, mi is a cél? Mi az értelme ennek? Vagy csak agytörő? Nem hinném...És hogyan fog "igazolni" téged a végeredmény?