{ }codelper
文字列の長さP0341

ハングルが混じったメッセージの残り文字数

メッセージ一通には m 文字まで書ける。
一行目に m、二行目に書いたメッセージが与えられる。
メッセージはハングル、英字、数字、記号(.
, !
?)、空白からなる。
文字数は、目に見える文字を一つずつ数えたものである。
ハングル一文字も、英字一文字も、空白一つも一文字である。
たとえば "안녕 hi" は 5 文字である。
m からメッセージの文字数を引いた値を出力せよ。
この値が正ならその分だけまだ書け、0 ならちょうど書ききっており、負ならその分だけあふれている。
あふれたときは -3 のようにマイナス記号を付けて書く。

入力
1 ≤ m ≤ 1,000(カンマなしの数字だけで与えられる)。
メッセージは 1 文字以上 200 文字以下で、空白で始まることも終わることもない。
ハングルは「가」から「힣」までの完成した文字だけが現れる(ㄱ、ㅏ のような字母や絵文字はない)。
入力は UTF-8 で与えられる
出力
m からメッセージの文字数を引いた値を一行に
例 1 · stdin
10
안녕 hi
stdout
5
例 2 · stdin
5
코델퍼 최고!!
stdout
-3
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

文字列の長さは Python len(s)、JS・TS s.length、Java s.length()、C# s.Length、C strlen(s)、C++ s.size()、Craykon s.長さ で聞きます。
ところが数える単位が違います。
Python は文字(コードポイント)を数えます。
JS・TS・Java・C#・Craykon は UTF-16 の単位を数えますが、ハングル一文字は一単位なので、この問題では文字数と同じです。
C と C++ はバイトを数えます。
落とし穴その一:UTF-8 ではハングル一文字が 3 バイトです。
そのため strlen や size() をそのまま使うと、例 1 の "안녕 hi" が 5 ではなく 9 と数えられて 1 が出ます。
C・C++ では文字の先頭バイトだけを数えれば済みます。
UTF-8 では一文字の二バイト目以降はすべて二進数の 10 で始まるので、(c & 0xC0) != 0x80 であるバイトの数が文字数です。
落とし穴その二:C の strlen と C++ の size() は符号なし整数(size_t)を返します。
C++ で文字数を size_t に数えて m - count を出力すると、負になれずにとても大きな数に回ってしまい、例 2 が -3 ではなく 18446744073709551613 になります。
int に数えるか、int に直してから引きます。
落とし穴その三:メッセージには空白があるので、行をまるごと読まなければなりません。
C++ の cin >> msg は最初の空白で止まり、例 1 が 8 になります(std::getline を使います)。
なお、この問題には出てこない絵文字 😀 一つを、Python は 1、JS・TS・Java・C#・Craykon は 2 と数え、C・C++ では 4 バイトです。

Pythonmain.py · 4 行
m = int(input())msg = input()# len은 바이트가 아니라 글자(코드 포인트)를 센다: len("안녕") == 2print(m - len(msg))
JavaScriptmain.js · 5 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const m = Number(lines[0]);const msg = lines[1];// length는 UTF-16 단위의 수다. 한글 한 글자는 한 단위라 글자 수와 같다console.log(m - msg.length);
TypeScriptmain.ts · 6 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const m: number = Number(lines[0]);const msg: string = lines[1];// length는 UTF-16 단위의 수다. 한글 한 글자는 한 단위라 글자 수와 같다console.log(m - msg.length);
JavaMain.java · 13 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.nio.charset.StandardCharsets; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));        int m = Integer.parseInt(br.readLine().trim());        String msg = br.readLine();        // length()는 UTF-16 단위의 수다. 한글 한 글자는 한 단위라 글자 수와 같다        System.out.println(m - msg.length());    }}
C#Main.cs · 12 行
using System; class Program{    static void Main()    {        int m = int.Parse(Console.ReadLine());        string msg = Console.ReadLine();        // Length는 UTF-16 단위의 수다. 한글 한 글자는 한 단위라 글자 수와 같다        Console.WriteLine(m - msg.Length);    }}
C++main.cpp · 18 行
#include <iostream>#include <limits>#include <string> int main() {    int m;    std::cin >> m;    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');    std::string msg;    std::getline(std::cin, msg);    // size()는 바이트 수를 준다. UTF-8에서 글자의 첫 바이트만 세면 글자 수다    // (둘째 바이트부터는 모두 10xxxxxx 꼴이다)    int count = 0;   // size_t가 아니라 int: m - count가 음수가 될 수 있다    for (unsigned char c : msg) {        if ((c & 0xC0) != 0x80) count++;    }    std::cout << m - count << "\n";}
Cmain.c · 17 行
#include <stdio.h>#include <stdlib.h> int main(void) {    char num[16], msg[700];   /* 한글 한 글자가 3바이트라 200글자면 600바이트까지 */    if (!fgets(num, sizeof num, stdin)) return 0;    if (!fgets(msg, sizeof msg, stdin)) return 0;    int m = atoi(num);    int count = 0;    /* strlen은 바이트 수를 준다. UTF-8에서 글자의 첫 바이트만 세면 글자 수다       (둘째 바이트부터는 모두 10xxxxxx 꼴이다) */    for (int i = 0; msg[i] != '\0' && msg[i] != '\n'; i++) {        if (((unsigned char)msg[i] & 0xC0) != 0x80) count++;    }    printf("%d\n", m - count);    return 0;}
Craykonmain.cray · 4 行
m = コンソール.行読み().数に();msg = コンソール.行読み();// 길이는 글자 수다: "안녕".길이 == 2コンソール.書く(m - msg.長さ);