frankx: VBA und CSV - aber wie?

Beitrag lesen

Hellihello Vinzenz,

Zweitens: Ja, Du hast wenig Einfluß auf die Speicherung und gerade die Abhängigkeit von den Ländereinstellungen kann gewaltig nerven.

Das ist mir einmal in einem Access-Projekt passiert, dort hatte ich Formulare dynamisch generiert und angepasst. Ich musste alles auf Twips umstellen, um nie, nie, nie in Zeichenketten mit Kommazahlen arbeiten zu müssen. War lehrreich.

Interessant ist, dass Excel durchaus korrekte CSV-Dateien erzeugt, diese allerdings selbst nicht richtig einlesen kann (getestet mit Excel 2007)

Genau das fiel mir auch auf. Immerhin unterscheidet sich die SaveAs Methode abhängig davon, ob sie aus dem Marko/VBA oder übers Menü aufgerufen wird (einmal mit Kommas, kann Excel nicht lesen auf anhieb, einmal mit Semikolon).

Ach ja, noch was: CSV-Dateien solltest Du in Excel _nicht_ über "Datei öffnen" oder das Kontextmenü der Datei öffnen. Nein, Du solltest in einer Arbeitsmappe über Daten -> Importieren gehen. Dann kannst Du einige Parameter festlegen.

Nun ja, Excel hat die Daten ja, ist ja die Datenbasis.

Mit Zeilenumbrüchen in Inhalten kommt Excel jedoch nicht klar.

Kann ich nicht bestätigen. Meins kann das hier lesen:

  
title1;titel2;titel3  
"""A2""";";B2;";"C2¶&chr(11)&aaa       Zelle_as_CSV = check_convert_and_wrap(Zelleninhalt)  
            Debug.Print (Spaltennummer & ""-"")  
            Debug.Print Zelle_as_CSV"  
A3;B3;C3  

Mein CSV-ersteller (erstmal ohne Abspeichern und Aufruf einer Batchdatei zum Hochladen auf einen Server) sieht momentan so aus:

  
'Variablendeklaration verplichtend durch  
Option Explicit  
' Indiziere Arrays von 1 an, matched zum Zähler da unten ab 1  
Option Base 1  
  
'Globale Vars deklarieren (s.a. Funktion Wrapper)  
'die Zuweisung durch chr() geht scheints nur im Sub  
'eigentlich wäre das eine Const, aber das klappt durch die chr()-Zuweisung nicht  
Dim Semikolon, CRLF, Anführungszeichen As String  
  
Sub my_csv()  
    CRLF = Chr(13) & Chr(10)  
    Anführungszeichen = Chr(34)  
    Semikolon = Chr(59)  
  
    'Declaration  
    Dim UrsprungsZeile, UrsprungsSpalte  As Object  
    Dim Zeilenanzahl, Spaltenanzahl, Zeilennummer, Spaltennummer As Integer  
  
    'Arrays  
    Dim Zeilen() As String  
    Dim Spalten() As String  
  
    Dim Zelleninhalt, Zelle_as_CSV, Zeile_as_CSV, Tabelle_as_CSV As String  
  
    'Rows- and Columns.Count for Use in Redimension of Arrays below  
    Zeilenanzahl = ActiveSheet.UsedRange.Rows.Count  
    Spaltenanzahl = ActiveSheet.UsedRange.Columns.Count  
  
    'Redimension des Arrays  
    ReDim Zeilen(Zeilenanzahl) As String  
    ReDim Spalten(Spaltenanzahl) As String  
  
    'initialize Ausgabestring  
    Tabelle_as_CSV = ""  
  
    'so, nun geh mal durch die Zeilen  
    For Zeilennummer = 1 To Zeilenanzahl Step 1  
        'Zeile als String initialiiseren  
        Zeile_as_CSV = ""  
        'so, nun geh mal durch die Spalten in der Zeile  
        For Spaltennummer = 1 To Spaltenanzahl Step 1  
            'hohl dir den Inhalt der Zelle, als *.value, weil mind. eine Zelle mehr als 1024 Zeichen haben kann  
            Zelleninhalt = ActiveSheet.UsedRange.Rows(Zeilennummer).Cells(Spaltennummer).Value  
            'lass den Wrapper ran  
            Zelle_as_CSV = check_convert_and_wrap(Zelleninhalt)  
            'stopf es ins Spaltenarray  
            Spalten(Spaltennummer) = Zelle_as_CSV  
        Next  
        'Spaltenarray in String mit Semikolon als Seperator zerlegen und ins Zeilenarray stopfen  
        Zeilen(Zeilennummer) = Join(Spalten, Semikolon)  
        'sollte hier das Spaltenarray geleert werden und wenn ja wie?  
    Next  
    'Zeilenarray in String mit CRLF als Seperator zerlegen - thats it  
    Tabelle_as_CSV = Join(Zeilen, CRLF)  
    Debug.Print Tabelle_as_CSV  
