{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7dee3692-750d-45fd-9f49-21dcc2b98887",
   "metadata": {
    "tags": []
   },
   "source": [
    "---\n",
    "title: Les entrées/sorties en Java\n",
    "description: Lecture et ecriture dans des fichiers, en mémoire, sur le réseau, ...\n",
    "categories: \n",
    "  - Java\n",
    "  - I111\n",
    "  - PO43\n",
    "  - Lecture\n",
    "provide_notes: true\n",
    "provide_slides: true\n",
    "jupyter: java\n",
    "---  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef7db69e-c021-4c8c-8877-3d6ea3c69a8a",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "84a93fc1-6b44-4d8e-bd62-7934444a6242",
   "metadata": {},
   "source": [
    "## Utilisation des Flots (streams)\n",
    "\n",
    "  * En Java, la communication entre le programme et les échanges de données\n",
    "    entre un programme et l’extérieur (autre programme, fichier ou application\n",
    "    réseau, …) sont réalisées à travers un “ flot ” de données\n",
    "  * Un flot permet de transporter séquentiellement des données. Les données sont transportées une par une (ou bloc) et dans l’ordre\n",
    "  * Le cycle d’utilisation d’un flot de données est le suivant :\n",
    "    1. Ouverture du flot\n",
    "    2. Tant qu’il y a des données à lire (ou à écrire), on lit (ou on écrit) la donnée suivante dans le flot\n",
    "    3. Fermeture du flot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f3a8879-70b5-4eb6-b397-fefa858a668b",
   "metadata": {},
   "source": [
    "## Sources ou destinations de flots\n",
    "\n",
    "  * Fichier\n",
    "  * Socket pour échanger des données sur un réseau\n",
    "  * Données de grandes tailles dans une base de données (blob) (images, par exemple)\n",
    "  * Pipe entre 2 processus\n",
    "  * Tableau d’octets (en mémoire)\n",
    "  * Chaîne de caractères\n",
    "  * URL (adresse Web)\n",
    "  * …"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69744286-4b1b-4053-b245-079d2cf7ec73",
   "metadata": {},
   "source": [
    "## Le paquetage java.io\n",
    "\n",
    "  *  La plupart des classes liées au E/S sont définies dans le paquetage java.io\n",
    "  *  Il prend en compte un grand nombre de flots :\n",
    "     -  On distingue deux grands types de flots :\n",
    "        -  Les octets\n",
    "        - Les caractères\n",
    "  * différentes sources et destinations\n",
    "  * Il est possible de décorer les flots : le modifier au fur et à mesure\n",
    "  * Le nombre de classes de ce paquetage est important, il faut regarder la javadoc."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18e1e795-fe1f-4ee6-b51f-6c77c610ab84",
   "metadata": {},
   "source": [
    "## Les grands types de flots\n",
    "\n",
    "  * Les flots d’octets servent à lire ou écrire des octets “ bruts ”, qui représentent des données codées, manipulées par un programme\n",
    "  * Les flots de caractères servent à lire ou écrire des données qui représentent des caractères lisibles par un homme (codés en Unicode)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfec52f8-e60e-4d09-bfef-dd0a9a02b89e",
   "metadata": {},
   "source": [
    "## Les types de classes\n",
    "  * Pour les deux grands type de flots (octets et caractères), on distingue :\n",
    "  * Les classes associées à une source ou une destination “ concrète ”\n",
    "    * FileReader pour lire un fichier\n",
    "  * Les classes qui permettent de “ décorer ” un autre flot\n",
    "    * BufferedReader qui ajoute un tampon (buffer) pour lire un flot de caractères"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14e55f19-ee48-4ab8-b65c-7e840410808b",
   "metadata": {},
   "source": [
    "## Les classes de base du paquetage java.io\n",
    "\n",
    "  * InputStream (Lecture d’octets)\n",
    "  * OutputStream (Ecriture d’octets)\n",
    "  * Reader (Lecture de caractères Unicode)\n",
    "  * Writer (Ecriture de caractères Unicode)\n",
    "  * File et Path (Manipulation de noms de fichiers et de répertoires)\n",
    "  * StreamTokenizer (Segmentation lexicale d’un flot d’entrée)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fddfc55d-5f8b-4b8a-9dda-93320a5a4810",
   "metadata": {},
   "source": [
    "## Les sources et destinations concrètes\n",
    "\n",
    "  * Fichiers :\n",
    "    * File{In|Out}putStream\n",
    "    * File{Reader|Writer}\n",
    "  * Tableaux\n",
    "    * ByteArray{In|Out}putStream\n",
    "    * CharArray{Reader|Writer}\n",
    "  * Chaînes de caractères\n",
    "    * String{Reader|Writer}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0eb6c71-296f-4c96-b939-655eeef5168f",
   "metadata": {},
   "source": [
    "## Les décorateurs\n",
    "\n",
    "  * Pour ajouter un tampon sur une entrée/sortie :\n",
    "    * Buffered{In|Out}putStream\n",
    "    * Buffered{Reader|Writer}\n",
    "  * Pour lire et écrire des types primitifs sous une forme binaire :\n",
    "    * Data{In|Out}putStream\n",
    "    * Pour compter les lignes lues :\n",
    "      * LineNumberReader\n",
    "  * Pour écrire sous forme de chaînes de caractères :\n",
    "    * PrintStream, PrintWriter\n",
    "  * Pour permettre de remettre un caractère lu dans le flot :\n",
    "    * PushbackInputStream, PushbackReader"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4024c6a-1c14-4f8f-adb2-577321af2aa3",
   "metadata": {},
   "source": [
    "## Ecrire des primitifs dans un fichier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "60c9650b-f566-406c-891e-0c6841fcd889",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (768950853.py, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  Cell \u001b[0;32mIn[1], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m    //| echo: true\u001b[0m\n\u001b[0m    ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "//| echo: true\n",
    "try (DataOutputStream dos=\n",
    "  new DataOutputStream(\n",
    "    new BufferedOutputStream(\n",
    "      new FileOutputStream(\"monFichier.bin\")))) {\n",
    " dos.writeDouble(27.7);\n",
    " dos.writeUTF(\"Pierre\");\n",
    " dos.writeInt(56); \n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc0d11e6-4aeb-4388-92b6-5abe99183dae",
   "metadata": {},
   "source": [
    "## Lire des primitifs dans un fichier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d87b132-38c4-4148-affc-7cffac652f8a",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "try (FileInputStream fis = new FileInputStream(\"monFichier.bin\");\n",
    "     BufferedInputStream bis = new BufferedInputStream(fis);\n",
    "     DataInputStream dis = new DataInputStream(bis)) {  \n",
    "    \n",
    "    double d = dis.readDouble(); \n",
    "    String s = dis.readUTF();\n",
    "    int i = dis.readInt();\n",
    "    System.out.println(\"%f %s %d\".formatted(d,s,i));   \n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6c069ed-0fd8-4e6a-80c1-24b7d156c4c1",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d23e01f-4dbc-43c9-bd60-e553b3583743",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "try (DataInputStream disBis = new DataInputStream(\n",
    "        new BufferedInputStream(new FileInputStream(\"monFichier.bin\")))) {\n",
    "        int i = disBis.readInt(); \n",
    "        System.out.println(\"%d\".formatted(i));\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fc7b3ee-b8a7-4545-838e-f7d5fb2bb6cb",
   "metadata": {},
   "source": [
    "## Les exceptions liées aux entrées-sorties\n",
    "\n",
    "  * Exceptions\n",
    "  * IOException (Exception durant une entrée-sortie)\n",
    "  * EOFException (Lecture d’une fin de fichier)\n",
    "  * FileNotFoundException (Fichier n’existe pas)\n",
    "  * ObjectStreamException (Problème lié à la sérialisation)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3f8a14d-0ca1-48d8-b369-7a9c1b32640f",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2191a5cb-f1b9-461c-9649-19dfad07bebd",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "try (DataInputStream dis = new DataInputStream(\n",
    "        new BufferedInputStream(new FileInputStream(\"monFichier.bin\")))) {\n",
    "    while (true) {\n",
    "        double d=dis.readDouble();\n",
    "    }\n",
    "} catch ( FileNotFoundException e ) { System.out.println(\"File not found.\"); }\n",
    "  catch ( EOFException e ) { System.out.println(\"End of file.\");}\n",
    "  catch ( IOException e ) { System.out.println(\"IO Exception\");}\n",
    "  finally { System.out.println(\"Done.\");} "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32b5885c-104f-4adc-a195-4c9214edfba8",
   "metadata": {},
   "source": [
    "## Reader (Classe abstraite de base)\n",
    "\n",
    "  * InputStreamReader (Lecture de caractères Unicode à partir d’un flot d’octets)\n",
    "  * FileReader (Lecture de caractères Unicode à partir des octets d’un fichier)\n",
    "  * FilterReader (Décorateur)\n",
    "  * BufferedReader (Entrées bufférisées)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "377882bd-92b4-4a73-9105-9e195ca8f781",
   "metadata": {},
   "source": [
    "## Séparateur de lignes\n",
    "\n",
    "  * La façon de séparer les lignes dépend du système d’exploitation\n",
    "  * Pour être portable utiliser\n",
    "    * println de PrintWriter (le plus simple)\n",
    "    * writeLine ou newLine de BufferedWriter\n",
    "    * ou la propriété système line.separator (System.getProperty(\"line.separator\"))\n",
    "  * Ne pas utiliser le caractère '\\ n' qui ne convient pas, par exemple, pour Windows"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12a8e046-a14f-4fd0-8e6c-a17bedcb4fbd",
   "metadata": {},
   "source": [
    "## Codage\n",
    "\n",
    "  * En Java les caractères sont codés en Unicode\n",
    "  * Des classes spéciales permettent de faire les traductions entre le codage Unicode et un autre codage\n",
    "  * Un codage par défaut est automatiquement installé par le JDK, conformément à la locale"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb25fc3e-b18f-4f61-9b06-74507b7806ba",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "try (BufferedReader br = new BufferedReader(new FileReader(\"monFichier.txt\"))) {\n",
    "  String ligne;\n",
    "  while ((ligne=br.readLine()) != null ) {\n",
    "      System.out.println(ligne);\n",
    "    }\n",
    "} catch ( FileNotFoundException e ) { System.out.println(\"file not found: %s\".formatted(e.getMessage()));}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3086e26d-b84a-43f9-b73f-3862b57d54e2",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "try ( PrintWriter pw = new PrintWriter(\n",
    "        new BufferedWriter ( new FileWriter(\"autreFichier.txt\")),true) ) {\n",
    "    String lignes[]={\"e:echo\",\"f:foxtrot\"};\n",
    "    Arrays.stream(lignes).forEach(pw::println);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "264345ef-abab-450f-b82b-1aee29f1b190",
   "metadata": {},
   "source": [
    "## La Classe ~~File~~ Path\n",
    "\n",
    "  * **Utiliser [Path](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/file/Path.html) à la place **\n",
    "  * Cette classe représente la notion de fichier, indépendamment du système d’exploitation\n",
    "  * Un fichier est repéré par un chemin abstrait composé d’un préfixe optionnel (nom d’un disque par exemple) et de noms (noms des répertoires parents et du fichier lui-même)\n",
    "  * Attention, File fichier = new File(\"/bidule/truc\"); ne lève aucune exception si /bidule/truc n’existe pas dans le système de fichier\n",
    "  * Constructeurs\n",
    "    * Les chemins passés en premier paramètre peuvent être des noms relatifs ou absolus\n",
    "      * File (String chemin)\n",
    "      * File (String cheminParent, String chemin)\n",
    "      * File (File parent, String chemin)\n",
    "  * La classe File offre des facilités pour la portabilité des noms de fichiers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea29c749-9016-4b71-9844-df29936017e2",
   "metadata": {},
   "source": [
    "## Les fonctionnalités de la classe File\n",
    "\n",
    "  * Elle permet d’effectuer des manipulations sur les fichiers et répertoires considérés comme un tout (mais pas de lire ou d’écrire le contenu) :\n",
    "    * lister un répertoire,\n",
    "    * supprimer, renommer un fichier\n",
    "    * créer un répertoire\n",
    "    * créer un fichier temporaire connaître les droits que l’on a sur un fichier (lecture, écriture)\n",
    "    * etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d7809a5-a0b0-49f5-b605-b971e691f4f4",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "Path path = Paths.get(\"autreFichier.txt\");\n",
    "BufferedReader reader = Files.newBufferedReader(path);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6eee5e1c-78f4-4e5e-a30f-78ebda99830e",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "Files.lines(Paths.get(\"autreFichier.txt\"))\n",
    "    .map(line ->line.split(\":\"))\n",
    "    .map(t -> \"La lettre %s se dit %s.\".formatted(t[0],t[1]))\n",
    "    .forEach(System.out::println);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69d35c99-59f5-4c22-9513-0ba03b954e8c",
   "metadata": {},
   "source": [
    "StringTokenizer et StreamTokenizer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62852406-4458-40bc-a50e-0f965da708c5",
   "metadata": {},
   "source": [
    "paquetage java.nio\n",
    "Reprend toute l’architecture des classes pour les entrées/sorties\n",
    "notion de canal (channel) et de buffer\n",
    "Utilise les possibilités avancées du système d’exploitation hôte pour\n",
    "optimiser les entrées-sorties et offrir plus de fonctionnalités"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b79d5c3c-352a-49fc-b034-c644929fefa3",
   "metadata": {},
   "source": [
    "## Classe URL\n",
    "\n",
    "  * protocole://machine[:port]/cheminPage\n",
    "  * Cette classe du paquetage java.net fournit de nombreux constructeurs\n",
    "  * Elle permet d’extraire des éléments à partir d’un URL (getPort, getHost,…)\n",
    "  * Les données associées à l’URL peuvent être obtenues par les méthodes\n",
    "    * openConnection et openStream ou par la méthode getContent"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c91f84fa-3de2-4d3c-be2c-056e0091c50f",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "435e31ec-c068-4fc2-9bfe-3a6d8c255b6d",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "%%loadFromPOM\n",
    "<dependency>\n",
    "    <groupId>org.jsoup</groupId>\n",
    "    <artifactId>jsoup</artifactId>\n",
    "    <version>1.16.1</version>\n",
    "</dependency>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1290c790-5ed6-4c4c-8645-1391f28decb6",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "URL url = new URL(\"https://www.univ-tln.fr/\");\n",
    "BufferedReader br=new BufferedReader ( new InputStreamReader(url.openStream()));\n",
    "br.lines().filter(l->l.contains(\"<title>\")).forEach(System.out::println);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59dbbaac-fe59-4fcd-8df1-62825e91298f",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1329f44e-95bf-49ba-9bd9-9dc39c16d5ec",
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "//| echo: true\n",
    "import org.jsoup.Jsoup;\n",
    "import org.jsoup.nodes.Document;\n",
    "import org.jsoup.nodes.Element;\n",
    "import org.jsoup.select.Elements;\n",
    "\n",
    "Document doc = Jsoup.connect(\"https://en.wikipedia.org/\").get();\n",
    "System.out.println(doc.title().toString().toUpperCase());\n",
    "Elements newsHeadlines = doc.select(\"#mp-itn b a\");\n",
    "for (Element headline : newsHeadlines) {\n",
    "  System.out.println(\"%s\\t\\t\\t\\t%s\".formatted(\n",
    "    headline.attr(\"title\"), headline.absUrl(\"href\")));\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "java [env:base] *",
   "language": "java",
   "name": "conda-base-java"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "Java",
   "pygments_lexer": "java",
   "version": "21.0.4+7-LTS"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
