/****************************************************************************** * Compilation: javac BinaryIn.java * Execution: java BinaryIn input output * Dependencies: none * * This library is for reading binary data from an input stream. * * % java BinaryIn https://introcs.cs.princeton.edu/java/cover.png output.png * ******************************************************************************/ package edu.princeton.cs.algs4; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.net.URL; import java.net.URLConnection; import java.util.NoSuchElementException; /** * The BinaryIn data type provides methods for reading * in bits from a binary input stream. It can process the bits * one bit at a time (as a {@code boolean}), * 8 bits at a time (as a {@code byte} or {@code char}), * 16 bits at a time (as a {@code short}), * 32 bits at a time (as an {@code int} or {@code float}), or * 64 bits at a time (as a {@code double} or {@code long}). *

* The binary input stream can be from standard input, a filename, * a URL name, a Socket, or an InputStream. *

* All primitive types are assumed to be represented using their * standard Java representations, in big-endian (most significant * byte first) order. *

* The client should not intermix calls to {@code BinaryIn} with calls * to {@code In}; otherwise unexpected behavior will result. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class BinaryIn { private static final int EOF = -1; // end of file private BufferedInputStream in; // the input stream private int buffer; // one character buffer private int n; // number of bits left in buffer /** * Initializes a binary input stream from standard input. */ public BinaryIn() { in = new BufferedInputStream(System.in); fillBuffer(); } /** * Initializes a binary input stream from an {@code InputStream}. * * @param is the {@code InputStream} object */ public BinaryIn(InputStream is) { in = new BufferedInputStream(is); fillBuffer(); } /** * Initializes a binary input stream from a socket. * * @param socket the socket */ public BinaryIn(Socket socket) { try { InputStream is = socket.getInputStream(); in = new BufferedInputStream(is); fillBuffer(); } catch (IOException ioe) { System.err.println("Could not open " + socket); } } /** * Initializes a binary input stream from a URL. * * @param url the URL */ public BinaryIn(URL url) { try { URLConnection site = url.openConnection(); InputStream is = site.getInputStream(); in = new BufferedInputStream(is); fillBuffer(); } catch (IOException ioe) { System.err.println("Could not open " + url); } } /** * Initializes a binary input stream from a filename or URL name. * * @param name the name of the file or URL */ public BinaryIn(String name) { try { // first try to read file from local file system File file = new File(name); if (file.exists()) { FileInputStream fis = new FileInputStream(file); in = new BufferedInputStream(fis); fillBuffer(); return; } // next try for files included in jar URL url = getClass().getResource(name); // or URL from web if (url == null) { url = new URL(name); } URLConnection site = url.openConnection(); InputStream is = site.getInputStream(); in = new BufferedInputStream(is); fillBuffer(); } catch (IOException ioe) { System.err.println("Could not open " + name); } } private void fillBuffer() { try { buffer = in.read(); n = 8; } catch (IOException e) { System.err.println("EOF"); buffer = EOF; n = -1; } } /** * Returns true if this binary input stream exists. * * @return {@code true} if this binary input stream exists; * {@code false} otherwise */ public boolean exists() { return in != null; } /** * Returns true if this binary input stream is empty. * * @return {@code true} if this binary input stream is empty; * {@code false} otherwise */ public boolean isEmpty() { return buffer == EOF; } /** * Reads the next bit of data from this binary input stream and return as a boolean. * * @return the next bit of data from this binary input stream as a {@code boolean} * @throws NoSuchElementException if this binary input stream is empty */ public boolean readBoolean() { if (isEmpty()) throw new NoSuchElementException("Reading from empty input stream"); n--; boolean bit = ((buffer >> n) & 1) == 1; if (n == 0) fillBuffer(); return bit; } /** * Reads the next 8 bits from this binary input stream and return as an 8-bit char. * * @return the next 8 bits of data from this binary input stream as a {@code char} * @throws NoSuchElementException if there are fewer than 8 bits available */ public char readChar() { if (isEmpty()) throw new NoSuchElementException("Reading from empty input stream"); // special case when aligned byte if (n == 8) { int x = buffer; fillBuffer(); return (char) (x & 0xff); } // combine last N bits of current buffer with first 8-N bits of new buffer int x = buffer; x <<= (8 - n); int oldN = n; fillBuffer(); if (isEmpty()) throw new NoSuchElementException("Reading from empty input stream"); n = oldN; x |= (buffer >>> n); return (char) (x & 0xff); // the above code doesn't quite work for the last character if N = 8 // because buffer will be -1 } /** * Reads the next r bits from this binary input stream and return * as an r-bit character. * * @param r number of bits to read * @return the next {@code r} bits of data from this binary input stream as a {@code char} * @throws NoSuchElementException if there are fewer than {@code r} bits available * @throws IllegalArgumentException unless {@code 1 <= r <= 16} */ public char readChar(int r) { if (r < 1 || r > 16) throw new IllegalArgumentException("Illegal value of r = " + r); // optimize r = 8 case if (r == 8) return readChar(); char x = 0; for (int i = 0; i < r; i++) { x <<= 1; boolean bit = readBoolean(); if (bit) x |= 1; } return x; } /** * Reads the remaining bytes of data from this binary input stream and return as a string. * * @return the remaining bytes of data from this binary input stream as a {@code String} * @throws NoSuchElementException if this binary input stream is empty or if the number of bits * available is not a multiple of 8 (byte-aligned) */ public String readString() { if (isEmpty()) throw new NoSuchElementException("Reading from empty input stream"); StringBuilder sb = new StringBuilder(); while (!isEmpty()) { char c = readChar(); sb.append(c); } return sb.toString(); } /** * Reads the next 16 bits from this binary input stream and return as a 16-bit short. * * @return the next 16 bits of data from this binary input stream as a {@code short} * @throws NoSuchElementException if there are fewer than 16 bits available */ public short readShort() { short x = 0; for (int i = 0; i < 2; i++) { char c = readChar(); x <<= 8; x |= c; } return x; } /** * Reads the next 32 bits from this binary input stream and return as a 32-bit int. * * @return the next 32 bits of data from this binary input stream as a {@code int} * @throws NoSuchElementException if there are fewer than 32 bits available */ public int readInt() { int x = 0; for (int i = 0; i < 4; i++) { char c = readChar(); x <<= 8; x |= c; } return x; } /** * Reads the next r bits from this binary input stream return * as an r-bit int. * * @param r number of bits to read * @return the next {@code r} bits of data from this binary input stream as a {@code int} * @throws NoSuchElementException if there are fewer than r bits available * @throws IllegalArgumentException unless {@code 1 <= r <= 32} */ public int readInt(int r) { if (r < 1 || r > 32) throw new IllegalArgumentException("Illegal value of r = " + r); // optimize r = 32 case if (r == 32) return readInt(); int x = 0; for (int i = 0; i < r; i++) { x <<= 1; boolean bit = readBoolean(); if (bit) x |= 1; } return x; } /** * Reads the next 64 bits from this binary input stream and return as a 64-bit long. * * @return the next 64 bits of data from this binary input stream as a {@code long} * @throws NoSuchElementException if there are fewer than 64 bits available */ public long readLong() { long x = 0; for (int i = 0; i < 8; i++) { char c = readChar(); x <<= 8; x |= c; } return x; } /** * Reads the next 64 bits from this binary input stream and return as a 64-bit double. * * @return the next 64 bits of data from this binary input stream as a {@code double} * @throws NoSuchElementException if there are fewer than 64 bits available */ public double readDouble() { return Double.longBitsToDouble(readLong()); } /** * Reads the next 32 bits from this binary input stream and return as a 32-bit float. * * @return the next 32 bits of data from this binary input stream as a {@code float} * @throws NoSuchElementException if there are fewer than 32 bits available */ public float readFloat() { return Float.intBitsToFloat(readInt()); } /** * Reads the next 8 bits from this binary input stream and return as an 8-bit byte. * * @return the next 8 bits of data from this binary input stream as a {@code byte} * @throws NoSuchElementException if there are fewer than 8 bits available */ public byte readByte() { char c = readChar(); return (byte) (c & 0xff); } /** * Unit tests the {@code BinaryIn} data type. * Reads the name of a file or URL (first command-line argument) * and writes it to a file (second command-line argument). * * @param args the command-line arguments */ public static void main(String[] args) { BinaryIn in = new BinaryIn(args[0]); BinaryOut out = new BinaryOut(args[1]); // read one 8-bit char at a time while (!in.isEmpty()) { char c = in.readChar(); out.write(c); } out.flush(); } } /****************************************************************************** * Copyright 2002-2022, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/