Hello,
még mindig számlázó progit írok. A probléma a következő. Adott az adatbázisban egy mező, ami szövegesen tárolja a delikvens címét (magyarországi), mégpedig az utcanévtől kezdve. Pl. "Alfa krt. 9 2em 1", "Béta u. 12/A" rendkívül sok formában. Ezt kellene szétbontani egy algoritmussal (utcanév, típus, szám, épület, lépcsőház, emelet, ajtó) formára, ahol az első 3 rész kötelezően létezik, a maradék opcionális.
Valakinek van valami megoldása már erre?
Köszönettel: karnokd
Page
viewed times
#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))
20 Comments
Mezei Zoltán
Karnok Dávid
Unknown User (frimen)
Nincs olyan algoritmus ami ezt Neked hibátlanul megcsinálja..
Én a helyedben regex-el csinálnám meg, aminek "parametereit" (egy ablakban modosithatová (bővithetővé)
tenném, mert hogy lesznek kivételek az tuti, meg lesz olyan amit nem lehet sehogysem kielemezni.. ez is biztos.
Xar ügy.
Karnok Dávid
Azt hiszem sikerült kitalálni egy heurisztikát. Ha valakit érdekel:
public class Cim {
/**
* Ország.
*/
public String orszag = "Magyarország";
/**
* Település.
*/
public String telepules;
/**
* Irányítószám.
*/
public int irszam;
/**
* A közterület neve.
*/
public String kozterNev;
/**
* A közterület jellege (utca, tér, stb.).
*/
public String kozterJelleg;
/**
* Házszám.
*/
public int hazszam;
/**
* Opcionális épület rész.
*/
public String epulet;
/**
* Opcionális lépcsőház.
*/
public String lepcsohaz;
/**
* Opcionális emelet.
*/
public String emelet;
/**
* Opcionális ajtó.
*/
public String ajto;
/**
* Cím szétbontása.
* @param cim a nyers szöveg.
* @return true ha sikerült
*/
public boolean parseCim(String cim) {
Cim ovr = CF.get(telepules + "|" + cim);
if (ovr != null) {
this.kozterNev = ovr.kozterNev;
this.kozterJelleg = ovr.kozterJelleg;
this.hazszam = ovr.hazszam;
this.epulet = ovr.epulet;
this.lepcsohaz = ovr.lepcsohaz;
this.emelet = ovr.emelet;
this.ajto = ovr.ajto;
return true;
}
List<String> elemek = normalizalas(cim);
// köztérnév kiderítése
int i = 0;
while (i < elemek.size() && isTulajdonnev(i, elemek)) {
i++;
}
// legalább három szóból kell állnia
if (elemek.size() < 3) {
return false;
}
if (i >= elemek.size() - 1) {
return false;
}
StringBuilder b = new StringBuilder();
for (int j = 0; j < i; j++) {
if (j > 0) {
b.append(' ');
}
b.append(elemek.get(j));
}
kozterNev = b.toString().trim();
// köztér jelleg
kozterJelleg = elemek.get(i);
// házszám
String hazszamStr = elemek.get(i + 1);
int posthaz = i + 2;
if (isSzamtartomany(hazszamStr)) {
int idx = hazszamStr.indexOf('-');
hazszam = szamnev(hazszamStr.substring(0, idx));
} else {
if (!isSzamnev(hazszamStr)) {
return false;
}
hazszam = szamnev(hazszamStr);
}
// részcím heurisztikák
if (posthaz < elemek.size()) {
// ajtó szám
int last = elemek.size() - 1;
// néhány cím a végén postafiókot tartalmaz
if (!"Pf.".equals(elemek.get(last - 1))
|| !isSzamnev(elemek.get(last))) {
if ("ajtó".equals(elemek.get(last))) {
ajto = elemek.get(last - 1);
// előtte perjellel emelet
if ("/".equals(elemek.get(last - 2))) {
if (isSzamnev(elemek.get(last - 3))) {
emelet = elemek.get(last - 3);
}
}
}
if (posthaz < last - 2 && "/".equals(elemek.get(posthaz))) {
lepcsohaz = elemek.get(posthaz + 1);
}
// ha a hazszam utan / van utána egy érték és vége, akkor az érték
// megy, akkor az a lépcsőház lesz
if (posthaz == last - 1 && "/".equals(elemek.get(last - 1))) {
lepcsohaz = elemek.get(last);
} else {
if (isSzamnev(elemek.get(last))) {
ajto = elemek.get(last);
String last1 = elemek.get(last - 1);
if ("/".equals(last1)) {
String last2 = elemek.get(last - 2);
if (isSzamnev(last2)) {
emelet = last2;
} else
if (isRomaiszam(last2)) {
emelet = last2;
} else
if (last - 2 == posthaz) {
emelet = last2;
}
} else
if ("fszt".equalsIgnoreCase(last1)
|| "fsz".equalsIgnoreCase(last1)
|| "fszt.".equalsIgnoreCase(last1)
|| "fsz.".equalsIgnoreCase(last1)) {
emelet = last1;
}
}
}
// emelet
int idx = elemek.indexOf("em");
if (idx < 0) {
idx = elemek.indexOf("emelet");
}
if (idx < 0) {
idx = elemek.indexOf("em.");
}
if (idx > 0) {
emelet = elemek.get(idx - 1);
}
// épület
idx = elemek.indexOf("ép");
if (idx < 0) {
idx = elemek.indexOf("ép.");
}
if (idx < 0) {
idx = elemek.indexOf("épület");
}
if (idx > 0) {
epulet = elemek.get(idx - 1);
}
}
}
return true;
}
/**
* Római szám?
* @param s szöveg
* @return true ha római szám.
*/
private static boolean isRomaiszam(String s) {
for (int i = 0; i < s.length(); i++) {
switch (s.charAt(i)) {
case 'I':
case 'V':
case 'X':
case 'L':
case 'C':
case 'M':
case 'D':
break;
default:
return false;
}
}
return true;
}
/**
* Szöveg normalizálása és szavakra bontása.
* @param s a szöveg
* @return a szavak
*/
private static List<String> normalizalas(String s) {
List<String> result = new LinkedList<String>();
StringBuilder sb = new StringBuilder();
char last = '\0';
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c != ' ') {
if (c == '.') {
sb.append(c);
c = ' ';
} else
if (c == '/') {
if (last != ' ') {
sb.append(' ');
}
sb.append(c);
c = ' ';
}
sb.append(c);
} else
if (c == ' ' && last != ' ') {
sb.append(' ');
}
last = c;
}
s = sb.toString().trim();
if (s.length() > 0) {
int start = 0;
int end = s.indexOf(' ');
while (end >= 0) {
String e = s.substring(start, end);
if (!".".equals(e)) {
result.add(e);
}
start = end + 1;
end = s.indexOf(' ', start);
}
if (end < 0) {
String e = s.substring(start);
if (!".".equals(e)) {
result.add(e);
}
}
}
return result;
}
/**
* A megadott szöveg számtartomány?
* @param s a szöveg
* @return true ha számtartomány
*/
public static boolean isSzamtartomany(String s) {
int idx = s.indexOf('-');
if (idx > 0) {
if (!isSzamnev(s.substring(0, idx))) {
return false;
}
if (!isSzamnev(s.substring(idx + 1))) {
return false;
}
return true;
}
return false;
}
/**
* A megadott szöveg nagybetűvel keződik?
* @param i az index
* @param elemek az elemek lista
* @return true ha nagybetűvel kezdődik
*/
public boolean isTulajdonnev(int i, List<String> elemek) {
String s = elemek.get(i);
if (s.length() > 0) {
if (Character.isUpperCase(s.charAt(0))) {
// ha a körtér nagybetűvel van írva
if ("Krt.".equals(s)) {
return false;
} else
if ("Ligetsor".equals(s) && "Baracs".equals(telepules)) {
elemek.set(i, "Liget");
elemek.add(i + 1, "sor");
}
return true;
} else {
// Mátyás király kivétel
if ("király".equals(s) && i > 0 && "Mátyás".equals(elemek.get(i - 1))) {
return true;
}
// Március 15. kivétel
if ("15.".equals(s) && i > 0 && "Március".equals(elemek.get(i - 1))) {
return true;
}
}
}
return false;
}
/**
* A szöveg számnév?
* @param s a szöveg
* @return true ha számnév
*/
public static boolean isSzamnev(String s) {
int len = s.length();
if (s.charAt(len - 1) == '.') {
len--;
}
for (int i = 0; i < len; i++) {
if (!Character.isDigit(s.charAt(i))) {
return false;
}
}
return true;
}
/**
* Számnév értékének lekérdezése.
* @param s szöveg
* @return szám
*/
public static int szamnev(String s) {
int len = s.length();
if (s.charAt(len - 1) == '.') {
len--;
}
return Integer.parseInt(s.substring(0, len));
}
/**
* Teszt program.
* @param args argumentumok
* @throws Exception figyelmen kívül hagyva
*/
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(new FileReader("Cimek.txt"));
String line = null;
while ((line = in.readLine()) != null) {
int idx = line.indexOf('|');
Cim c = new Cim();
c.telepules = line.substring(0, idx);
if (c.parseCim(line.substring(idx + 1))) {
System.out.print(line);
int j = line.length();
while (j++ < 40) {
System.out.print(' ');
}
System.out.print(" -> "
+ c.kozterNev + " | "
+ c.kozterJelleg + " | "
+ c.hazszam + " | ");
if (c.epulet != null) {
System.out.print("Épület: " + nvl(c.epulet) + " | ");
}
if (c.lepcsohaz != null) {
System.out.print("Lépcsőház: " + nvl(c.lepcsohaz) + " | ");
}
if (c.emelet != null) {
System.out.print("Emelet: " + nvl(c.emelet) + " | ");
}
if (c.ajto != null) {
System.out.print("Ajtó : " + nvl(c.ajto));
}
System.out.println();
} else {
System.out.println("Sikertelen: " + line);
}
}
in.close();
}
/**
* Null helyett üres szöveget ad vissza.
* @param s szöveg
* @return szöveg vagy üres
*/
private static String nvl(String s) {
return s != null ? s : "";
}
/**
* Új Cim objektum létrehozása és feltöltése a komponensekből.
* @param utca az utca neve
* @param kozterJelleg a köztér jellege
* @param hazszam a házszám
* @param epulet az épület
* @param lepcsohaz a lépcsőház
* @param emelet az emelet
* @param ajto az ajtó
* @return az Cim objektum
*/
private static Cim newCim(
String utca, String kozterJelleg, int hazszam,
String epulet, String lepcsohaz, String emelet, String ajto) {
Cim r = new Cim();
r.kozterNev = utca;
r.kozterJelleg = kozterJelleg;
r.hazszam = hazszam;
r.epulet = epulet;
r.lepcsohaz = lepcsohaz;
r.emelet = emelet;
r.ajto = ajto;
return r;
}
/**
* A normál Cim objektum konvertálása az cím formátumú objektumává.
* @param from a forrás Cim objektum
* @param to a cél cim objektum
*/
public static void convert(Cim from, Cim to) {
to.telepules = from.helyiseg;
to.irszam = Integer.parseInt(from.irsz);
to.parseCim(from.utcaHazszam);
}
/**
* Cím felülbíráló map. Kulcs formátum település|nyerscím
*/
private static final Map<String, Cim> CF;
/**
* Statikus inicializáló.
*/
static {
CF = new ConcurrentHashMap<String, Cim>();
// ---------------------------------------------------------------
CF.put("Táborfalva|Honvéd u. 10/11. A ép. fsz.1.", newCim(
"Honvéd", "u.", 10, "A", null, "fsz.", "1"));
CF.put("Veszprém|Március 15. u. 1/C. 6 em. 22/a.", newCim(
"Március 15.", "u.", 1, null, "C.", "6", "22/a"
));
}
}
tvik
És az összes királyt bele fogod drótozni vagy csak Mátyásnak jár ez a kiváltság?
Böszörményi Péter
Csapó Krisztina
public String orszag = "Magyarország";
miért public? az attribútumok általában private-ok szoktak lenni, nem?
Unknown User (frimen)
Csak még fél kilómérnyi sor hiányzik belőle, amire később fogsz ráeszmélni nagy valószínűséggel.:)
Én azon csodálkozom, hogy miért nem final, vagy static Magyarország.
Tudsz valamit? Jön még egy trianon vagy ismét lesz a sogorokkal monarhia?:))
tvik
Nem vagyok egy regexp guru, de itt egy kis kód, amivel lehet próbálkozni.
Három szövegmező, a felsőbe lehet írni a regexp-et, a középsőbe a szöveget, az alsóban pedig az eredményt írja ki. Megpróbálja csoportokra szedni a szöveget, ha sikerül neki illeszteni.
import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
public class Test {
public static void main(String[] args) throws Exception {
JFrame f = new JFrame("Test");
f.setLayout(new BorderLayout());
JSplitPane s1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
JSplitPane s2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
final JTextArea regex = new JTextArea();
final JTextArea input = new JTextArea();
final JTextArea result = new JTextArea();
regex.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
result.setText(evaluate(regex.getText(), input.getText()));
}
});
input.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
result.setText(evaluate(regex.getText(), input.getText()));
}
});
s1.add(new JScrollPane(regex));
s1.add(s2);
s2.add(new JScrollPane(input));
s2.add(new JScrollPane(result));
result.setEditable(false);
f.add(s1, BorderLayout.CENTER);
f.setSize(400, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
private static String evaluate(String regex, String input) {
try {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
boolean r = m.matches();
StringBuilder result = new StringBuilder();
if(r) {result.append("[Match]\n");
for(int i=1;i<=m.groupCount();i++) {
result.append(m.group(i));
result.append('\n');
}
}
return result.toString();
} catch(PatternSyntaxException ex) {
return ex.getDescription();
}
}
}
Karnok Dávid
> És az összes királyt bele fogod drótozni vagy csak Mátyásnak jár ez a kiváltság?
A teszt adatok között szerepelt egy ilyen utcanév. Kisbetűvel volt írva a király, ezért az algoritmus már nem sorolta a köztérnévhez. Ugyanez a helyzet a "Krt." szöveggel, ahol viszont a köztérjelleg volt nagybetűvel, ami miatt megint nem adott helyes eredményt.
A teszt adathalmazt meg egy google kereséssel szereztem.
> Jobban járt volna a világ, ha hentesnek tanulsz!:-)
> Csak még fél kilómérnyi sor hiányzik belőle, amire később fogsz ráeszmélni nagy valószínűséggel.:)
Mikor bepostoltam, akkor láttam igazán, hogy ilyen hosszú a kód. Hát ugyen nincs szerkesztés...
> miért public? az attribútumok általában private-ok szoktak lenni, nem?
Mivel én írom az osztályt, és azt a helyet, ahol felhasználom, ezért nem szivatom magam get()/set() metódusokkal. Továbbá nem library kód, és más rajtam kívül nem fog vele továbbfejleszteni, nem lesz többszálasan használva vagy beanként használva. Ezt úgy hívják "smart record". Mivel a java-ban nincsenek rekordok, ezért kénytelen vagyok osztályokat használni.
> Én azon csodálkozom, hogy miért nem final, vagy static Magyarország.
> Tudsz valamit? Jön még egy trianon vagy ismét lesz a sogorokkal monarhia?:))
Az ország nem kerül tárolásra a forrás rendszerben, így explicite megmondták nekem. Az mindig Magyarország. Azért nem final meg static, mert egyszer talán továbbfejlesztik a forrás rendszert is...
> Kivételesen Frimen-nel értek egyet, tényleg talán regexp-pel lehetne ezt jól megcsinálni.
Nyilván lehet valami regexp-et rátenni, de úgy találtam, hogy többre jutok a heurisztikával, mint egy regexp-pel. Pont a szintaktikai problémák miatt. A legelején gondoltam arra, hogy a köztérjelleget úgy keresem meg, hogy egy előre gyártott listát (u., utca, út, sor, tér, stb) keresek a szövegben, és a találat után ami előtte van az az utcanév, ami utána az meg a házszám. Kiderült az abev2006-ból, hogy összesen 160 féle köztérjelleg ismert az adóhatóság előtt, és ezek még csak nem is rövidítések. El lehet képzelni hányféle név
Mezei Zoltán
Ezt hívják úgynevezett gányolásnak. Ha holnap a fejedre esik egy műhold, akkor más fogja továbbfejleszteni a kódot, és szidni fog téged is keményen, mivel nem úgy fog kinézni a kód, ahogy megszokta.
Karnok Dávid
Ha holnap fejemre esik a műhold, akkor senki nem fogja továbbfejleszteni. Jó eséllyel dobják a projektet - és nem a kódstílus miatt, hanem azon rengeteg tudás miatt, ami velem szállt a sírba. Egyébként, ha megvan a source kód és rendesen le van javadoc-olva, akkor nincs semmi gond. Én következetesen így programozok.
tvik
Tesztadatokat a kódba égetni... hmm.
A köztérjelleget és a közterület nevét úgy nyerném ki, hogy átugrom az első két szót, aztán megkeresem az első szót, ami számmal kezdődik. A talált szó a házszám, az azelőtti szó jó eséllyel a közterület jellege, az azelőtti szavak a pedig a közterület neve. (Szó: whitespace-ekkel vagy string elejével-végével határolt, whitespace-eket nem tartalmazó karaktersorozat.) Előfeltétel, hogy a házszám mindig megvan és mindig számmal kezdődik, előtte pedig a köztér jellege van feltüntetve. Kisbetű-nagybetű nem számít. Ezt még talán a regexp is megcsinálja.
De ha nem regexp, csinálnék ilyeneket, hogy az eddig megtalált közterület jellegeket, közterület neveket eltárolgatnám és ráellenőriznék a kiadott eredményre. Valami tanuló algoritmus féleség.
Auth Gábor
Ha lenne idő rá, akkor minden beérkező szöveges címből csinálnék valamilyen jellemzőket (egyes részek hossza, jellege, karakterek, írásjelek, stb), és egy másfél órát rászánni, hogy egy adatbázist feltöltsünk meg tanított adatokkal, és később se lesz ebből soha hibamentes rendszer.
Ki lehet próbálni, hogy kinek mit csinál a programja például a "Budapest, Október 23. utca 12/B" címmel... :)
És akkor még nem beszéltünk az elgépelt vagy rosszul írt címekkel, amelyek szemmel olvasva nem tűnnek fel, mert az agyunk automatikusan korrigál, de a program nem fog... :)
A beillesztett program pedig egy kezdetleges vázlat, amelyre már lehet építeni... legalábbis én így látom.
tvik
A várost nem kell beleírni. Város nélkül az alább írt algoritmusom pl. ezt is megeszi. :)
Mezei Zoltán
Azt hiszem, hogy nagyon távol áll egymástól kettőnk programozási stílusa. Én erősen törekszem arra, hogy ne legyen olyan információ, ami csak nálam van meg. Kell, hogy le legyen írva a dolog, esetleg, hogy más is tudjon róla. Így egyszerűbb...
Czimmermann Gábor
Ha a fejedre esik egy műhód (), akkor mit foglalkozol az utánad jövőkkel? Vagy a jövővel?
A dokumentálást szerintem a tervben kell leírni, nem a kódban. A kód csak a fontosabb dolgokat tarttalmazza, pl. in/out paraméterek, rövid leírás, stb.
Karnok Dávid
Unknown User (frimen)
lehet csinálni.. más kérdés, hogy érteni kell hozzá.. ami a részedről szerintem hiányzik.
Persze ezzel sokan így vannak, mert elég bonyolult..
A legnagyobb probléma ezzel az egésszel az, hogy rossz végén van megfogva a tehén..
A kulcs modatot meg Te mondtad ki: "de mivel össze vissza írják be".
Te meg folyamatosan szopni fogsz, ha ez igy is marad..:)
Bakos Gábor
A munkáltatóm rendelkezik ilyen jellegű eszközzel. (Tudom, mivel részt vettem a feljesztésében.) Ha gondolod vedd fel a kapcsolatot a Scriptummal.