【问题标题】:Java byte array replace all occurrences of byte-array/stringJava字节数组替换所有出现的字节数组/字符串
【发布时间】:2020-08-09 21:23:44
【问题描述】:

是否有任何“已经实现”(非手动)的方法来替换字节数组中所有出现的单字节数组/字符串?我有一个案例,我需要创建包含平台相关文本(Linux(换行)、Windows(回车+换行))的字节数组。我知道这样的任务可以手动实现,但我正在寻找开箱即用的解决方案。请注意,这些字节数组很大,在我的情况下,解决方案需要性能方面。另请注意,我正在处理大量这些字节数组。

我目前的做法:

var byteArray = resourceLoader.getResource("classpath:File.txt").getInputStream().readAllBytes();
byteArray = new String(byteArray)
    .replaceAll((schemeModel.getOsType() == SystemTypes.LINUX) ? "\r\n" : "\n",
                (schemeModel.getOsType() == SystemTypes.LINUX) ? "\n" : "\r\n"
    ).getBytes(StandardCharsets.UTF_8);

由于创建新字符串并使用正则表达式查找出现的情况,这种方法在性能上并不明智。我知道由于 Windows 编码,手动实现需要查看字节序列。因此,手动实施也需要重新分配(如果需要)。

Appache common lang utils 包含ArrayUtils,其中包含方法
byte[] removeAllOccurrences(byte[] array, byte element)。是否有任何第三方库包含类似的方法来替换字节数组中出现的 ALL 字节数组/字符串??

编辑:正如 cmets 中提到的 @saka1029,我的方法不适用于 Windows 操作系统类型。由于这个错误,我需要坚持以下正则表达式:

(schemeModel.getOsType() == SystemTypes.LINUX) ? "\\r\\n" : "[?:^\\r]\\n", 
(schemeModel.getOsType() == SystemTypes.LINUX) ? "\n" : "\r\n")

这样,对于windows的情况,只搜索没有前面'\r'的'\n'的出现并用'\r\n'替换(正则表达式被修改为在'\n'而不是[^ \r]\n 直接定位,否则行中的最后一个字母也会被提取)。这样的工作流程无法使用常规方法实现,因此使这个问题无效。

【问题讨论】:

  • "包含平台相关文本的字节数组" - 如果您正在处理文本,为什么不使用字符串?正如您所发现的,字节数组很难使用。我们所说的数组有多大?
  • 我的用例需要使用 byteArray,因为我将其作为输入(从输入流中读取文件)并需要使用 ZipOutputStream 进一步处理它,从而在我的 API 中生成可下载的 Zip。文件没有静态大小(与普通 Java 文件大小相同),并且有很多这样的文件(从 20 到 100)。如前所述,直接使用字符串会导致创建此类字符串(我无法直接获取字符串),在我的情况下是不可接受的。
  • 如果您关心的是正则表达式,只需将replaceAll 更改为replaceThe replace method does not use regular expressions.
  • @VGR 感谢您的回复。没有注意到这一点。正则表达式不是我唯一的一个问题,但这肯定会给我带来性能提升。
  • @saka1029 我注意到了这一点,它也使答案无效。在 Windows 情况下,我需要搜索出现的 '\n' 而不是前面的 '\r' 并替换它。传统方法无法做到这一点,因此我可能会坚持使用正则表达式。

标签: java arrays line-endings


【解决方案1】:

如果您正在阅读文本,则应将其视为文本,而不是字节。使用BufferedReader 逐行读取,并插入您自己的换行符序列。

String newline = schemeModel.getOsType() == SystemTypes.LINUX ? "\n" : "\r\n";

OutputStream out = /* ... */;

try (Writer writer = new BufferedWriter(
        new OutputStreamWriter(out, StandardCharsets.UTF_8));
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(
            resourceLoader.getResource("classpath:File.txt").getInputStream(),
            StandardCharsets.UTF_8))) {

    String line;
    while ((line = reader.readLine()) != null) {
        writer.write(line);
        writer.write(newline);
    }
}

不需要字节数组,并且您只使用了少量内存——容纳遇到的最大行所需的内存量。 (我很少看到行长超过 1 KB 的文本,但即使是 1 MB 的内存需求也很小。)

如果您要“修复” zip 条目,则 OutputStream 可以是指向新 ZipEntry 的 ZipOutputStream:

String newline = schemeModel.getOsType() == SystemTypes.LINUX ? "\n" : "\r\n";

ZipInputStream oldZip = /* ... */;
ZipOutputStream newZip = /* ... */;

ZipEntry entry;
while ((entry = oldZip.getNextEntry()) != null) {
    newZip.putNextEntry(entry);

    // We only want to fix line endings in text files.
    if (!entry.getName().matches(".*\\." +
        "(?i:txt|x?html?|xml|json|[ch]|cpp|cs|py|java|properties|jsp)")) {

        oldZip.transferTo(newZip);
        continue;
    }

    Writer writer = new BufferedWriter(
        new OutputStreamWriter(newZip, StandardCharsets.UTF_8));

    BufferedReader reader = new BufferedReader(
        new InputStreamReader(oldZip, StandardCharsets.UTF_8));

    String line;
    while ((line = reader.readLine()) != null) {
        writer.write(line);
        writer.write(newline);
    }

    writer.flush();
}
    

一些注意事项:

  • 您是否故意忽略 Mac(以及其他既不是 Windows 也不是 Linux 的操作系统)?您应该为除 Windows 之外的所有内容假设 \n。即schemeModel.getOsType() == SystemTypes.WINDOWS ? "\r\n" : "\n"
  • 您的代码包含new String(byteArray),它假定您的资源字节使用运行程序的系统的默认字符集。我怀疑这不是您想要的;我在 InputStreamReader 的构造中添加了StandardCharsets.UTF_8 来解决这个问题。如果您真的打算使用默认字符集读取字节,则可以删除第二个构造函数参数。

【讨论】:

  • @saka1029 UTF-8 通常是最好的默认值。现在使用 UTF-8 的系统比使用 ISO-8859-x 的系统多得多。
  • 在我看来这是一个很好的解决方案,因为行分隔符很容易重新设计。目前在我的字节数组上使用InputStreamReaderByteArrayInputStream,因为我的项目太大而无法立即返工。我得到了预期的结果。
  • @saka1029 一个单字节字符集只能处理 256 个字符。它不可能处理 UTF-8 可以处理的所有字符。而且,任何非 Windows 系统都极有可能使用 UTF-8。
  • @saka1029 你知道 Java 字符串不像 C 字符串,也不是任意字节的合适容器,对吧?
猜你喜欢
  • 2011-10-20
  • 1970-01-01
  • 2012-08-01
  • 2011-10-04
  • 1970-01-01
  • 1970-01-01
  • 2013-10-23
  • 2021-12-27
  • 1970-01-01
相关资源
最近更新 更多