package 섹션1_String;

import java.io.*;

public class 중복된_문자제거 {
    public static void main(String[] args) throws IOException {



        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb= new StringBuilder();
        boolean[] checkArr = new boolean[26];

        String str= br.readLine();

        char [] arr= str.toCharArray();

        for(char c:arr){
            if(!checkArr[c-'a']){ // 처음 나온 문자인 경우
                checkArr[c-'a']=true;
                sb.append(c);
            }
        }

        bw.write(sb.toString());
        bw.flush();
        bw.close();
        br.close();
    }
}

 

  • boolean 배열을 이용하여 가리키는 문자가 나온 문자인지 아닌지 확인
  • 한번도 나오지 않은 문자인 경우, 배열에 방문 처리를 하고 정답에 문자 추가하기 

 


 

 

✅ IndexOf 를 이용한 중복된 문자 찾기 

 

for(int i=0;i<str.length();i++){ 
    System.out.println(str.charAt(i)+" "+i+" "+ str.indexOf(str.charAt(i)));
}

 

ksekkset // 입력한 문자열 
k 0 0 
s 1 1
e 2 2
k 3 0
k 4 0
s 5 1
e 6 2
t 7 7

 

 

📍 두 값에 차이가 발생하는 이유

 

k 0 0 
s 1 1
e 2 2
t 7 7

 

위의 네가지 경우를 보면 배열의 인덱스 값과 IndexOf의 리턴값이 동일하다. 

 

 

k 3 0
k 4 0
s 5 1
e 6 2

 

하지만 위의 경우에는 두 개의 값이 다른데, 현재 배열의 위치에 있는 문자가 중복되어 존재하기 때문이다. 

IndexOf는 동일한 문자값이 문자열 내에서 여러개 존재할 경우, 가장 앞에 있는 인덱스의 값을 리턴한다. 

 

그래서 현재 가리키고 있는 문자가 앞에서 존재하고 있었기 때문에, 배열의 인덱스 보다 작은 값으로 IndexOf의 리턴값이 나오게 된 것이다. 

 

IndexOf는 동일한 문자가 여러 개 존재할 경우, 가장 작은 값을 리턴하니까 문자가 중복된 문자가 존재하는 경우 indexOf의 값이 배열의 인덱스 보다는 큰 값을 가질 수 없다. 
( 배열의 인덱스 값 ≥ IndexOf ) 

 

 

즉, 두 값이 일치하지 않는다는 것은 이전에 중복된 문자가 존재한다는 것이므로 두 값이 일치할때만 정답 문자열에 추가하자. 

 

 

package 섹션1_String;

import java.io.*;

public class 중복된_문자제거 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb= new StringBuilder();

        String str= br.readLine();

        for(int i=0;i<str.length();i++){
           if(str.indexOf(str.charAt(i))==i){
               sb.append(str.charAt(i));
           }
        }
        
        bw.write(sb.toString());
        bw.flush();
        bw.close();
        br.close();
    }
}

 

 


 

📌 중요한 코드 

 

for(int i=0;i<str.length();i++){
           if(str.indexOf(str.charAt(i))==i){
               sb.append(str.charAt(i));
           }
        }

 

✅Character.isAlphabetic 

package 섹션1_String;

import java.io.*;

public class 특정문자뒤집기 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

      char[] arr= br.readLine().toCharArray();

      int l=0;
      int r=arr.length-1;

      while(l<r){

          if(Character.isAlphabetic(arr[l]) && Character.isAlphabetic(arr[r])){
              char temp=arr[l];
              arr[l]=arr[r];
              arr[r]=temp;

              l++;
              r--;
          } else if (Character.isAlphabetic(arr[l])) {
              r--;
          } else if(Character.isAlphabetic(arr[r])){
              l++;
          }else{
              l++;
              r--;
          }

      }
        bw.write(String.valueOf(arr));
        bw.flush();
        br.close();
        bw.close();

    }
}

 

 

 

📌중요한 코드 

 

if~else if~else 구조에서 "어떤 조건으로 구분"을 해야 식을 더 간단하게 작성할 수 있을지 고민해보기

while(l<r){

    if(!Character.isAlphabetic(arr[l])){ // l-> 특수문자인 경우
       l++;
    } else if(!Character.isAlphabetic(arr[r])){ // r -> 특수문자인 경우
        r--;
    }else{
        char temp=arr[l];
        arr[l]=arr[r];
        arr[r]=temp;

        l++;
        r--;
    }

}

 

 

