Merkwürdiges Zeichen in Datenbank-Feldern

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Kurt.Wallander
User
Beiträge: 28
Registriert: Donnerstag 24. September 2015, 22:46

Guten Abend,

ich muss schon sagen, dass mich das versammelte Wissen in diesem Forum nachhaltig beeindruckt. Für mich, der ich im Beruf mit völlig anderen Dingen zu tun habe, hobbymäßig ein bisschen code und Python erst vor gut drei Monaten für sich entdeckt habe, ist das ein steter Quell der Inspiration - wenngleich ich mir nicht selten ziemlich unwissend vorkomme. Deshalb nochmal ein fettes Dankeschön!

Gruß aus dem Zug auf dem Weg nach Haus
Kurt
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Sirius3:
Ja ich kenne die caret notation, allerdings nur von C0 control codes her. Wusste nicht, dass die auch 8-bittig verwendet wird.
Kurt.Wallander
User
Beiträge: 28
Registriert: Donnerstag 24. September 2015, 22:46

Aaalso ...

Quelltext im Browser Firefox:

Code: Alles auswählen

	<e2event>
		<e2eventid>28465</e2eventid>
		<e2eventstart>1449097500</e2eventstart>
		<e2eventduration>17700</e2eventduration>
		<e2eventcurrenttime>1449086629</e2eventcurrenttime>
		<e2eventtitle>ARD - PopNacht</e2eventtitle>
		<e2eventdescription></e2eventdescription>
		<e2eventdescriptionextended>Vom SWRŠ1.00, 2.00, 3.00, 4.00 Uhr Nachrichten und Wetter</e2eventdescriptionextended>
		<e2eventservicereference>1:0:1:6f03:044d:a401:ffff0000:0:0:0:</e2eventservicereference>
		<e2eventservicename><n/a></e2eventservicename>
	</e2event>
Wobei ... eigentlich wird im Browser zwischen "SWR1" und "1.00" ein Kästchen angezeigt, also wohl ein Zeichen, mit dem der Browser nichts anfangen kann. Wenn ich diesen Quelltext in die Zwischenablage kopiere und hier rein paste, verschwindet dieses Zeichen. Ersatzlos.

Wenn ich die Datei mit wget auf der Konsole oder mittels ...

Code: Alles auswählen

urllib.urlretrieve(url, local_file)
hole und anschließend mit nano öffne, sieht das so aus:

Code: Alles auswählen

<e2eventdescriptionextended>Vom SWR^Ê1.00, 2.00, 3.00, 4.00 Uhr Nachrichten
Inspiziere ich dieselbe Datei mit vi, sieht das so aus:

Code: Alles auswählen

Vom SWR<8a>1.00, 2.00, 3.00, 4.00 Uhr Nachrichten
Und mit vi im Hex-Modus:

Code: Alles auswählen

00006c0: 7363 7269 7074 696f 6e3e 0a09 093c 6532  scription>...<e2
00006d0: 6576 656e 7464 6573 6372 6970 7469 6f6e  eventdescription
00006e0: 6578 7465 6e64 6564 3e56 6f6d 2053 5752  extended>Vom SWR
00006f0: c28a 312e 3030 2c20 322e 3030 2c20 332e  ..1.00, 2.00, 3.
0000700: 3030 2c20 342e 3030 2055 6872 204e 6163  00, 4.00 Uhr Nac
0000710: 6872 6963 6874 656e 2075 6e64 2057 6574  hrichten und Wet
0000720: 7465 723c 2f65 3265 7665 6e74 6465 7363  ter</e2eventdesc
0000730: 7269 7074 696f 6e65 7874 656e 6465 643e  riptionextended>
Ich weiß nicht, ob das bei der Analyse hilft. Aber Sirius' These scheint mir plausibel: Da ist ein Zeichen, von dem der Browser nicht weiß, wie er es darstellen soll - also malt er da irgendwas Kryptisches hin oder unterschlägt das Zeichen komplett.
Kurt.Wallander
User
Beiträge: 28
Registriert: Donnerstag 24. September 2015, 22:46

