十七、文件(2)

news2024/11/20 23:18:39

本章概要

  • 文件系统
  • 路径监听
  • 文件查找
  • 文件读写

文件系统

为了完整起见,我们需要一种方法查找文件系统相关的其他信息。在这里,我们使用静态的 FileSystems 工具类获取"默认"的文件系统,但你同样也可以在 Path 对象上调用 getFileSystem() 以获取创建该 Path 的文件系统。你可以获得给定 URI 的文件系统,还可以构建新的文件系统(对于支持它的操作系统)。

import java.nio.file.*;

public class FileSystemDemo {
    static void show(String id, Object o) {
        System.out.println(id + ": " + o);
    }

    public static void main(String[] args) {
        System.out.println(System.getProperty("os.name"));
        FileSystem fsys = FileSystems.getDefault();
        for (FileStore fs : fsys.getFileStores()) {
            show("File Store", fs);
        }
        for (Path rd : fsys.getRootDirectories()) {
            show("Root Directory", rd);
        }
        show("Separator", fsys.getSeparator());
        show("UserPrincipalLookupService",
                fsys.getUserPrincipalLookupService());
        show("isOpen", fsys.isOpen());
        show("isReadOnly", fsys.isReadOnly());
        show("FileSystemProvider", fsys.provider());
        show("File Attribute Views",
                fsys.supportedFileAttributeViews());
    }
}

在这里插入图片描述

一个 FileSystem 对象也能生成 WatchServicePathMatcher 对象,将会在接下来两章中详细讲解。

路径监听

通过 WatchService 可以设置一个进程对目录中的更改做出响应。在这个例子中,delTxtFiles() 作为一个单独的任务执行,该任务将遍历整个目录并删除以 .txt 结尾的所有文件,WatchService 会对文件删除操作做出反应:

PathWatcher.java

import java.io.IOException;
import java.nio.file.*;

import static java.nio.file.StandardWatchEventKinds.*;

import java.util.concurrent.*;

public class PathWatcher {
    static Path test = Paths.get("test");

