You want to finish one link by attaching the filters chosen on a search screen to a base link.
The first line gives the base link base and the number of filter lines n.
A filter line is either just a name, or a name and a value separated by one space, like 'name value'.
The part before the first space of the line is the name, and everything after it is the value.
The value may contain more spaces, even several in a row. No space inside the value is dropped.
A line with only a name is a filter without a value, so it is discarded.
For each remaining filter, make a piece by joining the name, '=', and the converted value in this order.
The value is converted character by character with this rule: uppercase and lowercase English letters, digits, and the four characters - _ . ~ stay as they are, and any other character becomes '%' followed by its character code written as two hexadecimal digits.
The hexadecimal digits A to F are written in uppercase. For example, a space (code 32) becomes %20 and '/' (code 47) becomes %2F.
If at least one filter remains, append '?' to base and then the pieces in the given order, joined with '&'.
If no filter remains, print base alone, unchanged.
Input
The first line gives base and n separated by one space (1 ≤ n ≤ 100). base is a string of 1 to 50 characters without spaces and is used as is, without conversion. Each of the next n lines gives one filter. A name is 1 to 10 lowercase English letters. A value is 1 to 100 characters, uses only characters with codes 32 to 126 (space, letters, digits, symbols), and does not start or end with a space.
Output
One line with the finished link
Example 1 · stdin
https://shop.example/search 4
q red shoes & socks
page
sort price_asc
tag 50%/off!
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
This problem combines two things: collecting string pieces and putting a separator only between them, and turning a character into its code written in hexadecimal. Joining: Python "&".join(pieces), JS/TS pieces.join("&"), C# string.Join("&", pieces) and Craykon pieces.Join("&") put the separator only in between. When appending one by one with Java StringBuilder or C++ std::string +=, choose ? before the first piece and & after that yourself. C does not concatenate at all and prints each piece right away. Two hex digits: C printf("%%%02X", c), Java String.format("%%%02X", (int) c), C# ((int)c).ToString("X2"), Python f"%{ord(c):02X}", JS c.charCodeAt(0).toString(16).toUpperCase(); C++ and Craykon pick the code / 16-th and code % 16-th characters of "0123456789ABCDEF" (Craykon uses Str.Code(c) and Mathf.Quot(code, 16)). There are standard functions for this conversion, but their rules differ slightly. C# Uri.EscapeDataString(value) and Python urllib.parse.quote(value, safe="") match this problem's rule. Eight traps: (1) JS encodeURIComponent leaves the five characters ! ' ( ) * as they are. (2) Python quote defaults to safe="/", so / is left as it is. (3) Java URLEncoder.encode follows the HTML form rule: it turns a space into +, leaves * alone and turns ~ into %7E. (4) C's %02x is lowercase. Uppercase is %02X. (5) When cutting a line into name and value, Java split(" ", 2), C# Split(' ', 2) and Python split(" ", 1) keep the rest in the last piece, but JS split(" ", 2) keeps only the first two pieces and throws the rest away. In JS cut with indexOf(" ") and slice. (6) Splitting into words with Python split() and joining them again with one space shrinks several spaces in a row into one. (7) Putting & before every pair gives ?&q=... with an & before the first pair, and appending ? when no filter remains is also wrong. (8) A line without a value must not be attached as name=.
Pythonmain.py · 13 lines
from urllib.parse import quotebase, n = input().split()pairs = []for _ in range(int(n)): # 처음 나오는 빈칸에서만 자른다. 빈칸이 없으면 value는 빈 문자열이다 name, _, value = input().partition(" ") if value == "": continue # quote는 기본으로 "/"를 그대로 둔다. safe=""로 그것도 바꾸게 한다 pairs.append(name + "=" + quote(value, safe=""))# 조각을 목록에 모았다가 join으로 한 번에 잇는다: 사이에만 "&"가 들어간다print(base + ("?" + "&".join(pairs) if pairs else ""))
JavaScriptmain.js · 19 lines
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [base, n] = lines[0].split(" ");// encodeURIComponent는 ! ' ( ) * 다섯 글자를 그대로 둔다. 그 다섯 글자는 따로 바꾼다function encode(value) { return encodeURIComponent(value).replace( /[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase() );}const pairs = [];for (let i = 1; i <= Number(n); i++) { const line = lines[i]; const at = line.indexOf(" "); // split(" ", 2)는 셋째 조각부터 버리므로 쓰지 않는다 if (at === -1) continue; pairs.push(line.slice(0, at) + "=" + encode(line.slice(at + 1)));}console.log(base + (pairs.length > 0 ? "?" + pairs.join("&") : ""));
TypeScriptmain.ts · 20 lines
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const [base, n] = lines[0].split(" ");// encodeURIComponent는 ! ' ( ) * 다섯 글자를 그대로 둔다. 그 다섯 글자는 따로 바꾼다function encode(value: string): string { return encodeURIComponent(value).replace( /[!'()*]/g, (c: string) => "%" + c.charCodeAt(0).toString(16).toUpperCase() );}const pairs: string[] = [];for (let i = 1; i <= Number(n); i++) { const line = lines[i]; const at = line.indexOf(" "); // split(" ", 2)는 셋째 조각부터 버리므로 쓰지 않는다 if (at === -1) continue; pairs.push(line.slice(0, at) + "=" + encode(line.slice(at + 1)));}console.log(base + (pairs.length > 0 ? "?" + pairs.join("&") : ""));
JavaMain.java · 36 lines
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;public class Main { // URLEncoder.encode는 빈칸을 +로 바꾸고 ~를 바꿔 버려서 규칙과 다르다. 그래서 직접 만든다 static void appendEncoded(StringBuilder out, String value) { for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); boolean keep = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~'; if (keep) { out.append(c); } else { out.append(String.format("%%%02X", (int) c)); // %% 는 % 한 글자, %02X는 대문자 16진수 두 자리 } } } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String[] first = in.readLine().split(" "); int n = Integer.parseInt(first[1]); StringBuilder out = new StringBuilder(first[0]); boolean any = false; for (int i = 0; i < n; i++) { String[] parts = in.readLine().split(" ", 2); // 많아야 두 조각: 둘째 조각에 나머지가 모두 남는다 if (parts.length < 2) continue; out.append(any ? '&' : '?'); // 첫 쌍 앞에는 ?, 그다음부터는 & any = true; out.append(parts[0]).append('='); appendEncoded(out, parts[1]); } System.out.println(out); }}
C#Main.cs · 20 lines
using System;using System.Collections.Generic;class Program{ static void Main() { string[] first = Console.ReadLine().Split(' '); int n = int.Parse(first[1]); var pairs = new List<string>(); for (int i = 0; i < n; i++) { string[] parts = Console.ReadLine().Split(' ', 2); // 많아야 두 조각: 둘째 조각에 나머지가 모두 남는다 if (parts.Length < 2) continue; // Uri.EscapeDataString은 영문자·숫자와 - _ . ~ 만 남기고 %XX(대문자)로 바꾼다 pairs.Add(parts[0] + "=" + Uri.EscapeDataString(parts[1])); } Console.WriteLine(first[0] + (pairs.Count > 0 ? "?" + string.Join("&", pairs) : "")); }}
C++main.cpp · 36 lines
#include <iostream>#include <string>int main() { std::string base; int n; std::cin >> base >> n; std::string line; std::getline(std::cin, line); // 첫 줄의 나머지(줄바꿈)를 넘긴다 const std::string hex = "0123456789ABCDEF"; std::string out = base; // std::string은 += 로 뒤에 이어 붙여도 빠르다 bool any = false; for (int i = 0; i < n; i++) { std::getline(std::cin, line); auto at = line.find(' '); // 처음 나오는 빈칸 if (at == std::string::npos) continue; out += any ? '&' : '?'; any = true; out += line.substr(0, at); out += '='; for (std::size_t k = at + 1; k < line.size(); k++) { char c = line[k]; bool keep = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~'; if (keep) { out += c; } else { out += '%'; out += hex[c / 16]; // 16진수 윗자리 out += hex[c % 16]; // 16진수 아랫자리 } } } std::cout << out << '\n';}
Cmain.c · 30 lines
#include <ctype.h>#include <stdio.h>#include <string.h>int main(void) { char base[64]; int n; scanf("%63s %d", base, &n); getchar(); /* 첫 줄 끝의 줄바꿈을 넘긴다 */ fputs(base, stdout); int any = 0; char line[160]; for (int i = 0; i < n; i++) { if (!fgets(line, sizeof line, stdin)) break; line[strcspn(line, "\n")] = '\0'; char *space = strchr(line, ' '); /* 처음 나오는 빈칸 */ if (space == NULL) continue; *space = '\0'; /* 그 자리에서 이름과 값을 끊는다 */ /* C에서는 문자열을 이어 붙이는 대신 조각을 바로바로 출력한다 */ printf("%c%s=", any ? '&' : '?', line); any = 1; for (const char *p = space + 1; *p != '\0'; p++) { unsigned char c = (unsigned char)*p; if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') putchar(c); else printf("%%%02X", (unsigned)c); /* %X는 대문자, %x는 소문자 16진수 */ } } putchar('\n'); return 0;}
Craykonmain.cray · 45 lines
십육진수 = "0123456789ABCDEF";// 값의 글자를 하나씩 보고, 그대로 둘 글자가 아니면 %와 16진수 두 자리로 바꾼다바꾸기값(값){ 결과 = ""; foreach (c in 값) { 그대로 = c >= "A" && c <= "Z" || c >= "a" && c <= "z" || c >= "0" && c <= "9" || c == "-" || c == "_" || c == "." || c == "~"; if (그대로) { 결과 = 결과 + c; } else { 코드 = Str.Code(c); 결과 = 결과 + "%" + 십육진수[Mathf.Quot(코드, 16)] + 십육진수[코드 % 16]; } } return 결과;}첫줄 = Console.ReadLine().Split(" ");n = 첫줄[1].ToNumber();쌍들 = [];for (i = 0; i < n; i++){ 줄 = Console.ReadLine(); 자리 = 줄.IndexOf(" "); // 처음 나오는 빈칸, 없으면 -1 if (자리 == -1) { continue; } 이름 = 줄.Substring(0, 자리); 값 = 줄.Substring(자리 + 1, 줄.Length - 자리 - 1); 쌍들.Add(이름 + "=" + 바꾸기값(값));}if (쌍들.Count > 0){ Console.WriteLine(첫줄[0] + "?" + 쌍들.Join("&"));}else{ Console.WriteLine(첫줄[0]);}