Crazy, und bei der Ausgabe mit PHP ...

Code: Alles auswählen

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>RadioBeere - EPG</title>
    <meta name="viewport"
          content="width=device-width, initial-scale=1" />
    <meta charset="utf-8" />
    <?php
    include("include/styling.php");
    ?>
</head>

<body>
    <div data-role="page"
         class="ui-responsive-panel"
         id="panel"
         data-title="RadioBeere">
        <div data-role="header">
            <a href="#nav-panel"
                 data-icon="bars"
                 data-iconpos="notext">Menü</a>
            <h1>RadioBeere</h1>
            <a href="/"
                 data-icon="home"
                 data-iconpos="notext">Startseite</a>
        </div>

        <div data-role="main"
             class="ui-content">
            <h2>Aufnahmen</h2>

            <?php
            include("include/db-connect.php")
            ?>

                <?php

                $abfrage = "SELECT * FROM epg WHERE alias = 'hr1' ORDER BY time";

                $ergebnis = mysql_query($abfrage);

                while($row = mysql_fetch_object($ergebnis))
                    {
                    echo "<b>$row->id</b><br>";
                    echo "<b>$row->alias</b><br>";
                    echo "<b>$row->title</b><br>";
                    echo "<b>$row->teaser</b><br>";                    
                    echo "<b>$row->summary</b><br>";
                    echo "<b>$row->time</b><br>";
                    echo "<b>$row->duration</b><br>";
                    echo "<br><br>";
                    }

                    ?>

            <div class="illu-content-wrapper">
                <div class="illu-content illu-player">
                </div>
            </div>
        </div>
        <?php
        include("include/navigation.php");
        ?>
    </div>
    <?php
    include("include/jquery.php");
    ?>
</body>
</html>
Kommt nur noch Schmu an, da werden dann auch die Umlaute verkrüppelt:

Code: Alles auswählen

1
hr1
hr1 - LOUNGE

Die Geschichten hinter den Hits und K�nstlern. Ganz entspannt gibt es hier Interessantes aus f�nf Jahrzehnten Pop-Geschichte; dazu wichtige Neuerscheinungen, die "hr1-CD der Woche" und die passende Musikauswahl zum sp�ten Abend.�19:30 Nachrichten, Service und Wetter�20:00 Nachrichten, Service und Wetter�21:00 Nachrichten, Service und Wetter�22:00 Nachrichten, Service und Wetter�23:00 Nachrichten, Service und Wetter
1449079500
17700


2
hr1
Nachrichten, Service und Wetter


1449097200
300


3
hr1
ARD - PopNacht

Vom SWR�1.00, 2.00, 3.00, 4.00 Uhr Nachrichten und Wetter
1449097500
17700
Ich glaube, ich bin zu doof.
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@Kurt.Wallander: hast Du das Encoding der Verbindung auch richtig eingestellt? Z.B. per

Code: Alles auswählen

mysql_set_charset('utf8');
Kurt.Wallander
User
Beiträge: 28
Registriert: Donnerstag 24. September 2015, 22:46

Sirius3 hat geschrieben:@Kurt.Wallander: hast Du das Encoding der Verbindung auch richtig eingestellt? Z.B. per

Code: Alles auswählen

mysql_set_charset('utf8');
Aha! Wieder ein Schritt weiter:

Code: Alles auswählen

1
hr1
hr1 - LOUNGE

Die Geschichten hinter den Hits und Künstlern. Ganz entspannt gibt es hier Interessantes aus fünf Jahrzehnten Pop-Geschichte; dazu wichtige Neuerscheinungen, die "hr1-CD der Woche" und die passende Musikauswahl zum späten Abend.Š19:30 Nachrichten, Service und WetterŠ20:00 Nachrichten, Service und WetterŠ21:00 Nachrichten, Service und WetterŠ22:00 Nachrichten, Service und WetterŠ23:00 Nachrichten, Service und Wetter
1449079500
17700


