Java 8文件操作全攻略:高效读写TXT文件与并发管理精解

hi,我是程序员王也,一个资深Java开发工程师,平时十分热衷于技术副业变现和各种搞钱项目的程序员~,如果你也是,可以一起交流交流。

今天我们聊一聊平时开发中经常遇到的读写文件相关的问题。

在这里插入图片描述

读取TXT文件

在Java 8中,读取TXT文件可以通过多种方式实现,这里我们将探讨几种常见的方法,并提供相应的代码示例。

  1. 使用java.nio.file.Files类的readAllLines方法

    readAllLines方法能够一次性读取整个文件的所有行到一个List<String>中,适用于文件不大的情况。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.io.IOException;
    import java.util.List;
    
    public class ReadAllLinesExample {
        public static void main(String[] args) {
            String filePath = "path/to/your/file.txt"; // 替换为你的文件路径
            try {
                List<String> lines = Files.readAllLines(Paths.get(filePath));
                for (String line : lines) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们首先定义了文件路径,然后使用Files.readAllLines方法读取文件内容,并遍历每一行打印输出。

  2. 使用java.nio.file.Files类的newBufferedReader方法

    当处理较大的文件时,逐行读取是一种更为内存高效的方式。newBufferedReader方法返回一个BufferedReader对象,可以用来逐行读取文件。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.io.BufferedReader;
    import java.io.IOException;
    
    public class ReadLineByLineExample {
        public static void main(String[] args) {
            String filePath = "path/to/your/file.txt"; // 替换为你的文件路径
            try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们使用了try-with-resources语句来自动关闭BufferedReaderreadLine方法每次读取文件的下一行,直到文件结束。

  3. 异常处理和资源管理

    在读取文件时,可能会遇到各种异常,如文件不存在、权限问题等。正确的异常处理和资源管理是保证程序健壮性的关键。

    public class ReadFileWithExceptionHandling {
        public static void main(String[] args) {
            String filePath = "path/to/your/file.txt"; // 替换为你的文件路径
            try {
                List<String> lines = Files.readAllLines(Paths.get(filePath));
                for (String line : lines) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                System.err.println("An error occurred: " + e.getMessage());
                // 可以在这里添加更多的错误处理逻辑
            }
        }
    }
    

    在这个例子中,我们捕获了IOException并打印出错误消息。这有助于我们了解发生了什么问题,并采取适当的措施。

以下是关于JDK 8读写TXT文件技术文章的第五小节“写入TXT文件”部分的内容,包含了充足的案例源码说明:


写入TXT文件

写入TXT文件是文件处理中的另一个常见任务。在Java 8中,我们可以使用多种方式将数据写入文本文件。本节将介绍几种常用的写入方法,并提供相应的代码示例。

  1. 使用java.nio.file.Files类的write方法

    Files.write方法可以用来写入一系列字符串到文件中。这个方法非常灵活,允许你指定是否覆盖现有文件以及如何处理文件编码。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.List;
    import java.io.IOException;
    
    public class WriteAllLinesExample {
        public static void main(String[] args) {
            String filePath = "path/to/your/output.txt"; // 替换为你的输出文件路径
            List<String> lines = List.of("First line", "Second line", "Third line");
    
            try {
                // 写入字符串列表到文件,如果文件已存在则覆盖
                Files.write(Paths.get(filePath), lines);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们将一个字符串列表写入到指定的TXT文件中。如果文件已经存在,它将被覆盖。

  2. 使用java.nio.file.Files类的newBufferedWriter方法

    newBufferedWriter方法返回一个BufferedWriter对象,可以用来逐行写入文件。这种方法适用于需要对文件进行多次写入操作的场景。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.io.BufferedWriter;
    import java.io.IOException;
    
    public class WriteLineByLineExample {
        public static void main(String[] args) {
            String filePath = "path/to/your/output.txt"; // 替换为你的输出文件路径
    
            try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filePath))) {
                writer.write("First line");
                writer.newLine(); // 写入一个新行
                writer.write("Second line");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们使用BufferedWriter逐行写入数据。注意我们使用了newLine方法来插入一个新行,而不是依靠系统默认的行分隔符。

  3. 处理异常和确保资源释放

    写入文件时,异常处理和资源管理同样重要。下面的示例展示了如何在写入文件时进行异常处理,并确保资源得到正确释放。

    public class WriteFileWithExceptionHandling {
        public static void main(String[] args) {
            String filePath = "path/to/your/output.txt"; // 替换为你的输出文件路径
            String content = "Hello, World!";
    
            try {
                // 写入字符串到文件,如果文件已存在则覆盖
                Files.write(Paths.get(filePath), Collections.singletonList(content));
            } catch (IOException e) {
                System.err.println("An error occurred: " + e.getMessage());
                // 可以在这里添加更多的错误处理逻辑
            }
        }
    }
    

    在这个例子中,我们使用try-catch块来捕获并处理可能发生的IOException。这样可以确保即使在发生异常的情况下,程序也不会因为未关闭的资源而泄露资源。


追加内容到TXT文件

在日常开发中,有时我们需要将新的数据添加到现有文件的末尾,而不是覆盖原有内容。JDK 8提供了简单的方法来实现文件的追加操作。以下是如何使用java.nio.file.Files类来追加内容到TXT文件的示例。

  1. 使用java.nio.file.Files类的append方法

    append方法可以将字符串或字符串列表追加到文件的末尾。这个方法非常适合于需要频繁追加数据的场景。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.io.IOException;
    
    public class AppendToFileExample {
        public static void main(String[] args) {
            String filePath = "path/to/your/file.txt"; // 替换为你的文件路径
            String contentToAppend = "New line to append";
    
            try {
                // 追加字符串到文件末尾
                Files.append(contentToAppend, Paths.get(filePath));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们将一个字符串追加到指定的TXT文件末尾。如果文件不存在,append方法会创建一个新文件。

  2. 使用BufferedWriter追加内容

    如果你需要更细粒度的控制,比如追加多行或者格式化文本,可以使用BufferedWriter。下面是一个使用BufferedWriter追加多行内容的示例。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.io.BufferedWriter;
    import java.io.IOException;
    
    public class AppendMultipleLinesExample {
        public static void main(String[] args) {
            String filePath = "path/to/your/file.txt"; // 替换为你的文件路径
            String firstLine = "First line of appended content";
            String secondLine = "Second line of appended content";
    
            try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filePath))) {
                writer.write(firstLine);
                writer.newLine();
                writer.write(secondLine);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们使用try-with-resources语句来自动关闭BufferedWriter。我们追加了两行文本,每行之后都调用了newLine方法来确保新行的正确性。

  3. 处理异常和确保资源释放

    与读取文件一样,写入文件时也需要妥善处理异常,并确保所有资源在使用后被正确关闭。

    public class AppendWithExceptionHandling {
        public static void main(String[] args) {
            String filePath = "path/to/your/file.txt"; // 替换为你的文件路径
            String contentToAppend = "Additional content to append";
    
            try {
                // 追加字符串到文件末尾,并处理可能的异常
                Files.append(contentToAppend, Paths.get(filePath));
            } catch (IOException e) {
                System.err.println("An error occurred: " + e.getMessage());
                // 可以在这里添加更多的错误处理逻辑
            }
        }
    }
    

    在这个例子中,我们使用try-catch块来捕获并处理可能发生的IOException。这样可以确保即使在发生异常的情况下,程序也不会因为未关闭的资源而泄露资源。

文件属性查询和修改

在文件操作中,获取和修改文件属性是一个常见的需求。JDK 8的java.nio.file包提供了一系列的API来查询文件的元数据,如文件大小、创建时间、最后修改时间等,并且还允许我们修改某些文件属性。

  1. 查询文件属性

    Files类提供了一系列静态方法来查询文件属性。例如,readAttributes方法可以用来获取文件的一系列属性。

    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.attribute.BasicFileAttributes;
    import java.io.IOException;
    
    public class QueryFileAttributes {
        public static void main(String[] args) {
            Path filePath = Paths.get("path/to/your/file.txt"); // 替换为你的文件路径
    
            try {
                BasicFileAttributes attrs = Files.readAttributes(filePath, BasicFileAttributes.class);
                System.out.println("File name: " + attrs.fileName());
                System.out.println("File size: " + attrs.size() + " bytes");
                System.out.println("Last modified time: " + attrs.lastModifiedTime());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们查询了文件的名称、大小和最后修改时间,并将它们打印出来。

  2. 修改文件属性

    Files类还提供了一些方法来修改文件属性。例如,setLastModifiedTime方法可以用来更改文件的最后修改时间。

    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.attribute.BasicFileAttributes;
    import java.nio.file.attribute.FileTime;
    import java.io.IOException;
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    
    public class ModifyFileAttributes {
        public static void main(String[] args) {
            Path filePath = Paths.get("path/to/your/file.txt"); // 替换为你的文件路径
            LocalDateTime newLastModifiedTime = LocalDateTime.now(ZoneId.systemDefault());
    
            try {
                // 修改文件的最后修改时间为当前时间
                Files.setAttribute(filePath, "lastModifiedTime", FileTime.from(newLastModifiedTime));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们将文件的最后修改时间设置为当前系统时间。

  3. 处理异常和资源管理

    在查询和修改文件属性时,也可能会遇到各种异常,如文件不存在、权限不足等。正确的异常处理和资源管理是保证程序健壮性的关键。

    public class HandleFileAttributesException {
        public static void main(String[] args) {
            Path filePath = Paths.get("path/to/your/file.txt"); // 替换为你的文件路径
    
            try {
                BasicFileAttributes attrs = Files.readAttributes(filePath, BasicFileAttributes.class);
                // ... 处理文件属性 ...
            } catch (IOException e) {
                System.err.println("An error occurred: " + e.getMessage());
                // 可以在这里添加更多的错误处理逻辑
            }
        }
    }
    

    在这个例子中,我们使用try-catch块来捕获并处理可能发生的IOException

文件的复制、移动和删除

在日常文件处理任务中,复制、移动和删除文件是经常需要执行的操作。JDK 8的java.nio.file包提供了一系列的API来支持这些操作,使得它们变得更加简单和直观。

  1. 复制文件

    使用Files.copy方法可以复制文件。你可以选择是否保留原有文件的属性,如文件的创建时间和最后修改时间。

    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.io.IOException;
    import java.nio.file.CopyOption;
    
    public class CopyFileExample {
        public static void main(String[] args) {
            Path sourcePath = Paths.get("path/to/source.txt"); // 源文件路径
            Path targetPath = Paths.get("path/to/target.txt"); // 目标文件路径
    
            try {
                // 复制文件,可以选择覆盖目标文件或抛出异常
                CopyOption[] options = new CopyOption[] { StandardCopyOption.REPLACE_EXISTING };
                Files.copy(sourcePath, targetPath, options);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们复制了一个文件,并指定了REPLACE_EXISTING选项来覆盖已存在的文件。

  2. 移动文件

    Files.move方法可以用来移动文件,也就是将文件从一个路径重命名或移动到另一个路径。与复制操作类似,你可以选择是否覆盖目标路径中的文件。

    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.io.IOException;
    import java.nio.file.CopyOption;
    import java.nio.file.StandardCopyOption;
    
    public class MoveFileExample {
        public static void main(String[] args) {
            Path sourcePath = Paths.get("path/to/source.txt"); // 源文件路径
            Path targetPath = Paths.get("path/to/newsource.txt"); // 新的文件路径
    
            try {
                // 移动文件,如果目标文件已存在则覆盖
                Files.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们将文件从一个路径移动到另一个路径,并覆盖了目标路径中的同名文件。

  3. 删除文件

    删除文件可以通过Files.delete方法实现。如果文件不存在,该方法将抛出一个异常。

    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.io.IOException;
    
    public class DeleteFileExample {
        public static void main(String[] args) {
            Path filePath = Paths.get("path/to/your/file.txt"); // 要删除的文件路径
    
            try {
                // 删除文件
                Files.delete(filePath);
                System.out.println("File deleted successfully.");
            } catch (IOException e) {
                System.err.println("An error occurred: " + e.getMessage());
            }
        }
    }
    

    在这个例子中,我们删除了一个文件,并在删除成功后打印了一条确认消息。

  4. 处理异常和资源管理

    在执行文件的复制、移动和删除操作时,可能会遇到各种异常,如文件不存在、权限不足等。正确的异常处理是保证程序健壮性的关键。

    public class HandleFileOperationsException {
        public static void main(String[] args) {
            Path sourcePath = Paths.get("path/to/source.txt"); // 源文件路径
            Path targetPath = Paths.get("path/to/target.txt"); // 目标文件路径
    
            try {
                // 执行文件操作,并处理可能的异常
                Files.copy(sourcePath, targetPath);
            } catch (IOException e) {
                System.err.println("An error occurred: " + e.getMessage());
                // 可以在这里添加更多的错误处理逻辑
            }
        }
    }
    

    在这个例子中,我们使用try-catch块来捕获并处理可能发生的IOException

使用try-with-resources语句管理资源

在Java中处理文件时,正确地关闭打开的资源是一个重要的任务,以避免资源泄露。从JDK 7开始引入的try-with-resources语句可以自动管理资源,确保在try块执行完毕后,所有实现了AutoCloseable接口的资源都会被正确关闭。

  1. 使用try-with-resources读取文件

    当使用Files.newBufferedReader创建BufferedReader对象时,该对象会自动关闭底层的FileInputStream。使用try-with-resources可以简化代码,避免忘记关闭资源。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.io.BufferedReader;
    import java.io.IOException;
    
    public class ReadFileWithTryWithResources {
        public static void main(String[] args) {
            Path filePath = Paths.get("path/to/your/file.txt"); // 替换为你的文件路径
    
            try (BufferedReader reader = Files.newBufferedReader(filePath)) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,BufferedReader对象在try块结束时自动关闭,无需显式调用close方法。

  2. 使用try-with-resources写入文件

    同样地,Files.newBufferedWriter创建的BufferedWriter对象也会在try块结束时自动关闭。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.io.BufferedWriter;
    import java.io.IOException;
    
    public class WriteFileWithTryWithResources {
        public static void main(String[] args) {
            Path filePath = Paths.get("path/to/your/output.txt"); // 替换为你的输出文件路径
    
            try (BufferedWriter writer = Files.newBufferedWriter(filePath)) {
                writer.write("Hello, World!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,BufferedWriter对象在写入操作完成后自动关闭。

  3. 异常处理和资源管理

    使用try-with-resources不仅可以简化资源管理的代码,还可以确保即使在发生异常时,资源也能被正确关闭。

    public class HandleTryWithResourcesException {
        public static void main(String[] args) {
            Path filePath = Paths.get("path/to/your/file.txt"); // 文件路径
    
            try (BufferedReader reader = Files.newBufferedReader(filePath);
                 BufferedWriter writer = Files.newBufferedWriter(Paths.get("path/to/your/output.txt"))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    writer.write(line);
                    writer.newLine();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们读取一个文件并在另一个文件中写入其内容。即使在读取或写入过程中发生异常,两个资源也会被自动关闭。

并发文件读写操作

在多线程环境中,文件的读写操作需要特别注意,以确保数据的一致性和避免潜在的并发问题。Java 8提供了多种机制来支持并发文件操作,包括原子文件操作和并发API。

  1. 原子性写入

    Files.write方法在写入文件时提供了原子性保证,这意味着写入操作要么完全完成,要么完全不发生,这对于并发场景非常重要。

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.io.IOException;
    
    public class AtomicWriteExample {
        public static void main(String[] args) {
            Path filePath = Paths.get("path/to/your/atomicfile.txt"); // 替换为你的文件路径
            String content = "Concurrent update";
    
            // 使用原子写入操作来避免并发问题
            try {
                Files.write(Paths.get(filePath), content.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中,我们使用Files.write方法来原子性地写入文件内容。如果文件已存在,它将被新内容替换。

  2. 并发读取

    当多个线程需要读取同一个文件时,可以使用java.nio.file.Files类的newBufferedReader方法来创建支持并发的BufferedReader

    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.io.BufferedReader;
    import java.io.IOException;
    
    public class ConcurrentReadExample {
        private static Path sharedFile = Paths.get("path/to/your/sharedfile.txt"); // 共享文件路径
    
        public static void readerThread() {
            try (BufferedReader reader = Files.newBufferedReader(sharedFile)) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            // 创建多个线程来并发读取文件
            for (int i = 0; i < 5; i++) {
                new Thread(ConcurrentReadExample::readerThread).start();
            }
        }
    }
    

    在这个例子中,我们创建了多个线程来并发读取同一个文件。BufferedReader的实现是线程安全的,可以同时被多个线程使用。

  3. 使用并发API

    Java 8的java.util.concurrent包提供了多种并发API,如ExecutorServiceCountDownLatch,可以用来管理并发文件操作。

    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.io.IOException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.CountDownLatch;
    
    public class ConcurrentFileProcessing {
        private static Path sharedFile = Paths.get("path/to/your/sharedfile.txt"); // 共享文件路径
        private static final int THREAD_COUNT = 5; // 线程数量
        private static CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
    
        public static void readerThread() {
            try {
                // 执行文件读取操作
            } catch (IOException e) {
                e.printStackTrace();
           } finally {
                latch.countDown(); // 线程完成时调用
            }
        }
    
        public static void main(String[] args) {
            ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
            for (int i = 0; i < THREAD_COUNT; i++) {
                executor.submit(() -> readerThread());
            }
            latch.await(); // 等待所有线程完成
            executor.shutdown();
        }
    }
    

    在这个例子中,我们使用ExecutorService来创建一个固定大小的线程池,并提交多个读取任务。CountDownLatch用来等待所有线程完成操作。

总结

在本文中,我们深入探讨了使用JDK 8进行TXT文件读写操作的多个方面。通过一系列的示例和解释,我们了解了如何有效地读取、写入、复制、移动、删除文件,以及如何查询和修改文件属性。我们还讨论了在并发环境中处理文件时需要注意的问题,并展示了如何使用try-with-resources语句来自动管理资源。

  1. 读取文件

    我们学习了如何使用Files.readAllLinesnewBufferedReader方法来读取文件内容。这些方法提供了简单和内存高效的方式来处理文件数据。

    List<String> lines = Files.readAllLines(Paths.get("file.txt"));
    BufferedReader reader = Files.newBufferedReader(Paths.get("file.txt"));
    
  2. 写入文件

    我们探索了如何使用Files.writenewBufferedWriter方法来写入文件。这些方法允许我们以原子方式写入内容,确保数据的完整性。

    Files.write(Paths.get("file.txt"), "Content".getBytes());
    BufferedWriter writer = Files.newBufferedWriter(Paths.get("file.txt"));
    
  3. 文件操作

    文件的复制、移动和删除操作通过Files.copyFiles.moveFiles.delete方法实现。这些方法提供了一种一致的抽象,使得文件操作变得更加简单。

    Files.copy(Paths.get("source.txt"), Paths.get("destination.txt"), REPLACE_EXISTING);
    Files.move(Paths.get("old.txt"), Paths.get("new.txt"), REPLACE_EXISTING);
    Files.delete(Paths.get("file.txt"));
    
  4. 属性查询和修改

    我们了解了如何使用Files.readAttributesFiles.setAttribute方法来查询和修改文件属性,如文件大小、创建时间、最后修改时间等。

    BasicFileAttributes attrs = Files.readAttributes(Paths.get("file.txt"), BasicFileAttributes.class);
    Files.setAttribute(Paths.get("file.txt"), "lastModifiedTime", FileTime.from(now));
    
  5. 并发和资源管理

    在并发环境中,我们讨论了如何使用try-with-resources语句和并发API来确保文件操作的线程安全和资源的正确释放。

    try (BufferedReader reader = Files.newBufferedReader(Paths.get("file.txt"))) {
        // 并发读取操作
    }
    

最佳实践

  • 当处理文件时,始终确保使用异常处理来捕获和处理可能的IOException
  • 在并发环境中,使用原子性操作和线程安全的方法来避免数据竞争和资源冲突。
  • 利用try-with-resources语句来自动管理资源,减少资源泄露的风险。
  • 在进行文件操作时,考虑使用java.nio.file包中的类,它们提供了更现代和灵活的文件I/O操作。
  • 23
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值