End Sub  
  
  
Function check_convert_and_wrap(Zelleninhalt)  
    'Soll ich nun wrappen?  
    Dim wrap As Boolean  
    wrap = False  
  
   'checked, ob Anführungszeichen im Zelleninhalt  
    If InStr(Zelleninhalt, Anführungszeichen) > 0 Then  
        'dann bitte 1. wrappen  
        wrap = True  
        '2. die Anführungszeichen im Zelleninhalt jeweils verdoppeln  
        Zelleninhalt = Replace(Zelleninhalt, Anführungszeichen, Anführungszeichen & Anführungszeichen)  
  
    'wenn keine Anführungszeichen, dann schauen, ob Semikolon oder Absatzmarke (CR&LF)  
    ElseIf (InStr(Zelleninhalt, Semikolon) > 0 Or InStr(Zelleninhalt, CRLF) > 0) Then  
        'wenn ja, dann wrappen  
        wrap = True  
        Debug.Print (Zelleninhalt & "aaaaaa")  
    End If  
    'wenn wrappen  
    If wrap Then  
        'dann setz die Rückgabe in Anführungszeichen  
        check_convert_and_wrap = Anführungszeichen & Zelleninhalt & Anführungszeichen  
    Else  
        'ansonsten bleibt alles, wies ist und wird dennoch zurückgegeben  
        check_convert_and_wrap = Zelleninhalt  
    End If  
End Function  

Ich hab das ganze ja jetzt so angepackt deinem Rat folgend, ein Standardformat lieber für Character-Delimited-Values zu nehmen, wobei ich nach wie vor mit den o.g. Problemen und der Frage YAGNI überlege, ob String-Delimited-Values nicht doch auch eine (proprietäre) Alternative wären.

Zudem möchte ich eigentlich im Folgeschritt vergleichen, welche Zeilen sich geändert haben. Mit PHP würde ich also jedes Zeilenarray eigentlich wieder Zerlegen mittels implode() und den String dann vergleichen mit der bisherigen Zeile. Sind sie gleich, ist kein update nötig. Das würde bei String-Demlimited-Values ein implode() sparen (;-).

Abgesehen würde ich das ganze gern portieren auf OpenOffice. StarBasic aber scheint etwas anders zu sein. So finde ich kein Debug.Print sondern nur Print und das öffnet immer eine kleine OK-Box. Auch fehlt mir der Bezeichner für "ActiveSheet" und "UsedRange" zB. Schön finde ich aber, dass hier auch Javascript oder Python nutzbar wäre. Hast Du damit Erfahrung? Vielleicht mach ich mal einen neuen Thread dazu, hier in den verschachtelten Tiefen scheints mir, sind wir beide grad allein.

Zu guter letzt sinniere ich auch über das Konzept, das auf den Server hochzuladen. Mit einem "ftp -s:ftp-script" hab ich schon einen Ansatz. Aber dann weiß der Server ja immer noch nicht, dass er die neue Datenbasis vergleichen und einarbeiten solle.

Rufe ich ein *.htm-Dokument auf, dann muss der User selbst die Datei zum Hochladen bestimmen, was lästig wäre. Nähme ich ein *.hta, könnte ich die Lösung nicht 1:1 auf Linux übertragen. Auch in neuem Thread vielleicht.

DankDank und GrußGruß,

frankx