2
hr1
Nachrichten, Service und Wetter


1449097200
300


3
hr1
ARD - PopNacht

Vom SWRŠ1.00, 2.00, 3.00, 4.00 Uhr Nachrichten und Wetter
1449097500
17700
Es bleibt aber bei den kryptischen Zeichen für den Zeilenumbruch.
Kurt.Wallander
User
Beiträge: 28
Registriert: Donnerstag 24. September 2015, 22:46

Okay, ich hätte eine dreckige Lösung: Nachdem ich die Daten in die Datenbank geschrieben habe, wird einfach dieses ominöse Zeichen ersetzt durch ein \n. Ich krieg's anders nicht hin. Jetzt weiß ich nur nicht, warum folgende Anweisung funktioniert, wenn ich sie in phpmyadmin als SQL-Befehl ausführe ...

Code: Alles auswählen

UPDATE epg SET summary=(REPLACE(summary, "Š", "\n"))
... dies aber in meinem Script NICHT funktioniert:

Code: Alles auswählen

cursor.execute('UPDATE epg SET summary=(REPLACE(summary, "Š", "\\n"))')
connection.commit()
cursor.execute('UPDATE epg SET teaser=(REPLACE(teaser, "Š", "\\n"))')
connection.commit()
Edit: Vermutlich, weil das Zeichen Š als Suchstrang nicht gefunden wird. Testweise habe ich andere Springs gesucht, die werden anstandslos ersetzt. Hmmm ...
BlackJack

@Kurt.Wallander: Für den laufenden Betrieb wäre es wohl sinnvoller das Tabulator-setzen-Zeichen zu ersetzen *bevor* es in die Datenbank geschrieben wird.
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@Kurt.Wallander: Du willst also Line Tabulatorset durch ein Line Feed ersetzen. Dann tu das auch und versuch nicht S mit Caron durch Line Feed zu ersetzen. Und das ganze in der Datenbank per UPDATE zu machen, ist der total falsche Weg. Das führt nur dazu, dass Du kaputte Daten in Deiner Datenbank hast, die die nachträglich reparierst und man will NIE kaputte Daten in einer Datenbank haben!

Folglich direkt in Python mit replace ersetzen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf8 -*-
 
import MySQLdb
from contextlib import closing
from collections import namedtuple
import urllib
try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET
 
import login

EVENT = 'e2event'
TIME = 'e2eventstart'
DURATION = 'e2eventduration'
TITLE = 'e2eventtitle'
TEASER = 'e2eventdescription'
SUMMARY = 'e2eventdescriptionextended'
        
Record = namedtuple("Record", "alias, time, duration, title, teaser, summary")

def retrieve_epg(url):
    with closing(urllib.urlopen(url)) as f:
        return f.read()
 
def parse_epg(alias, epg):
    root = ET.fromstring(epg)
    return [Record(
        alias,
        int(element.findtext(TIME)),
        int(element.findtext(DURATION)),
        element.findtext(TITLE),
        element.findtext(TEASER),
        element.findtext(SUMMARY).replace(u'\xBA', u'\n'),
    ) for element in root.findall('.//'+EVENT)]
 
 
def add_to_db(connection, records):
    cursor = connection.cursor()
    cursor.executemany('INSERT INTO epg '
        '(alias, time, duration, title, teaser, summary) '
        'VALUES (%s,%s,%s,%s,%s,%s)', records)

def main():
    with closing(MySQLdb.connect(
            login.DB_HOST, login.DB_USER,
            login.DB_PASSWORD, login.DB_DATABASE)) as connection:
        cursor = connection.cursor()
        cursor.execute('TRUNCATE TABLE epg')
        connection.commit()
        
        cursor.execute('SELECT alias, url FROM lamedb')
        for alias, url in cursor:
            add_to_db(connection, parse_epg(alias, retrieve_epg(url)))
            connection.commit()
 
