{ }codelper
子串查找字符串P0395

把长文件名从中间缩短

文件列表界面最多只能显示名称的 w 个字符,所以长名称要把中间换成 "..." 来缩短。
长度不超过 w 的名称原样输出。
长度超过 w 的名称按下面的方法缩短成恰好 w 个字符。
名称中有 '.' 时,从最后一个 '.' 到末尾是扩展名;没有 '.' 时就没有扩展名。
从扩展名之前的部分中保留的字符数 r,等于 w 减去 3 再减去扩展名的长度。
r 至少为 1 时,依次连接前面部分的开头 ⌈r/2⌉ 个字符、"..."、前面部分的末尾 ⌊r/2⌋ 个字符和扩展名。
r 小于 1 时不单独留出扩展名,把整个名称看作前面部分,令 r 为 w − 3,用同样的方法缩短。
按输入中出现的顺序每行输出一个名称。
最后一行输出被缩短的名称的个数。

输入
第一行是 w 和名称的个数 n(4 ≤ w ≤ 100,1 ≤ n ≤ 1,000)。
接下来 n 行每行一个名称。
名称由 1~200 个可打印的 ASCII 字符组成,中间可能有空格,但不以空格开头或结尾
输出
每个名称一行,最后一行是被缩短的名称的个数
样例 1 · stdin
10 7
report.txt
quarterly report final.txt
a.tar.gz.backup
Makefile_for_release
interface-font.woff2
archive.tar.gz
notes
stdout
report.txt
qu...l.txt
a.ta...kup
Make...ase
i....woff2
ar...ar.gz
notes
5
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

各语言截取工具的约定不同。
Python s[a:b]、JS/TS slice(a, b)、Java substring(a, b) 接收开始位置和结束位置,C# Substring(a, 长度)、C++ substr(a, 长度)、Craykon 截取(a, 长度) 接收开始位置和长度。
C 用 printf("%.*s", 长度, 开始) 直接输出一段。
最后一个 '.' 用 Python rfind、JS/TS/Java lastIndexOf、C# LastIndexOf、C strrchr、C++ rfind 查找,Craykon 则从末尾一个一个地看字符来找。
陷阱一:用负数位置截取后面一段,在保留 0 个字符时会出错。
Python stem[-0:] 和 JS slice(-0) 不是空字符串,而是整个字符串。
示例的 interface-font.woff2 中 r 为 1,后面一段是 0 个字符,这样截取会得到 i...interface-font.woff2。
要像 stem[len(stem) - tail:] 这样用从前面数的位置截取。
陷阱二:没有 '.' 时,Python rfind、JS/TS/Java lastIndexOf、C# LastIndexOf 返回 -1,C++ rfind 返回 npos,C strrchr 返回 NULL。
不检查就用 Python name[-1:] 截取,最后一个字符会成为扩展名,示例的 Makefile_for_release 会变成 Mak...ease。
陷阱三:扩展名从最后一个 '.' 开始。
用 Python find 从第一个 '.' 截取,示例的 archive.tar.gz 会变成 arch....gz 而不是 ar...ar.gz。
陷阱四:r 不减去扩展名的长度,结果就会超过 w 个字符。
示例的第 2 个名称会变成 14 个字符的 quar...nal.txt。
陷阱五:漏掉 r 小于 1 时的规则,示例的 a.tar.gz.backup 会变成 ....backup 而不是 a.ta...kup。
陷阱六:前面一段向上取整,后面一段向下取整。
弄反的话,示例的第 2 个名称会变成 q...al.txt 而不是 qu...l.txt。
陷阱七:长度恰好为 w 的名称保持不变。
用 < 比较,示例的 report.txt 会变成 re...t.txt。
陷阱八:不能把“开始、结束”和“开始、长度”混用。
像 Java substring(len - tail, tail) 这样传入长度,在示例的第 2 个名称上开始位置大于结束位置,会因 StringIndexOutOfBoundsException 而停止;像 Craykon 截取(len - tail, len) 这样传入结束位置,会因截取位置在字符串之外的错误而停止。
陷阱九:名称中可能有空格,所以要读入整行。
在 C++ 中紧接着 std::cin >> w >> n 调用 getline,只会读到第一行剩下的换行符,结果先输出一个空行,并漏掉最后一个名称。
第一行也要用 getline 读取。

