【问题标题】:How can I read a file containing text and image data?如何读取包含文本和图像数据的文件?
【发布时间】:2013-12-23 06:00:34
【问题描述】:

我有一个结构如下的文件:

<number>
<title>
<text>
<image data>

例如:

176
Elephant
This image shows a cute elephant.
<image data bytes go here>

上面的例子我是这样写的:

String filepath = "myfile.txt";
BufferedImage image = ...; //load some image here

try(FileOutputStream output = new FileOutputStream(filepath);
BufferedWriter writer = new BufferedWriter(new FileWriter(filepath)))
{
    writer.write("Number: 176");
    writer.newLine();
    writer.write("Title: Elephant");
    writer.newLine();
    writer.write("Text: This image shows a cute elephant.");
    writer.newLine();

    ImageIO.write(image, "png", output);

    writer.flush();
}
catch (Exception) {//exception handling}

这很好用。但是,我不知道如何读取文件。 问题是我需要调用 ImageIO.read() 来解析图像,但我不能用 BufferedReader 调用它。我的草稿如下所示:

String filepath = "myfile.txt";

try (BufferedReader reader = new BufferedReader(new java.io.FileReader(filepath)))
{
    String number = reader.readLine();
    reader.readLine(); //skip a free line

    String title = reader.readLine();
    reader.readLine(); //skip a free line

    String text = reader.readLine();
    reader.readLine(); //skip a free line

    BufferedImage image = ???; //how can I read the image here? <----------------

}
catch (Exception e) {//error handling ...}

那么:如何读取图像(使用 ImageIO)?

非常感谢任何帮助:)


编辑:就像我用两个不同的作家写作一样,阅读时是否可以使用两个读者?比如“读取 3 行字符串,然后读取 1 行二进制数据”。

【问题讨论】:

  • 图片数据是如何格式化的?它是以二进制形式还是 Base64 表示的?
  • 以二进制形式表示。 (我使用 ImageIO.read 加载 PNG 图像文件,将其存储到 BufferedImage 并使用 ImageIO.write 写入)
  • 如果图像数据与其他数据混合在一起,那么您需要将该二进制数据读取到另一个流输入流中,以便可以从文件中正确读取。您“可能”能够使用与读取文件相同的InputStream 并简单地读取到图像数据的开头...
  • @MadProgrammer 我理解并欣赏你的想法,但也许实际的代码会对我有很大帮助:)
  • @Loc 我没有否决你的问题:O

标签: java image file text bufferedreader


【解决方案1】:

在我看来,问题在于阅读器和流的混合......

现在,我尝试使用...

try (OutputStream output = new FileOutputStream(filepath);
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output))) {
    
    writer.write("Number: 176");
    writer.newLine();
    writer.write("Title: Elephant");
    writer.newLine();
    writer.write("Text: This image shows a cute elephant.");
    writer.newLine();

    writer.flush();

    ImageIO.write(image, "png", output);

    output.flush();
} catch (Exception exp) {
    exp.printStackTrace();
}

写入文件。希望是更新输入流位置,并将内容写入文件末尾...

String filepath = "myfile.txt";
BufferedImage image = null;
try (InputStream is = new FileInputStream(new File(filepath));
                BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
    
    String number = br.readLine();
    String title = br.readLine();
    String text = br.readLine();
    
    System.out.println(number);
    System.out.println(title);
    System.out.println(text);

    ImageInputStream iis = ImageIO.createImageInputStream(is);
    image = ImageIO.read(iis);
    ImageIO.write(image, "png", new File("test.png"));
} catch (Exception exp) {
    exp.printStackTrace();
}

阅读它。

但这似乎不起作用。 “可能”发生的是文件位置没有更新到我们需要它正确读取图像的正确位置。

我做的是这个......

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;

public class ReadWriteImage {