    static void delTxtFiles() {
        try {
            Files.walk(test)
                    .filter(f ->
                            f.toString()
                                    .endsWith(".txt"))
                    .forEach(f -> {
                        try {
                            System.out.println("deleting " + f);
                            Files.delete(f);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        Directories.refreshTestDir();
        Directories.populateTestDir();
        Files.createFile(test.resolve("Hello.txt"));
        WatchService watcher = FileSystems.getDefault().newWatchService();
        test.register(watcher, ENTRY_DELETE);
        Executors.newSingleThreadScheduledExecutor()
                .schedule(PathWatcher::delTxtFiles,
                        250, TimeUnit.MILLISECONDS);
        WatchKey key = watcher.take();
        for (WatchEvent evt : key.pollEvents()) {
            System.out.println("evt.context(): " + evt.context() +
                    "\nevt.count(): " + evt.count() +
                    "\nevt.kind(): " + evt.kind());
            System.exit(0);
        }
    }
}

RmDir.java

import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.IOException;

public class RmDir {
    public static void rmdir(Path dir) throws IOException {
        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

Directories.java

import java.util.*;
import java.nio.file.*;

public class Directories {
    static Path test = Paths.get("test");
    static String sep = FileSystems.getDefault().getSeparator();
    static List<String> parts = Arrays.asList("foo", "bar", "baz", "bag");

    static Path makeVariant() {
        Collections.rotate(parts, 1);
        return Paths.get("test", String.join(sep, parts));
    }

    static void refreshTestDir() throws Exception {
        if (Files.exists(test)) {
            RmDir.rmdir(test);
        }
        if (!Files.exists(test)) {
            Files.createDirectory(test);
        }
    }

    public static void main(String[] args) throws Exception {
        refreshTestDir();
        Files.createFile(test.resolve("Hello.txt"));
        Path variant = makeVariant();
        // Throws exception (too many levels):
        try {
            Files.createDirectory(variant);
        } catch (Exception e) {
            System.out.println("Nope, that doesn't work.");
        }
        populateTestDir();
        Path tempdir = Files.createTempDirectory(test, "DIR_");
        Files.createTempFile(tempdir, "pre", ".non");
        Files.newDirectoryStream(test).forEach(System.out::println);
        System.out.println("*********");
        Files.walk(test).forEach(System.out::println);
    }

    static void populateTestDir() throws Exception {
        for (int i = 0; i < parts.size(); i++) {
            Path variant = makeVariant();
            if (!Files.exists(variant)) {
                Files.createDirectories(variant);
                Files.copy(Paths.get("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\Directories.java"),
                        variant.resolve("File.txt"));
                Files.createTempFile(variant, null, null);
            }
        }
    }
}

在这里插入图片描述

delTxtFiles() 中的 try 代码块看起来有些多余,因为它们捕获的是同一种类型的异常,外部的 try 语句似乎已经足够了。然而出于某种原因,Java 要求两者都必须存在(这也可能是一个 bug)。还要注意的是在 filter() 中,我们必须显式地使用 f.toString() 转为字符串,否则我们调用 endsWith() 将会与整个 Path 对象进行比较,而不是路径名称字符串的一部分进行比较。

一旦我们从 FileSystem 中得到了 WatchService 对象,我们将其注册到 test 路径以及我们感兴趣的项目的变量参数列表中,可以选择 ENTRY_CREATEENTRY_DELETEENTRY_MODIFY(其中创建和删除不属于修改)。

因为接下来对 watcher.take() 的调用会在发生某些事情之前停止所有操作,所以我们希望 deltxtfiles() 能够并行运行以便生成我们感兴趣的事件。为了实现这个目的,我通过调用 Executors.newSingleThreadScheduledExecutor() 产生一个 ScheduledExecutorService 对象,然后调用 schedule() 方法传递所需函数的方法引用,并且设置在运行之前应该等待的时间。

此时,watcher.take() 将等待并阻塞在这里。当目标事件发生时,会返回一个包含 WatchEventWatchkey 对象。展示的这三种方法是能对 WatchEvent 执行的全部操作。

查看输出的具体内容。即使我们正在删除以 .txt 结尾的文件,在 Hello.txt 被删除之前,WatchService 也不会被触发。你可能认为,如果说"监视这个目录",自然会包含整个目录和下面子目录,但实际上:只会监视给定的目录,而不是下面的所有内容。如果需要监视整个树目录,必须在整个树的每个子目录上放置一个 Watchservice

import java.io.IOException;
import java.nio.file.*;

import static java.nio.file.StandardWatchEventKinds.*;

import java.util.concurrent.*;

public class TreeWatcher {

    static void watchDir(Path dir) {
        try {
            WatchService watcher =
                    FileSystems.getDefault().newWatchService();
            dir.register(watcher, ENTRY_DELETE);
            Executors.newSingleThreadExecutor().submit(() -> {
                try {
                    WatchKey key = watcher.take();
                    for (WatchEvent evt : key.pollEvents()) {
                        System.out.println(
                                "evt.context(): " + evt.context() +
                                        "\nevt.count(): " + evt.count() +
                                        "\nevt.kind(): " + evt.kind());
                        System.exit(0);
                    }
                } catch (InterruptedException e) {
                    return;
                }
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        Directories.refreshTestDir();
        Directories.populateTestDir();
        Files.walk(Paths.get("test"))
                .filter(Files::isDirectory)
                .forEach(TreeWatcher::watchDir);
        PathWatcher.delTxtFiles();
    }
}

在这里插入图片描述

watchDir() 方法中给 WatchSevice 提供参数 ENTRY_DELETE,并启动一个独立的线程来监视该Watchservice。这里我们没有使用 schedule() 进行启动,而是使用 submit() 启动线程。我们遍历整个目录树,并将 watchDir() 应用于每个子目录。现在,当我们运行 deltxtfiles() 时,其中一个 Watchservice 会检测到每一次文件删除。

文件查找

到目前为止,为了找到文件,我们一直使用相当粗糙的方法,在 path 上调用 toString(),然后使用 string 操作查看结果。事实证明,java.nio.file 有更好的解决方案:通过在 FileSystem 对象上调用 getPathMatcher() 获得一个 PathMatcher,然后传入您感兴趣的模式。模式有两个选项:globregexglob 比较简单,实际上功能非常强大,因此您可以使用 glob 解决许多问题。如果您的问题更复杂,可以使用 regex,这将在接下来的 Strings 一章中解释。

在这里,我们使用 glob 查找以 .tmp.txt 结尾的所有 Path

import java.nio.file.*;

public class Find {
    public static void main(String[] args) throws Exception {
        Path test = Paths.get("test");
        Directories.refreshTestDir();
        Directories.populateTestDir();
        // Creating a *directory*, not a file:
        Files.createDirectory(test.resolve("dir.tmp"));

        PathMatcher matcher = FileSystems.getDefault()
                .getPathMatcher("glob:**/*.{tmp,txt}");
        Files.walk(test)
                .filter(matcher::matches)
                .forEach(System.out::println);
        System.out.println("***************");

        PathMatcher matcher2 = FileSystems.getDefault()
                .getPathMatcher("glob:*.tmp");
        Files.walk(test)
                .map(Path::getFileName)
                .filter(matcher2::matches)
                .forEach(System.out::println);
        System.out.println("***************");

        Files.walk(test) // Only look for files
                .filter(Files::isRegularFile)
                .map(Path::getFileName)
                .filter(matcher2::matches)
                .forEach(System.out::println);
    }
}

在这里插入图片描述

matcher 中,glob 表达式开头的 **/ 表示“当前目录及所有子目录”,这在当你不仅仅要匹配当前目录下特定结尾的 Path 时非常有用。单 * 表示“任何东西”,然后是一个点,然后大括号表示一系列的可能性—我们正在寻找以 .tmp.txt 结尾的东西。您可以在 getPathMatcher() 文档中找到更多详细信息。

matcher2 只使用 *.tmp,通常不匹配任何内容,但是添加 map() 操作会将完整路径减少到末尾的名称。

注意,在这两种情况下,输出中都会出现 dir.tmp,即使它是一个目录而不是一个文件。要只查找文件,必须像在最后 files.walk() 中那样对其进行筛选。

文件读写

此时,我们可以对路径和目录做任何事情。 现在让我们看一下操纵文件本身的内容。

如果一个文件很“小”,也就是说“它运行得足够快且占用内存小”,那么 java.nio.file.Files 类中的实用程序将帮助你轻松读写文本和二进制文件。

Files.readAllLines() 一次读取整个文件(因此,“小”文件很有必要),产生一个List<String>。 对于示例文件,我们将重用streams/Cheese.dat

ListOfLines.java

import java.nio.file.*;

public class ListOfLines {
    public static void main(String[] args) throws Exception {
        Files.readAllLines(Paths.get("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\Cheese.dat"))
                .stream()
                .filter(line -> !line.startsWith("//"))
                .map(line ->
                        line.substring(0, line.length() / 2))
                .forEach(System.out::println);
    }
}

Cheese.dat

// streams/Cheese.dat
Not much of a cheese shop really, is it?
Finest in the district, sir.
And what leads you to that conclusion?
Well, it's so clean.
It's certainly uncontaminated by cheese.

在这里插入图片描述

跳过注释行,其余的内容每行只打印一半。 这实现起来很简单:你只需将 Path 传递给 readAllLines() (以前的 java 实现这个功能很复杂)。readAllLines() 有一个重载版本,包含一个 Charset 参数来存储文件的 Unicode 编码。

Files.write() 被重载以写入 byte 数组或任何 Iterable 对象(它也有 Charset 选项):

import java.util.*;
import java.nio.file.*;

public class Writing {
    static Random rand = new Random(47);
    static final int SIZE = 1000;

    public static void main(String[] args) throws Exception {
        // Write bytes to a file:
        byte[] bytes = new byte[SIZE];
        rand.nextBytes(bytes);
        Files.write(Paths.get("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\bytes.dat"), bytes);
        System.out.println("bytes.dat: " + Files.size(Paths.get("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\bytes.dat")));

        // Write an iterable to a file:
        List<String> lines = Files.readAllLines(
                Paths.get("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\Cheese.dat"));
        Files.write(Paths.get("Cheese.txt"), lines);
        System.out.println("Cheese.txt: " + Files.size(Paths.get("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\Cheese.txt")));
    }
}

Cheese.txt

// streams/Cheese.dat
Not much of a cheese shop really, is it?
Finest in the district, sir.
And what leads you to that conclusion?
Well, it's so clean.
It's certainly uncontaminated by cheese.

在这里插入图片描述

我们使用 Random 来创建一个随机的 byte 数组; 你可以看到生成的文件大小是 1000。

一个 List 被写入文件,任何 Iterable 对象也可以这么做。

如果文件大小有问题怎么办? 比如说:

  1. 文件太大,如果你一次性读完整个文件,你可能会耗尽内存。
  2. 您只需要在文件的中途工作以获得所需的结果,因此读取整个文件会浪费时间。

Files.lines() 方便地将文件转换为行的 Stream

import java.nio.file.*;

public class ReadLineStream {
    public static void main(String[] args) throws Exception {
        Files.lines(Paths.get("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\ReadLineStream.java"))
                .skip(5)
                .findFirst()
                .ifPresent(System.out::println);
    }
}

在这里插入图片描述

这对本章中第一个示例代码做了流式处理,跳过 5 行,然后选择下一行并将其打印出来。

Files.lines() 对于把文件处理行的传入流时非常有用,但是如果你想在 Stream 中读取,处理或写入怎么办?这就需要稍微复杂的代码:

import java.io.*;
import java.nio.file.*;
import java.util.stream.*;

public class StreamInAndOut {
    public static void main(String[] args) {
        try (
                Stream<String> input =
                        Files.lines(Paths.get("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\StreamInAndOut.java"));
                PrintWriter output =
                        new PrintWriter("D:\\onJava\\test\\src\\main\\java\\com\\example\\test\\StreamInAndOut.txt")
        ) {
            input.map(String::toUpperCase)
                    .forEachOrdered(output::println);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

输出文件内容:

PACKAGE COM.EXAMPLE.TEST;// FILES/STREAMINANDOUT.JAVA

IMPORT JAVA.IO.*;
IMPORT JAVA.NIO.FILE.*;
IMPORT JAVA.UTIL.STREAM.*;

PUBLIC CLASS STREAMINANDOUT {
    PUBLIC STATIC VOID MAIN(STRING[] ARGS) {
        TRY (
                STREAM<STRING> INPUT =
                        FILES.LINES(PATHS.GET("D:\\ONJAVA\\TEST\\SRC\\MAIN\\JAVA\\COM\\EXAMPLE\\TEST\\STREAMINANDOUT.JAVA"));
                PRINTWRITER OUTPUT =
                        NEW PRINTWRITER("D:\\ONJAVA\\TEST\\SRC\\MAIN\\JAVA\\COM\\EXAMPLE\\TEST\\STREAMINANDOUT.TXT")
        ) {
            INPUT.MAP(STRING::TOUPPERCASE)
                    .FOREACHORDERED(OUTPUT::PRINTLN);
        } CATCH (EXCEPTION E) {
            THROW NEW RUNTIMEEXCEPTION(E);
        }
    }
}

因为我们在同一个块中执行所有操作,所以这两个文件都可以在相同的 try-with-resources 语句中打开。PrintWriter 是一个旧式的 java.io 类,允许你“打印”到一个文件,所以它是这个应用的理想选择。如果你看一下 StreamInAndOut.txt,你会发现它里面的内容确实是大写的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1111782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java 进阶篇】HTML DOM 事件详解

当用户在网页上点击按钮、输入文本、鼠标移动到某个区域或执行其他互动操作时&#xff0c;这些动作都可以触发事件。HTML DOM&#xff08;文档对象模型&#xff09;允许我们使用JavaScript来捕获、处理和响应这些事件&#xff0c;以实现网页的交互和动态性。本篇博客将围绕HTML…

选择最适合的产品研发和运营管理工具

Zoho Projects项目管理工具&#xff0c;以其卓越的敏捷开发协作能力&#xff0c;有效提升了产品规划和研发效率。 以下是对其优势的深入探讨&#xff1a; 一、面临的问题分析 需求变动频繁&#xff1a;在快速变化的市场环境中&#xff0c;如何灵活应对需求变动是一大挑战。跨…

数据库中了mkp勒索病毒怎么办?Mkp勒索病毒解密,数据恢复

相信在本月有很多的企业都遭到了mkp勒索病毒的攻击&#xff0c;该勒索病毒非常狡猾&#xff0c;利用中秋国庆假期期间&#xff0c;对很多的企业服务器进行了攻击&#xff0c;导致服务器内的所有文件都被加密&#xff0c;严重影响了企业的正常工作&#xff0c;经过最近一段时间对…

微信小程序怎么注册_微信小程序怎么可以免费认证

随着微信小程序的日益普及&#xff0c;越来越多的企业和个人开始关注如何注册微信小程序。本文将详细介绍微信小程序注册的步骤以及注意事项&#xff0c;帮助大家更好地了解如何成功注册微信小程序。 一、了解微信小程序注册的意义 微信小程序是一种轻量级的应用程序&#xff…

BAT029:批量为指定路径下的文件添加8位修改日期后缀名

引言&#xff1a;编写批处理程序&#xff0c;实现批量为指定路径下的文件添加8位修改日期后缀名。 一、新建Windows批处理文件 参考博客&#xff1a; CSDNhttps://mp.csdn.net/mp_blog/creation/editor/132137544 二、写入批处理代码 1.右键新建的批处理文件&#xff0c;点击…

eBay类目限制要多久?eBay促销活动有哪些?-站斧浏览器

eBay类目限制要多久&#xff1f; 1、eBay对不同类目的商品有不同的限制和要求。一些类目可能对新卖家有一定的限制&#xff0c;限制他们在该类目下销售商品的数量或需要满足某些条件才能进行销售。 2、对于新卖家的限制通常是在一定时间内&#xff0c;比如30天或90天&#xf…

【数据结构】线性表(四)双向链表的各种操作(插入、删除、查找、修改、遍历打印)

目录 线性表的定义及其基本操作&#xff08;顺序表插入、删除、查找、修改&#xff09; 四、线性表的链接存储结构 1. 单链表 2. 循环链表 3. 双向链表 a. 双向链表节点结构 b. 创建一个新的节点 c. 在链表末尾插入节点 d. 在指定位置插入节点 e. 删除指定位置的节点…

LeetCode算法刷题(python) Day42|09动态规划|62.不同路径、63. 不同路径 II

目录 LeetCode 62. 不同路径LeetCode 63. 不同路径II LeetCode 62. 不同路径 力扣题目链接 class Solution:def uniquePaths(self, m: int, n: int) -> int:dp [[1] * n for _ in range(m)]for j in range(n):for i in range(m):if i 0 and j > 0:dp[i][j] dp[i][j-1…

Python实现番茄小说内容下载

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 环境使用: Python 3.8 Pycharm 模块使用: requests --> pip install requests re parsel 代码展示&#xff1a; 导入模块 # 导入数据请求模块 import…

[自定义 Vue 组件] 小尾巴 Logo 组件 TailLogo

文字归档于&#xff1a;https://www.yuque.com/u27599042/coding_star/apt6y731ybmxgu5g 组件效果 组件依赖 自定义字符串工具函数 stringIsNull https://www.yuque.com/u27599042/coding_star/slncupw7un3ce7cb import {stringIsNull} from "/utils/string_utils.js&q…

python进制和编码

一、进制 计算机中底层所有的数据都是以 010101的形式存在&#xff08;图片、文本、视频等&#xff09;。 二、进制转换 v1 bin(25) # 十进制转换为二进制 print(v1) # "0b11001"v2 oct(23) # 十进制转换为八进制 print(v2) # "0o27"v3 hex(28) # 十进…

ThinkPHP 8.x MVC 数据库用户增加功能demo实现

ThinkPHP MVC 数据库用户增加功能实现 在thinkphp 多应用的项目中&#xff0c; 采用MVC的架构实现 增加用户的功能。 在多应用下的controller下创建UserInfo.php ,创建一个和用户信息相关操作的控制器。 定义一个插入信息的方法,这里叫insertUserInfo 我们要执行一个用户插入…

Apache Doris (四十五): Doris数据更新与删除 - Sequence 列

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. 基本原理

EtherNet/IP转Modbus TCP协议网关的接口

远创智控的YC-EIPM-TCP网关产品&#xff0c;它有什么作用呢&#xff1f;一起来了解一下吧&#xff01; 远创智控YC-EIPM-TCP网关产品可以通过各种数据接口和工业领域的仪表、PLC、计量设备等产品连接&#xff0c;实时采集这些设备中的运行数据、状态数据等信息&#xff0c;并把…

数字秒表verilog电子秒表跑表,代码/视频

名称&#xff1a;数字秒表verilog电子秒表跑表 软件&#xff1a;Quartus 语言&#xff1a;Verilog 代码功能&#xff1a; 设计电子秒表&#xff0c;秒表时间精确到0.01秒&#xff0c;可通过按键控制秒表启动、暂停、复位。 代码需要在Mini_Star开发板验证。 开发板资料&…

【springboot异常处理】

这里写自定义目录标题 1&#xff0c;Java Servlet规范异常处理触发异常 2&#xff0c;tomcat 异常处理实现tomcat请求处理流程异常发生时核心处理类 3&#xff0c;springmvc 异常定制以及扩展请求处理流程使用ControllerAdvice & ExceptionHandler 配置全局异常处理器未配置…

全面的Docker快速入门教程

前言&#xff1a; 都2023年了&#xff0c;你还在为了安装一个开发或者部署环境、软件而花费半天的时间吗&#xff1f;你还在解决开发环境能够正常访问&#xff0c;而发布正式环境无法正常访问的问题吗&#xff1f;你还在为持续集成和持续交付&#xff08;CI / CD&#xff09;工…

【C++11】右值引用、移动构造、移动赋值、完美转发 的原理介绍

文章目录 一、概念1.1 左值1.2 左值引用1.3 什么是右值&#xff1f;1.4 什么是右值引用&#xff1f;对于参数左值还是右值的不同&#xff0c;是被重载支持的左值引用的使用场景 和 缺陷 二、移动语义2.1 移动拷贝构造2.2 移动赋值 三、右值引用 与 STL3.1 移动拷贝构造 和 赋值…

Web攻防03_MySQL注入_数据请求

文章目录 PHP-MYSQL-数据请求类型1、数字型(无符号干扰)2、字符型&#xff08;有符号干扰&#xff09;3、搜索型&#xff08;有多符号干扰&#xff09;4、框架型&#xff08;有各种符号干扰&#xff09; PHP-MYSQL-数据请求方法数据请求方法GET&#xff1a;POST&#xff1a;Coo…

运筹系列86:MIP问题的建模tips

1. Either-or constraint 添加辅助变量y。 比如 Either 3 x 1 2 x 2 ≤ 18 3x_12x_2 \le 18 3x1​2x2​≤18 or x 1 4 x 2 ≤ 16 x_14x_2 \le 16 x1​4x2​≤16 可以用 3 x 1 2 x 2 ≤ 18 M y 3x_12x_2 \le 18My 3x1​2x2​≤18My x 1 4 x 2 ≤ 16 M ( 1 − y ) x_1…