Sziasztok,
a problémám, hogy normalizálni szeretnék egy szövegfájlt, a sok felesleges szóközt ki törölni. Ahol több a szóköz mint egy, ott egy üres szóköz legyen. A szövegfájlban viszont vannak olyan szövegek, amik zárójelbe vannak, azon belül nem szabad normalizálnom.
Az eredeti szövegre példa:
field analysis_version datatype packed_decimal default ' 1'
ezzel:
$line =~ s/\s+/ /g;
a következőt kapom:
field analysis_version datatype packed_decimal default ' 1'
Amit viszont én szeretnék, az ez lenne:
field analysis_version datatype packed_decimal default ' 1'
Már egy fél napot elidőztem vele, de eddig kollegák közül se tudott senki segíteni.
Valaki tudja a megoldást? Köszönöm a segítséget előre is.
Szerk: bár nem jelzi a példa, de az idézöjel utáni szövegnek is normalizálódni kellene, ha van mögötte.
Köszi/bocsi
- 7573 megtekintés
Hozzászólások
phpban tudtam, de forgasd át. ez így gagyi?
$text = "field analysis_version datatype packed_decimal default ' 1'";
$text = preg_replace("/(\w)(\s+)(\w)/","$1 $3",$text);
- A hozzászóláshoz be kell jelentkezni
$text = preg_replace("/(\w)(\s+)(\w|')/","$1 $3",$text);
eze, mert az idézőjel előtt nem szedte ki
- A hozzászóláshoz be kell jelentkezni
Nincs energiám kipróbálni, de ilyesmi?
while( $file_file_content =~ s/([^']?+)('[^']?+')([^']?+)//gm )
{
my $pre=$1;
my $post=$3;
my $middle=$2;
$pre =~ s/\s+/ /g;
$post =~ s/\s+/ /g;
$result=$result.$pre.$middle.$post;
}
- A hozzászóláshoz be kell jelentkezni
Én is lusta vagyok ellenőrizni :) , de így ránézésre furcsának tűnik. Egyrészt minek a substitution (s) mikor a match (m) is elég lenne (hiszen a lényeg csak a group-ok feltöltése), másrészt a ?+ is gyanúsnak tűnik - nem ++ vagy *+ akart az lenni?
(Persze ha lehet többfelé is aposztróf, akkor az egész bukik).
- A hozzászóláshoz be kell jelentkezni
Nos akkor 2. nekifutás:
while( $file_content =~ s/([^']+?)('[^']*?')//sm )
{
my $pre=$1;
my $middle=$2;
$pre =~ s/\s+/ /g;
$result=$result.$pre.$middle;
}
A /s az elején azér kell, hogy 2x ne találjuk meg ugyan azt a részt.
A +? a "non greedy match" azaz nem a lehető leghoszabb részre illik, hanem a legkisebbre.
A cucc feltételezi, hogy a teljes inputot előzőleg beolvastuk $file_content -be. Az eredményt meg ki kell írni a végén valahová. Ha a bemenetben soronkénti rekordok vannak, akkor ezen lehet finomítani.
Szerintem ez alapján el lehet indulni.
- A hozzászóláshoz be kell jelentkezni
Á, szóval minimal match akart lenni. Közben meg észre sem vettem, hogy az egész fájlt dolgozod fel egyszerre, így már világos miért alkalmaztad a substitution-t.
Viszont a minimal match-et még nem értem. Szerintem egyrészt itt lerontja itt a műveletigényt, hiszen először a subexpression a +? esetén egy karakterre illeszkedik, a +* pedig nullára, majd illesztené a '-t; ez nem sikerül, ezért mindkét részkifejezésre többször vissza kell lépnie. Ráadásul végeredmény szempontjából itt a minimal, a greedy és a possessive match ugyanazt adja, de pont csak a greedy és a possessive adja az opimális műveletigényt.
Ha feltesszük, hogy pont olyan minden sor, ahogy a példában szerepel, akkor lehet sokkal specifikusabban is feldolgozni, úgy hogy közben az eredeti fájl-változó értéke is megmarad:
my $regex = qr/^([^']*+)('[^']*+')\h*(\n?)/m;
my $result = "";
pos($file_content) = undef;
while ($file_content =~ m/$regex/g)
{
my $normalized = $1;
my $verbatim = $2;
my $nl = $3;
$normalized =~ s/\s+/ /g;
$result .= $normalized . $verbatim . $nl;
}
(A te megoldásod viszont sokkal általánosabb).
- A hozzászóláshoz be kell jelentkezni
Szia, köszi, de sajnos nem jó, mert ha van szöveg aszimpla idézo"jel után, azt is levágja. Azt ott tovább kellene normalizálni. Nézem amit küldtél, hátha rájövök a megoldásra.
Köszönöm nektek a segí´tséget.
- A hozzászóláshoz be kell jelentkezni
És ez hogy hangzik (nem tudom kipróbálni, lehet hogy nem is fut)?
my $regex = qr/(?:([^']++)|('))/xs;
my $result = "";
pos($file_content) = undef;
my $in_quote = 0;
while ($file_content =~ m/$regex/g)
{
my $fragment;
if (defined $1)
{
$fragment = $1;
$fragment =~ s/\h+/ /g unless ($in_quote);
}
else
{
$fragment = $2;
$in_quote = 1 - $in_quote;
}
$result .= $fragment;
}
- A hozzászóláshoz be kell jelentkezni
Köszi, de hibát dob sajnos
Itt probáltam ki:
http://codepad.org/4HXdspKu
Esetleg, ha eszedbejut, hogy miért nem jó, lefuttatnád?
- A hozzászóláshoz be kell jelentkezni
(előző bejegyzés törölve)
Szerk:
Most vettem észre, hogy ezen a honlapon lehet kódot futtatni. :)
Szóval az a helyzet, hogy a
print $];
szerint ez 5.008-as Perl, ami nagyon régi. Ez még nem ismeri sem a '++'-t, sem a '\h'-t. Ennek megfelelően a következőket javaslom:
- A '++'-t '+'-szal, a '\h'-t '[\t ]'-vel helyettesítsd (utóbbiban van egy szóköz is).
- Használd a
use strict;
use warnings;
utasításokat a program elején, akkor megmondja az interpreter részletesen, hogy mi baja van. (Pl. a $file_content helyett $filecontent-et írtál).
Forkoltam a kódot itt.
- A hozzászóláshoz be kell jelentkezni
hú, köszi, Istencsászár vagy!
- A hozzászóláshoz be kell jelentkezni
Szívesen. :)
- A hozzászóláshoz be kell jelentkezni
A kutya mindenit! Eddig azt hittem, hogy Perl-ben szakértő vagyok. Most odab*sztál rendesen. Én azt köszönöm, hogy tanítottál ezzel a röpke kóddal.
- A hozzászóláshoz be kell jelentkezni
Egy lehetséges megoldás, amit holnap reggel szerintem le fogok tagadni:
perl -F/\'/ -lane 'for $i (0..$#F){$F[$i]=~s/ +/ /g if !($i%2)} push @F,"" if $#F%2;print join chr(39), @F' szovegfajl.txt
- A hozzászóláshoz be kell jelentkezni
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
int quote,
prev,
c;
quote = 0;
prev = -1;
while (EOF != (c = fgetc(stdin))) {
switch (c) {
case '\'':
quote = !quote;
break;
case ' ':
if (' ' == prev && !quote) {
continue;
}
}
if (EOF == fputc(c, stdout)) {
perror("fputc(stdout)");
return EXIT_FAILURE;
}
prev = c;
}
if (ferror(stdin)) {
perror("fgetc(stdout)");
return EXIT_FAILURE;
}
if (EOF == fflush(stdout)) {
perror("fflush(stdout)");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Nem foglalkozik különösebben újsor karakterrel; ha nincs lezárva az utolsó idézet a sor vége előtt, akkor az újsor karakter is az idézet része lesz (meg a következő sorok is, amíg el nem érünk egy idézőjelet). Ha ezt nem kívánjuk (vagyis az újsor zárja le a szökevény idézetet), akkor ennyi kell még bele:
--- squeeze.c 2012-05-09 22:39:26.000000000 +0200
+++ squeeze-nl.c 2012-05-09 22:42:32.000000000 +0200
@@ -17,6 +17,10 @@
quote = !quote;
break;
+ case '\n':
+ quote = 0;
+ break;
+
case ' ':
if (' ' == prev && !quote) {
continue;
- A hozzászóláshoz be kell jelentkezni
Ez arra is jó példa, hogy miért a nyelvet választjuk a feladathoz, és nem fordítva... :)
- A hozzászóláshoz be kell jelentkezni
Igen, ez egy kiváló példa arra, hogy nem feltétlenül jó ágyúval (regex) verébre lőni. De ha már mindenképpen ágyú, akkor van egy Perles megfejtésem: Fel kell bontani a stringet idézőjelek mentén (split) és minden második tömbelemen kell csak elvégezni a szóközök átalakítását. Aztán összerakni (join) újból az egészet.
- A hozzászóláshoz be kell jelentkezni
Ezt kb. $RANDOM nyelven is meg lehet csinalni, sztem meg pl. awk-ban is, kell egy state valtozo, hogy eppen erdekel-e minket az adott szekcio.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal
- A hozzászóláshoz be kell jelentkezni
Ez igaz, de ha egyszer ágyúval lövünk...
- A hozzászóláshoz be kell jelentkezni
Ez egy zseniális ötlet. Csak az a gond, hogy a split-nek van egy furcsa tulajdonsága: ha a fájl elején vannak elválasztójelek, akkor rendben létrejönnek a nekik megfelelő üres tömbelemek; viszont ha a fájl végén vannak, akkor azokat simán eldobja. Emiatt a join-nál el fognak veszni aposztrófok. Nem tudom miért ilyen, lehet hogy bug.
- A hozzászóláshoz be kell jelentkezni
Nálam tökéletesen működik:
while(<>) {
my @arr = split(/'/);
for(my $i = 0; $i <= $#arr; ++$i) {
$arr[$i] =~ s/\s{2,}/ /g unless($i % 2);
}
print(join("'", @arr));
}
- A hozzászóláshoz be kell jelentkezni
Kicsit belenyúltam a kódodba: a beolvasott sort kiírom < és > jelek között, az eredményt [ és ] jelek között.
Lássunk két példafájlt:
1.) f végén van újsor:
jack@hypnos:~/Asztal$ cat f
'' sfdsfd f 's x'''
jack@hypnos:~/Asztal$
2.) g végén nincs:
jack@hypnos:~/Asztal$ cat f | tr -d '\n' >g
jack@hypnos:~/Asztal$ cat g
'' sfdsfd f 's x'''jack@hypnos:~/Asztal$
Futtassuk a kódot:
1.) f-re működik:
jack@hypnos:~/Asztal$ perl -w proba.pl f
<'' sfdsfd f 's x'''
>['' sfdsfd f 's x'''
]
2.) g-re nem:
jack@hypnos:~/Asztal$ perl -w proba.pl g
<'' sfdsfd f 's x'''>['' sfdsfd f 's x]
jack@hypnos:~/Asztal$
Neked azért működött, mert feltehetően a standard inputról adtál neki bemenetet. Ilyenkor a string végén ott a \n, tehát az aposztóf nem az utolsó karakter.
- A hozzászóláshoz be kell jelentkezni
Egy kicsit pontositsuk: nem a standard input volt a problema gyokere, hanem hogy valoszinuleg echo-zta az inputot, vagy szovegszerkesztoben allitotta ossze, amely automatikusan rak egy ujsort is a fajl vegere.
Ez azert van igy, mert ha megnezed, a `cat g | perl -w proba.pl` ugyanugy a standard inputrol dolgozik, viszont ugyanazt a kimenetet fogja adni, mint a 2). A standard input lete sehol nem implikalja a fajlvegi sortorest.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal
- A hozzászóláshoz be kell jelentkezni
Így van, rosszul fogalmaztam. Arra akartam célozni, hogy elindította paraméterek nélkül, és interaktívan használta.
(Akkor az sem jutott eszembe, hogy egyáltalán hogyan lehetne interaktívan újsor nélkül beadni az inputot - megoldás: Ctrl-D).
- A hozzászóláshoz be kell jelentkezni
megoldás: Ctrl-D
Bővebben: EOF -- Special character on input, which is recognized if the ICANON flag is set. When received, all the bytes waiting to be read are immediately passed to the process without waiting for a <newline>, and the EOF is discarded. Thus, if there are no bytes waiting (that is, the EOF occurred at the beginning of a line), a byte count of zero shall be returned from the read(), representing an end-of-file indication. If ICANON is set, the EOF character shall be discarded when processed.
- A hozzászóláshoz be kell jelentkezni
Hm, valóban a Vim odatett egy \n-t, pedig kérte a fene. Egyébként sosem értettem, miért olyan fontos a \n? Régi fordítók warningoltak is ilyesmire, de nem értem az okát.
És azt sem értem, hogy miért viselkedik így a Perl. Na mindegy, minden sor végére oda lehet kézzel pöttyinteni a soremelést:
while(<>) {
tr/\n\r//d; # https://en.wikipedia.org/wiki/Newline
$_ .= "\n";
...
- A hozzászóláshoz be kell jelentkezni
Egyébként sosem értettem, miért olyan fontos a \n?
A történelmi okot nem tudom megadni.
Az ISO C99 7.19.2 p2 szerint A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. Whether the last line requires a terminating new-line character is implementation-defined. [...]
A SUSv4 ugyanakkor szigorúbb:
Line: A sequence of zero or more non-<newline> characters plus a terminating <newline> character.
Incomplete Line: A sequence of one or more non-<newline> characters at the end of the file.
Text File: A file that contains characters organized into zero or more lines. The lines do not contain NUL characters and none can exceed {LINE_MAX} bytes in length, including the <newline> character. Although POSIX.1-2008 does not distinguish between text files and binary files (see the ISO C standard), many utilities only produce predictable or meaningful output when operating on text files. The standard utilities that have such restrictions always specify "text files" in their STDIN or INPUT FILES sections.
A Rationale ezt mondja: [...] The only difference between text and binary files is that text files have lines of less than {LINE_MAX} bytes, with no NUL characters, each terminated by a <newline>. The definition allows a file with a single <newline>, or a totally empty file, to be called a text file. If a file ends with an incomplete line it is not strictly a text file by this definition. [...]
A LINE_MAX-ról bővebben itt.
- A hozzászóláshoz be kell jelentkezni
Köszi, hogy utánanéztél. Találtam még pár jó megjegyzést:
http://stackoverflow.com/questions/729692/why-should-files-end-with-a-n…
A (POSIX2_)LINE_MAX=2048 új nekem. Még sosem ütköztem problémába miatta, de nem árt megjegyezni.
- A hozzászóláshoz be kell jelentkezni
Mert meg nem probaltal ketszaz oldalas bovitett kormondatot irni :-)
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal
- A hozzászóláshoz be kell jelentkezni
Megjegyezni nem muszáj, mert a POSIX definiál API függvényeket, és parancsot is (getconf) a lekérdezéshez. Shell script írásakor talán a két legfontosabb a LINE_MAX és az ARG_MAX. Bár ez utóbbi manapság már brutális nagy érték szokott lenni.
- A hozzászóláshoz be kell jelentkezni