    public static void main(String[] args) {
        try {
            writeTest();
            readTest();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public static void readTest() throws IOException {
        String filepath = "myfile.txt";
        BufferedImage image = null;
        try (InputStream is = new FileInputStream(new File(filepath))) {
        
            int lineCount = 0;
            StringBuilder sb = new StringBuilder(128);
        
            String number = null;
            String title = null;
            String text = null;
        
            int b = -1;
            while (lineCount < 3 && (b = is.read()) != -1) {
                if ((char)b == '\n') {
                    switch (lineCount) {
                        case 0:
                            number = sb.toString();
                            break;
                        case 1:
                            title = sb.toString();
                            break;
                        case 2:
                            text = sb.toString();
                            break;
                    }
                    sb.delete(0, sb.length());
                    lineCount++;
                } else {
                    sb.append((char)b);
                }
            }
        
            System.out.println(number);
            System.out.println(title);
            System.out.println(text);

            ImageInputStream iis = ImageIO.createImageInputStream(is);
            image = ImageIO.read(iis);
            ImageIO.write(image, "png", new File("test.png"));
        } catch (Exception exp) {
            exp.printStackTrace();
        }
    }

    public static void writeTest() throws IOException {
        String filepath = "myfile.txt";
        BufferedImage image = ImageIO.read(new File("/Users/swhitehead/Dropbox/MegaTokyo/thumnails/2005-09-29-3957_400.jpg"));

        try (FileOutputStream output = new FileOutputStream(filepath)) {
        
            output.write("Number: 176\n".getBytes());
            output.write("Title: Elephant\n".getBytes());
            output.write("Text: This image shows a cute elephant.\n".getBytes());

            ImageIO.write(image, "png", output);

            output.flush();
        } catch (Exception exp) {
            exp.printStackTrace();
        }

    }
}

基本上,我只是直接使用了OutputStreamInputStream...

更新

文件内容以...开头

Number: 176
Title: Elephant
Text: This image shows a cute elephant.
âPNG

欢迎来到 2021

所以,序数答案是前段时间做出的,世界已经在前进。除非您有非常特殊的不需要(即传输速度/带宽限制),否则我强烈建议您使用 JSON 和 Base64 之类的东西来发送混合在一起的二进制和文本。

主要原因,实际上编码/解码和维护要简单得多。最初的解决方案很难添加新内容,因为两端都需要能够支持更改,而 JSON 之类的东西(如果做得好)可以避免添加和删除内容。构建更复杂的解析工作流也更容易,因为您不依赖于流的“读/写”位置。

这个例子使用了org.json 库,但是你可以使用任何你想要的库。

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import org.json.JSONObject;

public class Main {

    public static void main(String[] args) throws IOException {
        new Main();
    }

    public Main() throws IOException {
        read(write());
    }

    public String write() throws IOException {
        BufferedImage image = ImageIO.read(getClass().getResource("/images/Background.png"));

        JSONObject jobj = new JSONObject();
        jobj.put("number", 176);
        jobj.put("title", "Elephant");
        jobj.put("text", "This image shows a cute elepant");

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            ImageIO.write(image, "png", baos);
            baos.flush();
            byte[] encode = Base64.getEncoder().encode(baos.toByteArray());
            String text = new String(encode, StandardCharsets.UTF_8);
            jobj.put("image", text);
        }

        String text = jobj.toString();

        return text;
    }

    public void read(String jsonText) throws IOException {
        JSONObject jobj = new JSONObject(jsonText);
        System.out.println("Number = " + jobj.getInt("number"));
        System.out.println("Title = " + jobj.getString("title"));
        System.out.println("Text = " + jobj.getString("text"));

        String encodedImage = jobj.getString("image");
        byte[] imageBytes = Base64.getDecoder().decode(encodedImage.getBytes(StandardCharsets.UTF_8));
        try (ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes)) {
            BufferedImage image = ImageIO.read(bais);

            JLabel label = new JLabel(new ImageIcon(image));
            label.setText("<html>Number = " + jobj.getInt("number") + "<br>Title = " + jobj.getString("title") + "<br>Text = " + jobj.getString("text"));

            JOptionPane.showMessageDialog(null, label);
        }
    }

}

如果我想发送 2 个文件怎么办?

然后使用JSONArrayorg.json 相当强大,有关详细信息,请参阅 Introduction to JSON-Java 。 Google 实现(GSON?)还提供了许多功能,可用于从 POJO 编码/解码 json,如果这是你的事

【讨论】:

  • 非常感谢! :) 我阅读了您的代码,理解了它并看到它完全按照我希望的方式工作。现在,我可以根据自己的需要调整它并优化性能(例如,使用 read(byte[]) 而不是 read())。我个人的教训是:永远不要将阅读或写作与阅读器和流媒体混为一谈。再次感谢您的努力。
  • @Loc 是的,但这很好:) 在我的第一个示例中使用 FileWriter 只是一种让他在我脑海中以代码形式运行的想法的方法。最后,使用二进制格式没有问题(或者有吗?)。
  • 没问题。它有效,我知道。我只以为你想使用 TEXTUAL 格式(你的文件扩展名是 .txt)
  • @loc 试试这个例子并在文本编辑器中打开文件,你应该能够在二进制内容之前读取文本。就我个人而言,我同意,使用 base64 编码会更好,甚至可能使用 XML 作为基本格式……但这会让 OP 越界……
  • 即使它是二进制文件,您仍然可以使用文本编辑器查看其内容。对吗?
【解决方案2】:

举个例子:

这很简单,如果您的示例中只有 3 行文本,那么当您检测到 \n 时,您只需读取 Text 的每一行,当您到达第三行时,您就知道图像数据是下一个,然后从该偏移量读取到您的图像中。

一个更好的方法是先用binary数据的偏移量写一个简单的索引头,然后你不必解析\n你可以读到偏移量并将所有内容视为文本,然后从偏移量读取并将其余部分视为您的图像。

您没有理由需要将任何内容编码为 Base64 或类似的内容,这只会使您的文件格式膨胀并导致更多的复杂性。

【讨论】:

  • 这听起来和我试图做的完全一样。但是您能否在代码中显示我是如何“从该偏移量读取到您的图像”的? (我的意思是:我必须在“BufferedImage image = ???”所在的行中写什么?)
  • 不将所有内容都转换为 base64。仅图像。因为上面要使用TEXT格式所以我们必须这样做。如果他想使用 BINARY 格式,我们不必将 Image 转换为 base64 字符串。
  • 你需要做的一切都在 JavaDoc 中,尝试一下,然后询问如何解决它。
【解决方案3】:

不确定它是否会起作用,但正如@MadProgrammer 所说,在 ImageIO.read 调用中使用 BufferedReader 初始化中的相同阅读器:

Reader fileReader = new FileReader(...);
BufferedReader reader = new BufferedReader(fileReader);
...
BufferedImage img = ImageIO.read(fileReader);

或者,使用 FileReader(FileInputStream)... 毕竟,您在写作时所做的事情非常相似。这里要吸取的教训:对称在这种情况下总是有帮助的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 2013-03-02
    • 2016-10-05
    • 2018-09-12
    • 2011-10-30
    • 1970-01-01
    • 2015-01-21
    相关资源
    最近更新 更多