if __name__ == '__main__':
    main()
Kurt.Wallander
User
Beiträge: 28
Registriert: Donnerstag 24. September 2015, 22:46

Ha! Der Hinweis, dass es doch eher sinnvoll ist, ein Zeichen VOR dem Schreiben in die Datenbank zu ersetzen, war der entscheidende.

Zwei Dinge musste ich noch ändern, damit das Script fehlerfrei durchläuft:

1)
replace(u'\x8a', u'\n')
statt
replace(u'\xBA', u'\n')

2)
Sofern summary oder teaser leer ist, was passieren kann, kriege ich einen Fehler mein Einfügen in die Datenbank. Logisch. Man kann nichts einfügen, wo nichts ist. Also muss ich diese beiden Fälle abfangen.

Dann noch im PHP-Script dies

Code: Alles auswählen

echo nl2br($row->summary);
Und fertig!

Wo ist hier der "Gelöst"-Button? Jetzt hoffe ich nur noch, dass \n ordentlich umgesetzt wird, wenn ich summary als ID3-Tag in eine MP3-Datei schreibe. Mal sehen.

Vielen, vielen Dank für eure Geduld. Dein Code ist enorm kompakt und elegant, @Sirius. namedtuple kannte ich noch nicht. In meiner Spaghetti-Schreibweise sieht die Lösung jetzt so aus:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf8 -*-

import MySQLdb
from contextlib import closing
import urllib
try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

import login


TIME = 'e2eventstart'
DURATION = 'e2eventduration'
TITLE = 'e2eventtitle'
TEASER = 'e2eventdescription'
SUMMARY = 'e2eventdescriptionextended'


def retrieve_epg(connection, cursor, db_result):

    epg = []

    for db_record in db_result:

        alias = db_record[0]
        url = db_record[1]
        f = urllib.urlopen(url)
        epg.append(f.read())

    return epg


def parse_epg(epg):

    time = []
    duration = []
    title = []
    teaser = []
    summary = []

    root = ET.fromstring(epg)

    for element in root.iter(TIME):
        time.append(element.text)
    for element in root.iter(DURATION):
        duration.append(element.text)
    for element in root.iter(TITLE):
        title.append(element.text)
    for element in root.iter(TEASER):
        teaser.append(element.text)
    for element in root.iter(SUMMARY):
        summary.append(element.text)

    return time, duration, title, teaser, summary


def add_to_db(connection, cursor,
              alias, time, duration, title, teaser, summary):

    i = 0

    while i < len(time):

        if not teaser[i]:
            teaser[i] = ''
        if not summary[i]:
            summary[i] = ''
        summary[i] = summary[i].replace(u'\x8a', u'\n')

        cursor.execute('INSERT INTO epg \
        (alias, title, teaser, summary, time, duration) \
        VALUES (%s,%s,%s,%s,%s,%s)',
                       (alias, title[i], teaser[i],
                        summary[i], time[i], duration[i]))

        i += 1

    connection.commit()


def main():

    with closing(MySQLdb.connect(
            login.DB_HOST, login.DB_USER,
            login.DB_PASSWORD, login.DB_DATABASE)) as connection:
        with closing(connection.cursor()) as cursor:

            cursor.execute('TRUNCATE TABLE epg')
            connection.commit()
            cursor.execute('SELECT alias, url FROM lamedb')
            db_result = cursor.fetchall()

            epg = retrieve_epg(connection, cursor, db_result)

            i = 0

            for db_record in db_result:

                alias = db_record[0]
                time, duration, title, teaser, summary = parse_epg(epg[i])
                add_to_db(connection, cursor,
                          alias, time, duration, title, teaser, summary)

                i += 1


if __name__ == '__main__':
    main()
Antworten