Pythonmain.py · 24 行
import sys lines = sys.stdin.read().split("\n")w, n = map(int, lines[0].split())out = []count = 0for name in lines[1:n + 1]:    if len(name) <= w:                 # 길이가 w 이하면 그대로        out.append(name)        continue    dot = name.rfind(".")              # 마지막 '.', 없으면 -1    ext = name[dot:] if dot >= 0 else ""    r = w - 3 - len(ext)               # 확장자 앞부분에서 남길 글자 수    if r < 1:                          # 자리가 없으면 확장자를 따로 두지 않는다        ext = ""        r = w - 3    stem = name[:len(name) - len(ext)]    head = (r + 1) // 2                # 앞 조각은 올림    tail = r // 2                      # 뒤 조각은 내림    # stem[-tail:]은 tail이 0일 때 전체가 되므로 앞에서 센 위치로 자른다    out.append(stem[:head] + "..." + stem[len(stem) - tail:] + ext)    count += 1out.append(str(count))print("\n".join(out))
JavaScriptmain.js · 26 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [w, n] = lines[0].split(" ").map(Number);const out = [];let count = 0;for (let i = 1; i <= n; i++) {  const name = lines[i];  if (name.length <= w) { // 길이가 w 이하면 그대로    out.push(name);    continue;  }  const dot = name.lastIndexOf("."); // 마지막 '.', 없으면 -1  let ext = dot >= 0 ? name.slice(dot) : "";  let r = w - 3 - ext.length; // 확장자 앞부분에서 남길 글자 수  if (r < 1) { // 자리가 없으면 확장자를 따로 두지 않는다    ext = "";    r = w - 3;  }  const stem = name.slice(0, name.length - ext.length);  const head = Math.ceil(r / 2); // 앞 조각은 올림  const tail = Math.floor(r / 2); // 뒤 조각은 내림  // slice(-tail)은 tail이 0일 때 전체가 되므로 앞에서 센 위치로 자른다  out.push(stem.slice(0, head) + "..." + stem.slice(stem.length - tail) + ext);  count++;}out.push(String(count));console.log(out.join("\n"));
TypeScriptmain.ts · 28 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const [w, n] = lines[0].split(" ").map(Number);const out: string[] = [];let count = 0;for (let i = 1; i <= n; i++) {  const name = lines[i];  if (name.length <= w) { // 길이가 w 이하면 그대로    out.push(name);    continue;  }  const dot = name.lastIndexOf("."); // 마지막 '.', 없으면 -1  let ext = dot >= 0 ? name.slice(dot) : "";  let r = w - 3 - ext.length; // 확장자 앞부분에서 남길 글자 수  if (r < 1) { // 자리가 없으면 확장자를 따로 두지 않는다    ext = "";    r = w - 3;  }  const stem = name.slice(0, name.length - ext.length);  const head = Math.ceil(r / 2); // 앞 조각은 올림  const tail = Math.floor(r / 2); // 뒤 조각은 내림  // slice(-tail)은 tail이 0일 때 전체가 되므로 앞에서 센 위치로 자른다  out.push(stem.slice(0, head) + "..." + stem.slice(stem.length - tail) + ext);  count++;}out.push(String(count));console.log(out.join("\n"));
JavaMain.java · 37 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int w = Integer.parseInt(st.nextToken());        int n = Integer.parseInt(st.nextToken());        StringBuilder out = new StringBuilder();        int count = 0;        for (int i = 0; i < n; i++) {            String name = in.readLine();            if (name.length() <= w) {              // 길이가 w 이하면 그대로                out.append(name).append('\n');                continue;            }            int dot = name.lastIndexOf('.');       // 마지막 '.', 없으면 -1            String ext = dot >= 0 ? name.substring(dot) : "";            int r = w - 3 - ext.length();          // 확장자 앞부분에서 남길 글자 수            if (r < 1) {                           // 자리가 없으면 확장자를 따로 두지 않는다                ext = "";                r = w - 3;            }            String stem = name.substring(0, name.length() - ext.length());            int head = (r + 1) / 2;                // 앞 조각은 올림            int tail = r / 2;                      // 뒤 조각은 내림            // substring(시작, 끝)과 substring(시작)            out.append(stem.substring(0, head)).append("...")               .append(stem.substring(stem.length() - tail)).append(ext).append('\n');            count++;        }        out.append(count).append('\n');        System.out.print(out);    }}
C#Main.cs · 40 行
using System;using System.Text; class Program{    static void Main()    {        string[] first = Console.ReadLine()!.Split(' ');        int w = int.Parse(first[0]);        int n = int.Parse(first[1]);        var sb = new StringBuilder();        int count = 0;        for (int i = 0; i < n; i++)        {            string name = Console.ReadLine()!;            if (name.Length <= w)                  // 길이가 w 이하면 그대로            {                sb.Append(name).Append('\n');                continue;            }            int dot = name.LastIndexOf('.');       // 마지막 '.', 없으면 -1            string ext = dot >= 0 ? name.Substring(dot) : "";            int r = w - 3 - ext.Length;            // 확장자 앞부분에서 남길 글자 수            if (r < 1)                             // 자리가 없으면 확장자를 따로 두지 않는다            {                ext = "";                r = w - 3;            }            string stem = name.Substring(0, name.Length - ext.Length);            int head = (r + 1) / 2;                // 앞 조각은 올림            int tail = r / 2;                      // 뒤 조각은 내림            // Substring(시작, 길이)와 Substring(시작)            sb.Append(stem.Substring(0, head)).Append("...")              .Append(stem.Substring(stem.Length - tail)).Append(ext).Append('\n');            count++;        }        sb.Append(count).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 38 行
#include <iostream>#include <sstream>#include <string> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    std::string line;    std::getline(std::cin, line);              // 첫 줄도 getline으로 읽어 줄 바꿈을 남기지 않는다    std::istringstream first(line);    int w, n;    first >> w >> n;    int count = 0;    for (int i = 0; i < n; i++) {        std::string name;        std::getline(std::cin, name);        int len = (int)name.size();        if (len <= w) {                        // 길이가 w 이하면 그대로            std::cout << name << '\n';            continue;        }        std::size_t dot = name.rfind('.');     // 마지막 '.', 없으면 npos        std::string ext = dot != std::string::npos ? name.substr(dot) : "";        int r = w - 3 - (int)ext.size();       // 확장자 앞부분에서 남길 글자 수        if (r < 1) {                           // 자리가 없으면 확장자를 따로 두지 않는다            ext = "";            r = w - 3;        }        std::string stem = name.substr(0, name.size() - ext.size());        int head = (r + 1) / 2;                // 앞 조각은 올림        int tail = r / 2;                      // 뒤 조각은 내림        // substr(시작, 길이)와 substr(시작)        std::cout << stem.substr(0, head) << "..." << stem.substr(stem.size() - tail) << ext << '\n';        count++;    }    std::cout << count << '\n';    return 0;}
Cmain.c · 32 行
#include <stdio.h>#include <string.h> int main(void) {    static char name[256];    int w, n, count = 0;    if (!fgets(name, sizeof name, stdin) || sscanf(name, "%d %d", &w, &n) != 2) return 0;    for (int i = 0; i < n; i++) {        if (!fgets(name, sizeof name, stdin)) break;        int len = (int)strcspn(name, "\r\n");        name[len] = '\0';        if (len <= w) {                          /* 길이가 w 이하면 그대로 */            puts(name);            continue;        }        const char *dot = strrchr(name, '.');    /* 마지막 '.', 없으면 NULL */        int ext = dot ? (int)(name + len - dot) : 0;        int r = w - 3 - ext;                     /* 확장자 앞부분에서 남길 글자 수 */        if (r < 1) {                             /* 자리가 없으면 확장자를 따로 두지 않는다 */            ext = 0;            r = w - 3;        }        int stem = len - ext;        int head = (r + 1) / 2;                  /* 앞 조각은 올림 */        int tail = r / 2;                        /* 뒤 조각은 내림 */        /* %.*s는 길이만큼만 찍는다. 뒤 조각과 확장자는 이어져 있으니 끝까지 찍는다 */        printf("%.*s...%s\n", head, name, name + stem - tail);        count++;    }    printf("%d\n", count);    return 0;}
Craykonmain.cray · 36 行
first = 控制台.读数字();w = first[0];n = first[1];count = 0;循环 (i = 0; i < n; i++){    name = 控制台.读行();    如果 (name.长度 <= w)    {        控制台.输出(name);        继续;    }    dot = name.长度 - 1; // 끝에서부터 마지막 '.'을 찾는다. 없으면 -1    当 (dot >= 0 && name[dot] != ".")    {        dot -= 1;    }    ext = "";    如果 (dot >= 0)    {        ext = name.截取(dot);    }    r = w - 3 - ext.长度; // 확장자 앞부분에서 남길 글자 수    如果 (r < 1)    {        ext = "";        r = w - 3;    }    stem = name.截取(0, name.长度 - ext.长度);    tail = 数学.商(r, 2); // 뒤 조각은 내림    head = r - tail; // 앞 조각은 올림    // 부분문자열(시작, 길이)와 부분문자열(시작)    控制台.输出(stem.截取(0, head) + "..." + stem.截取(stem.长度 - tail) + ext);    count += 1;}控制台.输出(count);