✅ StringBuilder의 reverse 함수 

package 섹션1_String;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

public class 단어뒤집기 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();


        int n = Integer.parseInt(br.readLine()); // 테스트 케이스 개수

        // 방법 1 StringBuilder 사용
        for(int i=0;i<n;i++){
            String str = new StringBuilder(br.readLine()).reverse().toString();
            sb.append(str).append("\n");
        }

        bw.write(sb.toString());
        bw.flush();
        br.close();
        bw.close();
    }
}

 

 

 

 

 

 

✅ 투 포인터를 사용하여 직접 문자열을 바꿔주기 

package 섹션1_String;

import java.io.*;

public class 단어뒤집기 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();

        int n = Integer.parseInt(br.readLine()); // 테스트 케이스 개수

       for(int i=0;i<n;i++){

           char [] arr= br.readLine().toCharArray();

           int l=0;
           int r = arr.length - 1;

           // 문자열 뒤집기
           while(l<r){
               char temp=arr[l];
               arr[l] = arr[r];
               arr[r]=temp;

               l++;
               r--;
           }
           sb.append(String.valueOf(arr)).append("\n");
       }

        bw.write(sb.toString());
        bw.flush();
        br.close();
        bw.close();
    }
}

 

 


 

 

 

✅ 아래와 같이 코드를 작성하게 될 경우, 어떤 출력결과가 나올지 생각해보자 

 

package 섹션1_String;

import java.io.*;

public class 단어뒤집기 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();
        StringBuilder answer= new StringBuilder();

        int n = Integer.parseInt(br.readLine()); // 테스트 케이스 개수

        for(int i=0;i<n;i++){
            // 문자열 뒤집기
            String str= br.readLine();
            sb.append(str).reverse().append("\n");
        }

        bw.write(sb.toString());
        bw.flush();
        br.close();
        bw.close();
    }
}

 

 

입력

3
good
Time
Big



 

정답

giB
doog
Time

 

 

 

 

 

위의 코드가 잘못된 이유는 sb에 값이 계속 누적되어서 각각의 문자열을 뒤집는 것이 아닌, 이전 문자열 까지 함께 누적하며 뒤집었기 때문이다. 

 

문자열을 입력받을 때 마다 새로운 StringBuilder를 사용해줘야한다. 

그렇지 않으면 StringBuilder에 값이 계속 누적되므로 다른 출력결과가 나오게 된다. 

 


📌 중요한 코드 

char [] arr= br.readLine().toCharArray();

int l=0;
int r = arr.length - 1;

// 문자열 뒤집기
while(l<r){
    char temp=arr[l];
    arr[l] = arr[r];
    arr[r]=temp;

    l++;
    r--;
}

 

문제 포인트

  • 문장 속 각 단어는 공백으로 구분, 단어 중에서 가장 긴 단어를 출력
  • 가장 긴 단어가 여러개인 경우, 문장 속에서 가장 앞쪽에 위치한 단어를 출력 

 

풀이 

  1. 입력받은 문자열을 공백 단위로 구분하기
  2. 구분된 단어들의 길이를 비교하여,  최댓값 업데이트 && 해당 문자열을 저장 

 

✅ split

package 섹션1_String;

import java.io.*;

public class 가장긴단어 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        
        int max=Integer.MIN_VALUE; // 가장 작은 문자열 길이 값으로 0으로 초기화 해도 상관없음
        String answer = "";

        // 입력받은 문자열 -> 공백을 기준으로 분리
        String[] s = br.readLine().split(" ");

        for (String x : s) {
            if(x.length()>max){
                max=x.length();
                answer=x;
            }
        }

        bw.write(answer);
        bw.flush();
        br.close();
        bw.close();




    }
}

 

 

 

✅ IndexOf, subString

 

package 섹션1_String;

import java.io.*;

public class 가장긴단어 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int max=Integer.MIN_VALUE; // 가장 작은 문자열 길이 값으로 0으로 초기화 해도 상관없음
        int pos=0; // 공백이 위치한 곳 
        String answer = "";

        // 입력받은 문자열 -> 공백을 기준으로 문자열을 분리
        String str= br.readLine();
        
        while((pos=str.indexOf(" "))!= -1){
            String tmp = str.substring(0, pos);
            int len=tmp.length();
            if(len > max){
                max=len;
                answer=tmp;
            }
            🌀str = str.substring(pos + 1); 
        }

        // 마지막 단어는 공백이 존재하지 않으므로 따로 check 
       🌀if(str.length()>max) answer=str; 

        bw.write(answer);
        bw.flush();
        br.close();
        bw.close();
        
    }
}

문제 포인트 

  • 대문자는 소문자로, 소문자는 대문자로 변환한다. 
  • 따라서 어떤 기준에 따라 대문자와 소문자로 구분할 수 있는지 생각해보면 된다. 

 

풀이 

아스키 코드표에 따르면 영어 대문자는 65~90, 소문자는 97~122 의 값을 가진다. 

따라서 대소문자의 차이값은 32이다. 

 

입력받은 문자열을 하나하나 확인해보면서, 대문자의 범위 or 소문자의 범위에 들어가는지 확인하여 대소문자를 구분한다. 

이후 대문자인 경우 소문자로, 소문자인 경우 대문자로 변환한다. 

 

 

 

package 섹션1_String;

import java.io.*;

public class 대소문자변환 {
    public static void main(String[] args) throws IOException {


        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder newStr = new StringBuilder();

        String str = br.readLine();


        int diff='a'-'A'; // 대문자와 소문자의 차이

        for(char c:str.toCharArray()){
            if(c>=65 && c<=90){ // 대문자인 경우 소문자로 변환 
                newStr.append((char) (c + diff)); 
            }else{ // 소문자인 경우 대문자로 변환 
                newStr.append((char) (c - diff));
            }
        }

        bw.write(newStr.toString());
        bw.flush();
        bw.close();
        br.close();

    }
}

 

 

 

✅ Character static 메서드 이용하기 

for(char c:str.toCharArray()){
    if(Character.isLowerCase(c)){
        newStr.append(Character.toUpperCase(c));
    } else {
        newStr.append(Character.toLowerCase(c));
    }
}

문제 포인트 

  • 문자열은 영어 알파벳으로만 구성되어있다. 
  • 영어 대소문자를 구분하지 않는다 

 

풀이 

대소문자를 구분하지 않는다면 첫번째 줄에서 입력받은 문자열과, 두번째 줄에서 입력받은 문자를 모두 대문자 or 소문자로 바꿔서 통일시켜줘야한다. 

 

 

 

package 섹션1_String;

import java.io.*;

public class 문자찾기 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String str= br.readLine().toUpperCase(); // 문자열
        char c= br.readLine().toUpperCase().charAt(0); // 찾고자 하는 문자

        int answer=0; // 찾고자하는 문자가 나온 횟수

        for(char strChar:str.toCharArray()){
            if(strChar==c){
                answer++;
            }
        }
        System.out.println(answer);
        br.close();
    }
}

 

 

 

for문을 이용한 문자열 비교 

for(int i=0;i<str.length();i++){ // 문자열의 길이만큼 반복
    if(c==str.charAt(i)){
        answer++;
    }
}

 

 

 


 

✅ == 연산 

 

 

 

String이나 Character 처럼 객체를 ==로 비교하게 되면, 값 뿐만 아니라 인스턴스의 주소도 비교하기 때문에 값이 같아도 다른 객체인 경우(인스턴스의 주소가 다른경우) false 값이 나오게 된다. 따라서 문자열에서 단순히 값만 비교하고 싶은 경우 eqauls( ) 를 사용해야한다. 

 

하지만 int,boolean,char 과 같은 원시타입의 경우 == 를 사용하게 된다면 단순히 값만 비교한다. 

📌 문제

 

찾는 비밀번호는 inhere 디렉터리 아래의 어떤 파일에 저장되어있다. 그리고 그 파일은 아래의 특징들을 전부가지고 있는 파일이다. 

  • human-readable
  • 1033 bytes in size
  • not executable

 

 

📌 풀이

 

이 문제는 특정한 조건을 가지고 있는 파일을 찾는 문제이다. 

 

 

find 명령어: find [경로] [표현식 1] [표현식 2] ...

 

먼저 find 명령어의 옵션들을 이용하여 조건에 맞는 파일들을 검색한다. 

  • '-type f'로 일반 파일을 검색한 후 grep 명령어를 이용해 파일의 확장자가 .text인 파일을 다시 찾는다. 
  • '-size 1033c'로 1033 바이트 크기의 파일을 찾는다. 
  • '! -executable'로 실행 가능하지 않은 파일을 찾는다.

 

$ find inhere -type f -size 1033c ! -executable -exec file {} + | grep text
find inhere -type f -size 1033c ! -executable -exec file {} \; | grep text

 

 

 -exec  {} \; 와 -exec  {} + 의 차이점 

 

위의 두 명령어의 차이점은 첫번째의 경우, 조건에 따라 검색된 여러개의 파일에 대하여 명령이 각각 한번씩 수행된다는 점이다. 

반대로 두번째 + 명령어를 사용하면, 찾아낸 여러개의 파일을 하나로 생각하여 명령이 한번만 수행된다. 

 

 

실행결과

bandit5@bandit:~/inhere$ find -type f -size 1033c ! -executable -exec file {} \; | grep text
./maybehere07/.file2: ASCII text, with very long lines (1000)

bandit5@bandit:~/inhere$ find -type f -size 1033c ! -executable -exec file {} + | grep text
./maybehere07/.file2: ASCII text, with very long lines (1000)

bandit5@bandit:~$ cat inhere/maybehere07/.file2

 

 

이제 파일의 내용을 읽고 정답을 제출해주면 된다. 

 

 

 

📌 정리

조건에 맞는 파일을 검색하고 싶다면 find 명령어를 사용하자

 

 

 

 

 

 

📌 참고자료 

 

find 명령어 옵션들 

https://m.blog.naver.com/tmk0429/222301447297


find 옵션으로 실행가능한 파일인지 아닌지 확인하는 방법 

https://stackoverflow.com/questions/70539901/how-can-i-find-all-non-executable-files-in-a-directory-in-linux

https://stackoverflow.com/questions/4458120/search-for-executable-files-using-find-command

'OverTheWire > bandit' 카테고리의 다른 글

Bandit Level 4 → Level 5  (0) 2025.01.10
Bandit Level 2 → Level 3  (0) 2025.01.10
Bandit Level 1 → Level 2  (0) 2025.01.10
Bandit Level 0 → Level 1  (0) 2025.01.10
Bandit Level 0  (0) 2025.01.10

📌 문제

다음 단계의 비밀번호는 inhere 디렉터리 안에서 사람이 읽을 수 있는 파일안에 담겨 있다고 한다. 

사람이 읽을 수 있는 파일이란 흔히 문자로 적혀있는 것을 말하는 것이다. 

 

이 문제에서는 파일의 타입을 검사하는 방법에 대해 정리해보자 

 

 

📌 풀이 

bandit4@bandit:~/inhere$ ls
-file00  -file02  -file04  -file06  -file08
-file01  -file03  -file05  -file07  -file09
bandit4@bandit:~/inhere$ cat ./-file00
�p��&�y�,�(jo�.at�:uf�^���@

 

inhere안에 여러가지 파일들이 보인다.  가장 앞에 있는 파일 하나를 읽어보니 우리는 읽을 수 없는 형태가 출력된다. 

아마 이 파일들을 하나씩 출력하다보면 우리가 찾는 비밀번호가 들어있는 파일을 찾을 수 있을 것이다. 

 

하지만 그렇게 찾기에는 너무 번거롭다. 

파일안의 내용을 우리가 읽을 수 있다는 것은 "문자나 숫자"로 이루어져있기 때문이다.  그렇다면 파일의 타입중에서 .txt 또는 .text 형태의 확장자를 가진 파일이 있는지 확인해보면 될 것 같다. 

 

file [파일 이름] 
bandit4@bandit:~/inhere$ file ./*
./-file00: data
./-file01: data
./-file02: data
./-file03: data
./-file04: data
./-file05: data
./-file06: data
./-file07: ASCII text
./-file08: data
./-file09: data

 

file 명령어를 이용하여 파일의 타입을 검사할 수 있다.  특정 파일 확장자만 찾고 싶다면 grep 명령어를 이용하자. 

 

 

문자열 검색 grep , 명령어를 연결해주는 | (파이프) 
$ file ./* | grep *.text

 

 

 

📌 정리 

파일의 타입을 검사하는 명령어는 file [파일이름] 이다. 

'OverTheWire > bandit' 카테고리의 다른 글

Bandit Level 5 → Level 6  (0) 2025.01.11
Bandit Level 2 → Level 3  (0) 2025.01.10
Bandit Level 1 → Level 2  (0) 2025.01.10
Bandit Level 0 → Level 1  (0) 2025.01.10
Bandit Level 0  (0) 2025.01.10

📌 문제

다음 단계의 비밀번호는 inhere이라는 디렉터리 아래의 숨겨진 파일안에 존재한다고 한다. 

 

이 문제를 통해서 "숨겨진 파일"이란 무엇인지, 그리고 그러한 파일을 읽는 방법 알아보자 

 

 

 

📌 풀이

 

리눅스에서는 ls 명령어로 보이지 않는 숨겨진 파일이 존재한다. 

이러한 숨겨진 파일은 . 으로 시작하는 파일들을 의미한다. 이런 파일들은 주로 환경세팅이나 설정과 관련된 파일이므로 사용자에게 보이지 않도록 하는 것이다.  일반적인 환경에서는 . 파일들은 잘 사용하지 않기 때문에 파일조회시 보여지지 않는다. 

필요할때 특정한 조회를 통해 파일을 찾도록 하는 것이 디렉터리를 더 깔끔하게 관리할 수 있다.  

 

 

hidden file 읽는 방법

 숨겨진 파일들을 보기 위해서는 ls -a, ls -A 명령어를 이용하면된다. 

-a : --all 
. 으로 시작하는 모든 파일들을 보여줌 

-A : --almost-all
. 과 .. 으로 시작하는 파일을 제외하고 숨겨진 파일들을 보여줌

 

 

 

📌 정리

숨겨진 파일은 . 으로 시작되는 파일을 말한다. 이런 파일을 읽고 싶다면 ls-a, -A 옵션을 이용한다. 

 

 

📌 참고자료

 

숨겨진 파일 읽는 방법

https://askubuntu.com/questions/232649/how-to-show-or-hide-a-hidden-file

숨겨진 파일이 존재하는 이유

https://unix.stackexchange.com/questions/147859/why-are-some-files-and-folders-hidden

. , ..  파일이란 무엇인지 

https://askubuntu.com/questions/94780/what-are-dot-files

 

📌 문제

홈 디렉터리 아래에 spaces in this filename 이라는 파일안에 bandit3의 비밀번호가 들어있다고 한다. 

해당 파일을 읽어서 비밀번호를 찾으면 된다. 

이 문제를 통해서 파일 이름에 공백이 있는 경우 파일을 읽는 방법에 대해 알아보자 

 

 

📌 풀이 

 

cat 명령어는 동시에 여러개의 파일을 읽을 수 있고, 각각의 파일 이름을 공백으로 구분한다.

cat [파일이름1] [파일이름2] .....

 

따라서 spaces in this filename 이라는 파일의 이름을 그대로 입력하게 되면, cat은 하나의 파일이 아닌 4개의 파일을 각각 읽으라는 의미로 인식하게 된다. 

bandit2@bandit:~$ ls
spaces in this filename
bandit2@bandit:~$ cat ./spaces in this filename
cat: ./spaces: No such file or directory
cat: in: No such file or directory
cat: this: No such file or directory
cat: filename: No such file or directory

 

그렇게 명령을 입력해보니 역시 space, in , this, filename이라는 파일들은 해당 디렉터리안에 존재하지 않는다고 나온다. 

 

공백이 있는 파일을 읽는 방법은 " " 나 ' ' 안에 파일 이름을 적어주면 된다.

또는 \ 를 사용하여 공백 문자를 무시하도록 해도 된다. 

bandit2@bandit:~$ cat "spaces in this filename"

bandit2@bandit:~$ cat 'spaces in this filename'

bandit2@bandit:~$ cat spaces\ in\ this\ filename

bandit2@bandit:~$ cat ./*

 

 

 

📌 정리

공백이 있는 파일의 이름은 "" , '' 안에 파일이름을 입력하거나 \를 이용하여 특수문자가 무시되도록한다. 

 

 

 

참고자료

https://appuals.com/how-to-handle-passing-filenames-with-spaces-in-bash/

'OverTheWire > bandit' 카테고리의 다른 글

Bandit Level 5 → Level 6  (0) 2025.01.11
Bandit Level 4 → Level 5  (0) 2025.01.10
Bandit Level 1 → Level 2  (0) 2025.01.10
Bandit Level 0 → Level 1  (0) 2025.01.10
Bandit Level 0  (0) 2025.01.10

+ Recent posts