{ }codelper
substringsfinding textP0395

Shortening long file names in the middle

A file list screen can show at most w characters of a name, so a long name is shortened with "..." in the middle. A name of length at most w is printed unchanged. A name longer than w is shortened to exactly w characters as follows. If the name contains a '.', the extension runs from the last '.' to the end; if there is no '.', there is no extension. The number r of characters to keep from the part before the extension is w minus 3 minus the length of the extension. If r is at least 1, join in order the first ⌈r/2⌉ characters of that part, "...", the last ⌊r/2⌋ characters of that part, and the extension. If r is less than 1, no extension is set apart: treat the whole name as that part, let r be w − 3, and shorten it the same way. Print the names one per line in input order. On the last line print the number of names that were shortened.

Input
The first line has w and the number of names n (4 ≤ w ≤ 100, 1 ≤ n ≤ 1,000).
Each of the next n lines has one name.
A name has 1 to 200 printable ASCII characters; it may contain spaces in the middle but does not start or end with a space
Output
One line per name, then the number of shortened names on the last line
Example 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
Input (stdin)

When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).

$ Ready

Open this on a computer to write and run code. On mobile you can read the problem and the solutions.

In other languagesShow solutions by language · 8 languages

The slicing tools follow different conventions.
Python s[a:b], JS/TS slice(a, b) and Java substring(a, b) take a start and an end position, while C# Substring(a, length), C++ substr(a, length) and Craykon Substring(a, length) take a start and a length.
C prints a piece directly with printf("%.*s", length, start).
The last '.' is found with Python rfind, JS/TS/Java lastIndexOf, C# LastIndexOf, C strrchr and C++ rfind, and in Craykon you look at characters one by one from the end.
Trap one: cutting the back piece with a negative position goes wrong when 0 characters are kept.
Python stem[-0:] and JS slice(-0) are not the empty string but the whole string.
For interface-font.woff2 in the example r is 1, so the back piece has 0 characters, and cutting this way gives i...interface-font.woff2.
Cut with a position counted from the front, as in stem[len(stem) - tail:].
Trap two: when there is no '.', Python rfind, JS/TS/Java lastIndexOf and C# LastIndexOf return -1, C++ rfind returns npos and C strrchr returns NULL.
Cutting with Python name[-1:] without checking makes the last character the extension, so Makefile_for_release in the example becomes Mak...ease.
Trap three: the extension starts at the last '.'.
Cutting at the first '.' with Python find turns archive.tar.gz in the example into arch....gz instead of ar...ar.gz.
Trap four: if the extension length is not subtracted from r, the result exceeds w characters.
The second name of the example becomes the 14-character quar...nal.txt.
Trap five: leaving out the rule for r less than 1 turns a.tar.gz.backup in the example into ....backup instead of a.ta...kup.
Trap six: the front piece rounds up and the back piece rounds down.
Swapped, the second name of the example becomes q...al.txt instead of qu...l.txt.
Trap seven: a name of length exactly w stays unchanged.
Comparing with < turns report.txt in the example into re...t.txt.
Trap eight: do not mix start/end with start/length.
Passing a length as in Java substring(len - tail, tail) makes the start greater than the end on the second name of the example and stops with StringIndexOutOfBoundsException, and passing an end position as in Craykon Substring(len - tail, len) stops with an error saying the cut is outside the string.
Trap nine: names may contain spaces, so read whole lines.
In C++, calling getline right after std::cin >> w >> n reads only the line break left on the first line, so an empty line comes out first and the last name is lost.
Read the first line with getline too.

Pythonmain.py · 24 lines
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 lines
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 lines
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 lines
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 lines
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 lines
#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 lines
#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 lines
first = Console.ReadNumbers();w = first[0];n = first[1];count = 0;for (i = 0; i < n; i++){    name = Console.ReadLine();    if (name.Length <= w)    {        Console.WriteLine(name);        continue;    }    dot = name.Length - 1; // 끝에서부터 마지막 '.'을 찾는다. 없으면 -1    while (dot >= 0 && name[dot] != ".")    {        dot -= 1;    }    ext = "";    if (dot >= 0)    {        ext = name.Substring(dot);    }    r = w - 3 - ext.Length; // 확장자 앞부분에서 남길 글자 수    if (r < 1)    {        ext = "";        r = w - 3;    }    stem = name.Substring(0, name.Length - ext.Length);    tail = Mathf.Quot(r, 2); // 뒤 조각은 내림    head = r - tail; // 앞 조각은 올림    // 부분문자열(시작, 길이)와 부분문자열(시작)    Console.WriteLine(stem.Substring(0, head) + "..." + stem.Substring(stem.Length - tail) + ext);    count += 1;}Console.WriteLine(count);