IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Delphi Discussion :

base de données ou non?


Sujet :

Delphi

  1. #1
    Membre averti
    Inscrit en
    Avril 2007
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 13
    Par défaut base de données ou non?
    salut , mon problème et que que je ne si il faut que j'utilise une base de donner au autre chose pour que les chiffre de la premier colonne corresponde a ceux de la deuxième colonne .
    je m'explique ...
    les premier chiffre de chaque colonne corresponde au frappe du pave numérique(pour sa pas de problème)
    en revanche il faut que chaque chiffre de la première colonne une foi frappé au clavier marque sur Tedit le chiffre correspondant


    6=1 5=1 4=1 3=1
    7=2 6=2 5=2 4=2
    8=3 7=3 6=3 5=3
    9=4 8=4 7=4 6=4
    0=1 9=5 8=5 7=5
    1=2 0=2 9=6 8=6
    2=3 1=3 0=3 9=7
    3=4 2=4 1=4 0=4
    4=5 3=5 2=5 1=5
    5=6 4=6 3=6 2=6

  2. #2
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2003
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2003
    Messages : 560
    Par défaut
    Bonjour,

    Je ne comprend pas très bien votre question.

    Si j'ai bien compris, vous désirez établir des conversions de caractères.

    Vous désirez stocker les équivalence de conversions, c'est à dire je remplace la valeur x par la valeur x'.

    Si c'est bien cela que vous désirez, un TStringList peut très bien faire l'affaire.

    Si vous stocker "C1.1=2" "C1.2=3' etc. vous stocker directement la colonne (C1.), la valeur de frappe (1 ou 2 dans l'example) et la valeur de remplacement (=2 ou =3 dans l'exemple).

    vous pouvez accéder directement a chaque valeur de remplacement avec la fonction Values.
    Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    var
      List: TStringList
    begin
      List := TStringList.create;
      try
        List.Add('C1.1=2');
        List.Add('C1.2=3');
        ShowMessage(List.Values['C1.1']); // affiche la valeur de la colonne 1 (C1) entrée 1
      Finally
        List.free;
      end;
    Vous pouvez sauver et lire facilement votre List par un SaveToFile/LoadFromFile.
    Il existe de nombreux composant permettant de coder la liste (confidentialité).

    Par contre vous devez copier la liste en même temps que votre executable pour transférer l'application sur une autre machine.
    Ce n'est pas une solution standard.

    Il existe naturellement beaucoup d'autre solutions (fichier avec un format qui vous est propre, fichier ini, fichier registre, table ado sauvegarder sous forme de fichier text etc.).

    Si vous n'avez que cela à sauvegarder, je ne pense pas qu'une Base de donnée soit indispensable. Si par contre vous avez d'autres données à sauvegarder, je vous conseille d'envisager une solution global dés le départ. Cela évite bien des repentires.

  3. #3
    Membre averti
    Inscrit en
    Avril 2007
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 13
    Par défaut
    oui il sagi bien d'établir des conversions de caractères.
    pour TStringList je ne dispose pas de cette outil (delphie 2005 perso)
    il y a quatre colonne

    6=1 ... 5=1 ... 4=1 ... 3=1
    7=2 ... 6=2 ... 5=2 ... 4=2
    8=3 ... 7=3 ... 6=3 ... 5=3
    9=4 ... 8=4 ... 7=4 ... 6=4
    0=1 ... 9=5 ... 8=5 ... 7=5
    1=2 ... 0=2 ... 9=6 ... 8=6
    2=3 ... 1=3 ... 0=3 ... 9=7
    3=4 ... 2=4 ... 1=4 ... 0=4
    4=5 ... 3=5 ... 2=5 ... 1=5
    5=6 ... 4=6 ... 3=6 ... 2=6

    tout est la, il sagie d'un algo de décodage
    chaque colonne correspond a une case Tedit
    ex) dans la première colonne le 6 et le 0 devrons afficher le même numéro c'est a dire ici le 1. le 7et le 1 = 2 ect....

  4. #4
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2003
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2003
    Messages : 560
    Par défaut
    Dans ce cas mon message précédent était pertinent.

    Je créerai mes TEdit en leur associant le même évènement onchange, avec des test (onPresskey) pour empécher des saisie non numérique et une maxlength de 1.

    Dans le On Change j'écrirai quelque chose du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    var
      Cible: TEdit;
      Header: String;
    var
      // exemple sur 3 TEdit en entré / sorti (traduit)
      if Sender = edtInput1 the begin
        Cible := edtTranslate1;
        Header := 'C1.';
      end else  if Sender = edtInput2 the begin
        Cible := edtTranslate2;
        Header := 'C2.';
      end else  if Sender = edtInput3 the begin
        Cible := edtTranslate3;
        Header := 'C3.';
      end else
        exit;
     
       // recherche dans mas liste pré rempli comme dans le message précédent
       Cible.Text := List.Values[Header + IntTostr(TEdit(Sender).Text];
    Rapel si dans une un tstringList vous avez mis la valeur 'abcd=fdsfdsf', le fait de faire Values[abcd] vous donnera le résultat 'fdsfdsf'.

  5. #5
    Membre averti
    Inscrit en
    Avril 2007
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 13
    Par défaut
    ok merci pour tous mais le problème s'est que je n'ai a priori pas tstringList se qui est bien dommage
    donc j'ai fait autrement .
    le codes suivant correspond a la touche (1) du pave numérique .
    j'ai marquer &1 dans capiton et j'ai mis visble sur false
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    procedure TForm10.Button2Click(Sender: TObject);
    begin
    if (TF=0) then Edit1.Text := Edit1.Text+'2'
                          else begin
                         Edit1.Text:='2';
                         TF :=0;
                         end;
    if (TF=1) then Edit2.Text := Edit2.Text+'3'
                         else begin
                         Edit2.Text:='3';
                         Tf:= 1;
                         end;
    if (TF=2) then Edit3.Text := Edit3.Text+'4'
                         else begin
                         Edit3.Text:='4';
                         TF :=2;
                         end;
    if (TF=3) then Edit4.Text := Edit4.Text+'5'
                         else begin
                         Edit4.Text:='5';
                         TF :=3;
                         end;
    end;

    le problème s'est que je n'arrive pas a séparer les quatre Tedit bien que j'ai utiliser la variable TF quand je frappe 1 les quatre Tedit se replisse
    comment passer du Tedit1 au Tedit2 une foie le premier rempli ?

  6. #6
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2003
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2003
    Messages : 560
    Par défaut
    Cela me semble un peux usine a gaze. Je suppose que TF est le n° de sisie (1 caractère 2° caractère etc)

    Si je prend un exemple avec Tf à 1;

    if (TF=0) then Edit1.Text := Edit1.Text+'2' non donc
    Edit1.Text:='2';
    TF :=0;


    Test suivant if (TF=1) then Edit2.Text := Edit2.Text+'3' non (tf à été mis a 1 au test précédent) donc
    Edit2.Text:='3';
    Tf:= 1;

    etc..
    Vous remplissez donc tout vos champs.

    Le plus simple si vous avez plusieurs zone de saisie (1 par caractère) est de tester le sender (if sender = TEDIT1 then).

    si vous avez 1 seule zone de saisie vous pouvez realiser quelque chose de ce type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    TEditOnChange(Sender: TObject)
      procedure TraiteCaption(Traitement: Integer; InputCar:  String; OutputEdit: TEdit);
      var
        Output: String;
      begin
        Output := ' '; // valleur par defautr = espace vide
        if (inputCar >= '0') and (InputCar <=9) then begin
         case Traitememnt of
           1: // traitememnt cas 1, remplir Output
           2: // tritememnt cas 2 
           etc.. 
         end;
       end;
       OutputEdit.caption := Output;
      end;
    var
      Caption: String;
    begin
     
      Caption := MonchampsEdit.Caption + '----'; // caption contient toujour 4 caractères minima. aucun bug possible entre Caption[1] et Caption[4]
     
      TraiteCaption(1, Caption[1], EditSortie1);
      TraiteCaption(2, Caption[2], EditSortie1);
      TraiteCaption(3, Caption[3], EditSortie1);
      TraiteCaption(4, Caption[4], EditSortie1);
    End;
    Un telle système vous permet de traite au fur et a mesure de la saisie les 4 valeurs possibles simultanément, sans gérer des "flag" comme TF

  7. #7
    Membre Expert
    Avatar de Clorish
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    2 474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 2 474
    Par défaut
    Visiblement les données sont limité et de taille restrainte.

    Moi j'opterais pour un tableau (constant ou non selon le choix) d'entier :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    TCorespondance = Array [1..2] of Integer;
     
    TTable = Array[1..4] of Array[1..9] of TCorrespondance.
     
    Var Table : TTable = (
    ((6,1), (7,2),[...])
    ((5,1), (6,2),[...])
    [...]
    )
    apres on peux meme se baser sur les ID de la ligne pour eviter le type TCorrespondance :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Table[1, 1] = 2   //Col1, caractere tappé '1'
    Table[1, 2] = 3
    Table[1, 3] = 4

  8. #8
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2003
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2003
    Messages : 560
    Par défaut
    J'avais proposé un TStringList, car il à l'avantage de facilement se sauvegarder/Recharger. Au début j'avais pensé que cela était important. Autrement ou était l'avantage d'envisager l'utilisation d'une base de donnée ?

  9. #9
    Membre averti
    Inscrit en
    Avril 2007
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 13
    Par défaut
    merci pour tous ces consiel vraiment sympas
    j'avais penser a une bas de donner parce que s'est se que je connait étant donner que je suis débutant je fait avec mes connaissance qui sont tres limiter.

    je ne coche pas la case résolut dessuite car j'aurais d'autre question probablement et je n'ai pas fini de rentrer le codes que tu ma donner quelque petit problème mais sa devrait aller (mes neurone on chauffer j'ai fait une petite pause )

  10. #10
    Membre chevronné

    Homme Profil pro
    Chef de Projet ATIC
    Inscrit en
    Novembre 2005
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Chef de Projet ATIC
    Secteur : Finance

    Informations forums :
    Inscription : Novembre 2005
    Messages : 274
    Par défaut
    Juste pour signaler que TStringList est disponible dans delphi 2005 Perso, comme dans toutes les versions

  11. #11
    Membre Expert
    Avatar de Clorish
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    2 474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 2 474
    Par défaut
    Citation Envoyé par sirius123
    merci pour tous ces consiel vraiment sympas
    j'avais penser a une bas de donner parce que s'est se que je connait étant donner que je suis débutant je fait avec mes connaissance qui sont tres limiter.
    Si tu es vraiement u ndebutant ... je ne te conseille pas les BdD tout de suite sous DElphi .. c'est quand meme assez gros comme domaine
    A moins que tu ne soit pas aussi debutant que tu ne le penses

  12. #12
    Membre averti
    Inscrit en
    Avril 2007
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 13
    Par défaut
    oui s'est assez hard mais je travail avec des exemple que j'ai récupérer si est la que je modifie a ma convenance en tatonent
    est j'ai deux ouvrage sur delphie 2005 celui de Michel Martin et celui de Francois Pellerin qui luis parle de base de donner.
    pour l'outil TStringList j'ai beau regarder je ne le trouve pas le seule que j'ai trouver s'est TStringGrid

  13. #13
    Membre Expert
    Avatar de Clorish
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    2 474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 2 474
    Par défaut
    Citation Envoyé par sirius123
    oui s'est assez hard mais je travail avec des exemple que j'ai récupérer si est la que je modifie a ma convenance en tatonent
    Bon courage !

    Citation Envoyé par sirius123
    est j'ai deux ouvrage sur delphie 2005 celui de Michel Martin et celui de Francois Pellerin qui luis parle de base de donner.
    Mr P.toth a fait aussi un tres bon bouquin .... bon certe il s'est areter a la versions 7 de delphi le fainéant ....

    Citation Envoyé par sirius123
    pour l'outil TStringList j'ai beau regarder je ne le trouve pas le seule que j'ai trouver s'est TStringGrid
    C'ets pas un composant c'est une classe simple
    Suffit juste de declarer une variable et la manipuler

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Var Lst : TStringList;
    Lst := TStringList.Create;
    Lst.add('....');
    [...]
    Lst.Free;

  14. #14
    Membre averti
    Inscrit en
    Avril 2007
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 13
    Par défaut
    ok j'ai finalement laisser tomber mon usine a gaz qui fonctionnait a peut prêt mais était lourde et génératrice de bug.
    j'ai opter pour un tableau .
    problème insoluble pour moi j'ai une erreur ([Erreur] Unit11.pas(55): E2010 Types incompatibles : 'string' et 'Integer')
    le truc s'est qu'il ni a pas de type String
    j'ai déclarer le tableau comme suite
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      public
        { Déclarations publiques }
     
    var Tableau1:array[0..9] of integer;
    j'ai tenter plusieurs modif mais rien n'y fait ????

  15. #15
    Membre chevronné

    Homme Profil pro
    Chef de Projet ATIC
    Inscrit en
    Novembre 2005
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Chef de Projet ATIC
    Secteur : Finance

    Informations forums :
    Inscription : Novembre 2005
    Messages : 274
    Par défaut
    Il faudrait nous recopier la partie de code où il y a le problème.

    S'il t(indique type incompatible, c'est que tu essaies d'affecter un string à ton tableau de int

  16. #16
    Membre averti
    Inscrit en
    Avril 2007
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 13
    Par défaut
    merci Okaryn petit problème résolu j'arrive au bout j'ai bien appris sur les tableau .un dernier problèmes

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
     
    procedure TForm11.FormCreate(Sender: TObject);
    begin
     
    Ta[1,1] := 2;
    Ta[1,2] := 3;
    Ta[1,3] := 4;
    Ta[1,4] := 5;
    Ta[1,5] := 6;    {colonne n° 1 correspond edit 1}
    Ta[1,6] := 1;
    Ta[1,7] := 2;
    Ta[1,8] := 3;
    Ta[1,9] := 4;
    Ta[1,0] := 1;
     
    Ta[2,1] := 3;
    Ta[2,2] := 4;
    Ta[2,3] := 5;
    Ta[2,4] := 6;
    Ta[2,5] := 1;    {colonne n° 2 edite2}
    Ta[2,6] := 2;
    Ta[2,7] := 3;
    Ta[2,8] := 4;
    Ta[2,9] := 5;
    Ta[2,0] := 2;
     
    Ta[3,1] := 4;
    Ta[3,2] := 5;
    Ta[3,3] := 6;
    Ta[3,4] := 1;
    Ta[3,5] := 2;   {colonne n° 3 edit3}
    Ta[3,6] := 3;
    Ta[3,7] := 4;
    Ta[3,8] := 5;
    Ta[3,9] := 6;
    Ta[3,0] := 3;
     
    Ta[4,1] := 5;
    Ta[4,2] := 6;
    Ta[4,3] := 1;
    Ta[4,4] := 2;
    Ta[4,5] := 3;   {colonne n° 4 edite4}
    Ta[4,6] := 4;
    Ta[4,7] := 5;
    Ta[4,8] := 6;
    Ta[4,9] := 7;
    Ta[4,0] := 4;
    end;
     
     
    procedure TForm11.Button1Click(Sender: TObject);{bouton de stokage}
    begin
    Val(Edit1.Text,C1);
    Val(Edit2.Text,C2);
    Val(Edit3.Text,C3);  {stokage dans la variable C des quatre chiffre tapper dans edit}
    Val(Edit4.Text,C4);
    end;
     
    procedure TForm11.Button3Click(Sender: TObject);{bouton de resulta dans edit5}
    begin
        Val(Edit5.Text,C1,C2,C3,C4);
        C1:=Ta[1,{frappe 0 a 9'}];
        C2:=Ta[2,{frappe 0 a 9'}];     {théorique}
        C3:=Ta[3,{frappe 0 a 9'}];
        C4:=Ta[4,{frappe 0 a 9'}];
    end;
     
    end.
    la dernière partie du code représente se que je voudrait faire
    comment affecter le chiffre taper dans edit a indice (ligne)du tableau pour afficher la variable C1....
    est comment afficher C1,C2,C3,C4 dans edit5 a la suite dans l'ordre indiquer

  17. #17
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Par défaut
    pour l'outil TStringList j'ai beau regarder je ne le trouve pas
    C'est quand même bizarre car le TStringList existe depuis la première version de Delphi (bêta novembre 1994)! En plus il est super-souple d'utilisation.
    Je crains que tu as mal regardé, et pour le trouver :
    - tapes le mot TStringList dans une ligne de ton code,
    - clickes sur le milieu de ce mot,
    - et appuyes sur la touche F1 ce qui doit faire surgir la page l'Aide concernant TStringList.
    ... et c'est kif-kif pour retrouver autre chose.
    Quand t'auras goûté aux TStringList je suis sûr qu'il fera vite part des ingrédients de tes recettes de codes Delphi.
    N'oubliez pas de consulter les FAQ Delphi et les cours et tutoriels Delphi

  18. #18
    Membre chevronné Avatar de archonte
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    343
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 343
    Par défaut
    Citation Envoyé par sirius123
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      public
        { Déclarations publiques }
     
    var Tableau1:array[0..9] of integer;

    En regardant ton message suivant dans lequel tu montre un tableau à 2 dimensions, on se demande quel type de tableau tu utilises et surtout pourquoi faire ??

  19. #19
    Membre averti
    Inscrit en
    Avril 2007
    Messages
    13
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 13
    Par défaut
    Citation Envoyé par archonte
    En regardant ton message suivant dans lequel tu montre un tableau à 2 dimensions, on se demande quel type de tableau tu utilises et surtout pourquoi faire ??
    c'était un essai rapide pour voire la faisabilité de mon code est nullement le projet finale.
    et dans la citation que tu montre il ne sagie pas d'un tableau a 2 dimension mais 1 dimension.
    j'ai conscience que je pose des question évidente et que j'écris des codes qui on n'ont aucun sense mais je ne demande qua apprendre.
    et encore merci pour votre indulgence

  20. #20
    Membre chevronné Avatar de archonte
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    343
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 343
    Par défaut
    Je ne tiens pas à apporter une note virulente à la discussion (je ne suis qu'un amateur en Delphi !) : je voulais simplement préciser qu'une question énoncée clairement et distinctement trouve une réponse rapide et appropriée (parfois par celui-là même qui la pose !!) Courage

Discussions similaires

  1. Réponses: 2
    Dernier message: 21/10/2011, 11h03
  2. Tester base de données dorsale non accédée
    Par gerard101 dans le forum VBA Access
    Réponses: 3
    Dernier message: 24/06/2011, 15h16
  3. Affichage champ base de données si non vide
    Par lstephan dans le forum ASP
    Réponses: 0
    Dernier message: 01/12/2009, 08h25
  4. Problème de base de données ACCESS non reconnue avec ASP
    Par Therambok dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 05/01/2009, 17h38
  5. [MySQL] Upload images dans la base de données ou non?
    Par Veers59 dans le forum PHP & Base de données
    Réponses: 6
    Dernier message: 18/06/2007, 08h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo