이메일 전송시에는 일반 문자 그대로 전송하지 않고
일반적으로 8bit 이나 base64 방식으로 인코딩해서 전송을 합니다.
8bit 문자열은 imap_8bit() 함수를 이용해서 8bit 문자열로 인코딩할 수 있고 quoted_printable_decode() 함수를 이용해서 디코딩할 수 있습니다.
base64 는 base64_encode() 함수로 인코딩하고 base64_decode() 함수로 디코드합니다.
그런데 이메일 인코딩시에는 한가지 규칙이 있습니다.
질문에 문자들을 예를 들면...
=?EUC-KR?B?xde9usau?=
위 내용에서 앞, 뒤에 =?, ?= 는 시작과 끝을 나타내는 것이며 그 사이에 문자열을 ? 로 구분하였을 때...
EUC-KR 은 문자 형식을 말해주며 B 는 Base64 를 뜻 합니다. 만약 Q 라면 Quoted 문자열이 됩니다.
그리고 나머지가 실제 인코딩된 문자열이 되는 것이죠.
그래서 질문에 있는 문자열은 base64 로 인코딩된 문자열이고 실제 인코딩된 문자열인 'xde9usau' 부분을 base64_decode() 함수를 이용해서 디코드하면 '테스트' 라는 문자열이 됩니다.
성군.story
- 이메일 전송시 base64 인코딩 2007.08.14
- The Base16, Base32, and Base64 Data Encodings 2007.08.14
- Base64 encode/decode 함수 2007.08.14
- realloc 2007.08.14 4
- static 함수 2007.08.14
- 로봇을 만들어 봅시다. 2007.08.11
- 이클립스에서 만든 로봇을 불러와 봅시다 2007.08.11
- 이클립스 로보코드 환경 설정 2007.08.11
- guda의 미투데이 - 2007년 8월 10일 2007.08.11
- Robocode 마스터의 비밀: Factored wall avoidance (한글) 2007.08.11
이메일 전송시 base64 인코딩
The Base16, Base32, and Base64 Data Encodings
Request for Comments: 3548 July 2003
Category: Informational
The Base16, Base32, and Base64 Data Encodings
Status of this Memo
This memo provides information for the Internet community. It does
not specify an Internet standard of any kind. Distribution of this
memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (2003). All Rights Reserved.
Abstract
This document describes the commonly used base 64, base 32, and base
16 encoding schemes. It also discusses the use of line-feeds in
encoded data, use of padding in encoded data, use of non-alphabet
characters in encoded data, and use of different encoding alphabets.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
2. Implementation discrepancies . . . . . . . . . . . . . . . . . 2
2.1. Line feeds in encoded data . . . . . . . . . . . . . . . 2
2.2. Padding of encoded data . . . . . . . . . . . . . . . . 3
2.3. Interpretation of non-alphabet characters in encoded
data . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.4. Choosing the alphabet . . . . . . . . . . . . . . . . . 3
3. Base 64 Encoding . . . . . . . . . . . . . . . . . . . . . . . 4
4. Base 64 Encoding with URL and Filename Safe Alphabet . . . . . 6
5. Base 32 Encoding . . . . . . . . . . . . . . . . . . . . . . . 6
6. Base 16 Encoding . . . . . . . . . . . . . . . . . . . . . . . 8
7. Illustrations and examples . . . . . . . . . . . . . . . . . . 9
8. Security Considerations . . . . . . . . . . . . . . . . . . . 10
9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 11
9.1. Normative References . . . . . . . . . . . . . . . . . . 11
9.2. Informative References . . . . . . . . . . . . . . . . . 11
10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 11
11. Editor's Address . . . . . . . . . . . . . . . . . . . . . . . 12
12. Full Copyright Statement . . . . . . . . . . . . . . . . . . . 13
Josefsson Informational [Page 1]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
1. Introduction
Base encoding of data is used in many situations to store or transfer
data in environments that, perhaps for legacy reasons, are restricted
to only US-ASCII [9] data. Base encoding can also be used in new
applications that do not have legacy restrictions, simply because it
makes it possible to manipulate objects with text editors.
In the past, different applications have had different requirements
and thus sometimes implemented base encodings in slightly different
ways. Today, protocol specifications sometimes use base encodings in
general, and "base64" in particular, without a precise description or
reference. MIME [3] is often used as a reference for base64 without
considering the consequences for line-wrapping or non-alphabet
characters. The purpose of this specification is to establish common
alphabet and encoding considerations. This will hopefully reduce
ambiguity in other documents, leading to better interoperability.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in RFC 2119 [1].
2. Implementation discrepancies
Here we discuss the discrepancies between base encoding
implementations in the past, and where appropriate, mandate a
specific recommended behavior for the future.
2.1. Line feeds in encoded data
MIME [3] is often used as a reference for base 64 encoding. However,
MIME does not define "base 64" per se, but rather a "base 64
Content-Transfer-Encoding" for use within MIME. As such, MIME
enforces a limit on line length of base 64 encoded data to 76
characters. MIME inherits the encoding from PEM [2] stating it is
"virtually identical", however PEM uses a line length of 64
characters. The MIME and PEM limits are both due to limits within
SMTP.
Implementations MUST NOT not add line feeds to base encoded data
unless the specification referring to this document explicitly
directs base encoders to add line feeds after a specific number of
characters.
Josefsson Informational [Page 2]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
2.2. Padding of encoded data
In some circumstances, the use of padding ("=") in base encoded data
is not required nor used. In the general case, when assumptions on
size of transported data cannot be made, padding is required to yield
correct decoded data.
Implementations MUST include appropriate pad characters at the end of
encoded data unless the specification referring to this document
explicitly states otherwise.
2.3. Interpretation of non-alphabet characters in encoded data
Base encodings use a specific, reduced, alphabet to encode binary
data. Non alphabet characters could exist within base encoded data,
caused by data corruption or by design. Non alphabet characters may
be exploited as a "covert channel", where non-protocol data can be
sent for nefarious purposes. Non alphabet characters might also be
sent in order to exploit implementation errors leading to, e.g.,
buffer overflow attacks.
Implementations MUST reject the encoding if it contains characters
outside the base alphabet when interpreting base encoded data, unless
the specification referring to this document explicitly states
otherwise. Such specifications may, as MIME does, instead state that
characters outside the base encoding alphabet should simply be
ignored when interpreting data ("be liberal in what you accept").
Note that this means that any CRLF constitute "non alphabet
characters" and are ignored. Furthermore, such specifications may
consider the pad character, "=", as not part of the base alphabet
until the end of the string. If more than the allowed number of pad
characters are found at the end of the string, e.g., a base 64 string
terminated with "===", the excess pad characters could be ignored.
2.4. Choosing the alphabet
Different applications have different requirements on the characters
in the alphabet. Here are a few requirements that determine which
alphabet should be used:
o Handled by humans. Characters "0", "O" are easily interchanged,
as well "1", "l" and "I". In the base32 alphabet below, where 0
(zero) and 1 (one) is not present, a decoder may interpret 0 as
O, and 1 as I or L depending on case. (However, by default it
should not, see previous section.)
Josefsson Informational [Page 3]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
o Encoded into structures that place other requirements. For base
16 and base 32, this determines the use of upper- or lowercase
alphabets. For base 64, the non-alphanumeric characters (in
particular "/") may be problematic in file names and URLs.
o Used as identifiers. Certain characters, notably "+" and "/" in
the base 64 alphabet, are treated as word-breaks by legacy text
search/index tools.
There is no universally accepted alphabet that fulfills all the
requirements. In this document, we document and name some currently
used alphabets.
3. Base 64 Encoding
The following description of base 64 is due to [2], [3], [4] and [5].
The Base 64 encoding is designed to represent arbitrary sequences of
octets in a form that requires case sensitivity but need not be
humanly readable.
A 65-character subset of US-ASCII is used, enabling 6 bits to be
represented per printable character. (The extra 65th character, "=",
is used to signify a special processing function.)
The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters. Proceeding from left to right, a
24-bit input group is formed by concatenating 3 8-bit input groups.
These 24 bits are then treated as 4 concatenated 6-bit groups, each
of which is translated into a single digit in the base 64 alphabet.
Each 6-bit group is used as an index into an array of 64 printable
characters. The character referenced by the index is placed in the
output string.
Josefsson Informational [Page 4]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
Table 1: The Base 64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
Special processing is performed if fewer than 24 bits are available
at the end of the data being encoded. A full encoding quantum is
always completed at the end of a quantity. When fewer than 24 input
bits are available in an input group, zero bits are added (on the
right) to form an integral number of 6-bit groups. Padding at the
end of the data is performed using the '=' character. Since all base
64 input is an integral number of octets, only the following cases
can arise:
(1) the final quantum of encoding input is an integral multiple of 24
bits; here, the final unit of encoded output will be an integral
multiple of 4 characters with no "=" padding,
(2) the final quantum of encoding input is exactly 8 bits; here, the
final unit of encoded output will be two characters followed by two
"=" padding characters, or
(3) the final quantum of encoding input is exactly 16 bits; here, the
final unit of encoded output will be three characters followed by one
"=" padding character.
Josefsson Informational [Page 5]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
4. Base 64 Encoding with URL and Filename Safe Alphabet
The Base 64 encoding with an URL and filename safe alphabet has been
used in [8].
An alternative alphabet has been suggested that used "~" as the 63rd
character. Since the "~" character has special meaning in some file
system environments, the encoding described in this section is
recommended instead.
This encoding should not be regarded as the same as the "base64"
encoding, and should not be referred to as only "base64". Unless
made clear, "base64" refer to the base 64 in the previous section.
This encoding is technically identical to the previous one, except
for the 62:nd and 63:rd alphabet character, as indicated in table 2.
Table 2: The "URL and Filename safe" Base 64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 - (minus)
12 M 29 d 46 u 63 _ (understrike)
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
5. Base 32 Encoding
The following description of base 32 is due to [7] (with
corrections).
The Base 32 encoding is designed to represent arbitrary sequences of
octets in a form that needs to be case insensitive but need not be
humanly readable.
Josefsson Informational [Page 6]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
A 33-character subset of US-ASCII is used, enabling 5 bits to be
represented per printable character. (The extra 33rd character, "=",
is used to signify a special processing function.)
The encoding process represents 40-bit groups of input bits as output
strings of 8 encoded characters. Proceeding from left to right, a
40-bit input group is formed by concatenating 5 8bit input groups.
These 40 bits are then treated as 8 concatenated 5-bit groups, each
of which is translated into a single digit in the base 32 alphabet.
When encoding a bit stream via the base 32 encoding, the bit stream
must be presumed to be ordered with the most-significant-bit first.
That is, the first bit in the stream will be the high-order bit in
the first 8bit byte, and the eighth bit will be the low-order bit in
the first 8bit byte, and so on.
Each 5-bit group is used as an index into an array of 32 printable
characters. The character referenced by the index is placed in the
output string. These characters, identified in Table 2, below, are
selected from US-ASCII digits and uppercase letters.
Table 3: The Base 32 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 9 J 18 S 27 3
1 B 10 K 19 T 28 4
2 C 11 L 20 U 29 5
3 D 12 M 21 V 30 6
4 E 13 N 22 W 31 7
5 F 14 O 23 X
6 G 15 P 24 Y (pad) =
7 H 16 Q 25 Z
8 I 17 R 26 2
Special processing is performed if fewer than 40 bits are available
at the end of the data being encoded. A full encoding quantum is
always completed at the end of a body. When fewer than 40 input bits
are available in an input group, zero bits are added (on the right)
to form an integral number of 5-bit groups. Padding at the end of
the data is performed using the "=" character. Since all base 32
input is an integral number of octets, only the following cases can
arise:
(1) the final quantum of encoding input is an integral multiple of 40
bits; here, the final unit of encoded output will be an integral
multiple of 8 characters with no "=" padding,
Josefsson Informational [Page 7]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
(2) the final quantum of encoding input is exactly 8 bits; here, the
final unit of encoded output will be two characters followed by six
"=" padding characters,
(3) the final quantum of encoding input is exactly 16 bits; here, the
final unit of encoded output will be four characters followed by four
"=" padding characters,
(4) the final quantum of encoding input is exactly 24 bits; here, the
final unit of encoded output will be five characters followed by
three "=" padding characters, or
(5) the final quantum of encoding input is exactly 32 bits; here, the
final unit of encoded output will be seven characters followed by one
"=" padding character.
6. Base 16 Encoding
The following description is original but analogous to previous
descriptions. Essentially, Base 16 encoding is the standard standard
case insensitive hex encoding, and may be referred to as "base16" or
"hex".
A 16-character subset of US-ASCII is used, enabling 4 bits to be
represented per printable character.
The encoding process represents 8-bit groups (octets) of input bits
as output strings of 2 encoded characters. Proceeding from left to
right, a 8-bit input is taken from the input data. These 8 bits are
then treated as 2 concatenated 4-bit groups, each of which is
translated into a single digit in the base 16 alphabet.
Each 4-bit group is used as an index into an array of 16 printable
characters. The character referenced by the index is placed in the
output string.
Table 5: The Base 16 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 0 4 4 8 8 12 C
1 1 5 5 9 9 13 D
2 2 6 6 10 A 14 E
3 3 7 7 11 B 15 F
Unlike base 32 and base 64, no special padding is necessary since a
full code word is always available.
Josefsson Informational [Page 8]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
7. Illustrations and examples
To translate between binary and a base encoding, the input is stored
in a structure and the output is extracted. The case for base 64 is
displayed in the following figure, borrowed from [4].
+--first octet--+-second octet--+--third octet--+
|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
+-----------+---+-------+-------+---+-----------+
|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
+--1.index--+--2.index--+--3.index--+--4.index--+
The case for base 32 is shown in the following figure, borrowed from
[6]. Each successive character in a base-32 value represents 5
successive bits of the underlying octet sequence. Thus, each group
of 8 characters represents a sequence of 5 octets (40 bits).
1 2 3
01234567 89012345 67890123 45678901 23456789
+--------+--------+--------+--------+--------+
|< 1 >< 2| >< 3 ><|.4 >< 5.|>< 6 ><.|7 >< 8 >|
+--------+--------+--------+--------+--------+
<===> 8th character
<====> 7th character
<===> 6th character
<====> 5th character
<====> 4th character
<===> 3rd character
<====> 2nd character
<===> 1st character
Josefsson Informational [Page 9]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
The following example of Base64 data is from [4].
Input data: 0x14fb9c03d97e
Hex: 1 4 f b 9 c | 0 3 d 9 7 e
8-bit: 00010100 11111011 10011100 | 00000011 11011001
11111110
6-bit: 000101 001111 101110 011100 | 000000 111101 100111
111110
Decimal: 5 15 46 28 0 61 37 62
Output: F P u c A 9 l +
Input data: 0x14fb9c03d9
Hex: 1 4 f b 9 c | 0 3 d 9
8-bit: 00010100 11111011 10011100 | 00000011 11011001
pad with 00
6-bit: 000101 001111 101110 011100 | 000000 111101 100100
Decimal: 5 15 46 28 0 61 36
pad with =
Output: F P u c A 9 k =
Input data: 0x14fb9c03
Hex: 1 4 f b 9 c | 0 3
8-bit: 00010100 11111011 10011100 | 00000011
pad with 0000
6-bit: 000101 001111 101110 011100 | 000000 110000
Decimal: 5 15 46 28 0 48
pad with = =
Output: F P u c A w = =
8. Security Considerations
When implementing Base encoding and decoding, care should be taken
not to introduce vulnerabilities to buffer overflow attacks, or other
attacks on the implementation. A decoder should not break on invalid
input including, e.g., embedded NUL characters (ASCII 0).
If non-alphabet characters are ignored, instead of causing rejection
of the entire encoding (as recommended), a covert channel that can be
used to "leak" information is made possible. The implications of
this should be understood in applications that do not follow the
recommended practice. Similarly, when the base 16 and base 32
alphabets are handled case insensitively, alteration of case can be
used to leak information.
Base encoding visually hides otherwise easily recognized information,
such as passwords, but does not provide any computational
confidentiality. This has been known to cause security incidents
when, e.g., a user reports details of a network protocol exchange
Josefsson Informational [Page 10]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
(perhaps to illustrate some other problem) and accidentally reveals
the password because she is unaware that the base encoding does not
protect the password.
9. References
9.1. Normative References
[1] Bradner, S., "Key words for use in RFCs to Indicate Requirement
Levels", BCP 14, RFC 2119, March 1997.
9.2. Informative References
[2] Linn, J., "Privacy Enhancement for Internet Electronic Mail:
Part I: Message Encryption and Authentication Procedures", RFC
1421, February 1993.
[3] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part One: Format of Internet Message Bodies",
RFC 2045, November 1996.
[4] Callas, J., Donnerhacke, L., Finney, H. and R. Thayer, "OpenPGP
Message Format", RFC 2440, November 1998.
[5] Eastlake, D., "Domain Name System Security Extensions", RFC 2535,
March 1999.
[6] Klyne, G. and L. Masinter, "Identifying Composite Media
Features", RFC 2938, September 2000.
[7] Myers, J., "SASL GSSAPI mechanisms", Work in Progress.
[8] Wilcox-O'Hearn, B., "Post to P2P-hackers mailing list", World
Wide Web http://zgp.org/pipermail/p2p-hackers/2001-
September/000315.html, September 2001.
[9] Cerf, V., "ASCII format for Network Interchange", RFC 20, October
1969.
10. Acknowledgements
Several people offered comments and suggestions, including Tony
Hansen, Gordon Mohr, John Myers, Chris Newman, and Andrew Sieber.
Text used in this document is based on earlier RFCs describing
specific uses of various base encodings. The author acknowledges the
RSA Laboratories for supporting the work that led to this document.
Josefsson Informational [Page 11]
RFC 3548 The Base16, Base32, and Base64 Data Encodings July 2003
11. Editor's Address
Simon Josefsson
EMail: simon@josefsson.org
Base64 encode/decode 함수
/*------ Base64 Encoding Table ------*/ static const char MimeBase64[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; /*------ Base64 Decoding Table ------*/ static int DecodeMimeBase64[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ }; int base64_decode(char *text, unsigned char *dst, int numBytes ) { const char* cp; int space_idx = 0, phase; int d, prev_d = 0; unsigned char c; space_idx = 0; phase = 0; for ( cp = text; *cp != '\0'; ++cp ) { d = DecodeMimeBase64[(int) *cp]; if ( d != -1 ) { switch ( phase ) { case 0: ++phase; break; case 1: c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) ); if ( space_idx < numBytes ) dst[space_idx++] = c; ++phase; break; case 2: c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) ); if ( space_idx < numBytes ) dst[space_idx++] = c; ++phase; break; case 3: c = ( ( ( prev_d & 0x03 ) << 6 ) | d ); if ( space_idx < numBytes ) dst[space_idx++] = c; phase = 0; break; } prev_d = d; } } return space_idx; } int base64_encode(char *text, int numBytes, char **encodedText) { unsigned char input[3] = {0,0,0}; unsigned char output[4] = {0,0,0,0}; int index, i, j, size; char *p, *plen; plen = text + numBytes - 1; size = (4 * (numBytes / 3)) + (numBytes % 3? 4 : 0) + 1; (*encodedText) = malloc(size); j = 0; for (i = 0, p = text;p <= plen; i++, p++) { index = i % 3; input[index] = *p; if (index == 2 || p == plen) { output[0] = ((input[0] & 0xFC) >> 2); output[1] = ((input[0] & 0x3) << 4) | ((input[1] & 0xF0) >> 4); output[2] = ((input[1] & 0xF) << 2) | ((input[2] & 0xC0) >> 6); output[3] = (input[2] & 0x3F); (*encodedText)[j++] = MimeBase64[output[0]]; (*encodedText)[j++] = MimeBase64[output[1]]; (*encodedText)[j++] = index == 0? '=' : MimeBase64[output[2]]; (*encodedText)[j++] = index < 2? '=' : MimeBase64[output[3]]; input[0] = input[1] = input[2] = 0; } } (*encodedText)[j] = '\0'; return 0; }
realloc
realloc
동적으로 할당된 메모리를 재할당합니다. |
Declaration
void *realloc( void *memblock, size_t size ) |
Return value
성공 - 새로운 메모리 영역을 가리키는 포인터 반환실패 - NULL 포인터 반환
Parameters
memblock - NULL 포인터 또는 이전에 할당한 메모리 영역을 가리키는 포인터size - 새로 할당받을 메모리 크기(단위는 바이트)
Detail descriptions
realloc()를 번역할 때 "재할당" 또는 "다시 할당"이라고 하는데 많이 어색합니다. 다시 할당하는 것이 아니라 할당된 메모리 영역의 크기를 변경, 대부분은 확장하는 것입니다. 정확하게 이름을 짓는다면 extendalloc()정도가 좋겠습니다. 굳이 "재할당(realloc)"이라고 이름 지은 것은 대부분 새로운 메모리를 할당받기 때문입니다.
realloc()에 전달되는 메모리 영역의 크기는 이전에 할당됐던 영역보다 클 수도 있고 작을 수도 있습니다. 대부분 메모리가 부족했기 때문에 다시 할당 받는 것이지만, 작은 경우가 있을 수도 있습니다. 그러나, 작은 경우라면 메모리를 새로 할당할 이유가 없습니다. 메모리가 부족한 경우에 인접해 있는 연속된 구간을 확보할 수 있다면, 다시 말해 현 상태에서 늘릴 수 있는 영역이 있다면 새로 할당하지 않아도 됩니다. 그러나, 대부분은 그렇지가 못하므로 기존 메모리를 해제하고 다른 위치에 할당하게 됩니다. [C 언어 표준에서는 어떻게 할당해야 한다는 것에 대해서는 언급하지 않습니다. 이 모든 상황은 구현에 따라 달라질 수 있습니다.]
메모리를 새로운 위치에 할당하려면 아래 순서에 맞게 작업해야 합니다.
- 새로운 위치에 지정한 크기만큼 메모리를 할당합니다.
- 이전 영역에 있던 내용을 새로운 영역에 복사합니다.
- 이전 영역의 메모리를 해제합니다.
realloc()는 이와 같이 동작합니다. 새로운 영역을 할당받더라도 현재 내용을 잃어버리는 일은 절대 없습니다. realloc()를 흉내낸 코드가 "Example codes" 항목에 있습니다. 참고하기 바랍니다.
그러나, 이와 같은 방식에는 엄청난 결함이 있습니다. realloc()는 새로운 위치에 할당될 수 있기 때문에, 이전 영역을 가리키는 모든 포인터 변수를 새로운 주소로 변경해야 합니다. 그렇게 할 수 없다면 해제된 영역을 가리키는 포인터가 언젠가는 문제를 일으킬 것입니다. 동적으로 할당한 메모리가 있고 여러 개의 포인터가 공유하고 있다면, 절대 realloc()를 사용해서는 안됩니다. 악명높은 덜 떨어진 포인터, 댕글링(dangling) 포인터가 탄생합니다.
realloc()는 어떤 경우를 실패라고 생각할까요? malloc()라면 할당할 메모리가 부족한 경우에 NULL 포인터를 반환합니다. 첫 번째는 확장에 실패한 경우입니다. realloc()의 목적은 메모리를 확장해서 옮기는 것인데, 현재 위치에서뿐만 아니라 새로운 위치로도 옮겨가지 못하면 NULL 포인터를 반환합니다. 이때 이전 영역에 있던 내용은 그대로 남아 있습니다. 두 번째는 새롭게 할당할 크기를 0으로 지정한 경우입니다. 크기가 0이라는 것은 새로운 할당으로 간주하지 않고, 이전에 사용하던 영역의 해제로 봅니다. free() 함수를 호출하는 셈입니다. 사용하던 영역은 해제되어 다음 번 동적 할당에서 사용할 수 있게 되고, NULL 포인터를 반환합니다. NULL 포인터의 반환이 너무 당연한 것이 할당받은 메모리가 없으므로 가리킬 곳도 없습니다. 그러나, 메모리를 해제하기 위해 realloc()를 사용해서는 안됩니다. free()로 해제할 때 가장 분명한 코드가 나오기 때문에 가독성에서 너무 좋지 않은 행동입니다.
memblock 매개 변수에 NULL 포인터를 전달하는 것은 malloc()를 호출하는 것과 같습니다. 사용중인 메모리가 없기 때문에 해제할 메모리도 없고, 결국 할당만 일어나게 되므로 malloc() 호출과 같습니다.
realloc()가 성공할 때의 반환값이 두 가지가 있습니다. 첫 번째는 현재 위치에서 확장에 성공한 경우로, memblock 매개 변수에 전달된 주소와 같습니다. 두 번째는 연속된 메모리가 없어서 새로운 위치에 할당된 경우로 memblock 매개 변수와 다른 주소입니다. 주의해야 할 것이 있다면 현재 위치에서 확장될 수 있다고 생각하는 것입니다. 함수 이름이 주는 모호함 때문에 자칫 엄청난 실수를 할 수 있습니다.
간혹 realloc()가 필요한지 의아할 때가 있습니다. 메모리가 부족한 상황을 만나기 전에 리스트(list) 자료구조를 사용하거나 엄청난 크기로 할당한 다음 나누어 쓰는 방법이 있습니다. 동적으로 할당한 메모리를 여러 개의 포인터 변수가 가리키는 상황이 생길 수 있다면, 지금 얘기한 방법들을 진지하게 고민해야 할 것입니다.
Remarks
메모리 동적 할당 함수인 calloc(), malloc(), realloc()를 비교한 표가 malloc()에 있습니다. 참고하기 바랍니다.
Header files
<stdlib.h> <malloc.h>
Example codes
- realloc()로 메모리를 "확장"하거나 "축소"할 때 발생하는 포인터 변화를 보여줍니다.
- 먼저 NULL 포인터를 전달해서 메모리를 할당한 다음, 사용중인 크기보다 두 배 크게 "확장"하고, 최초 크기보다 두 배 작게 "축소"해서 결과를 비교합니다.
- 출력 결과를 보면 "확장"할 때는 반환된 주소가 사용중인 주소와 다르지만, "축소"할 때는 사용중인 주소와 같습니다. "축소"할 때는 사용중인 메모리를 재활용하고 있음을 알 수 있습니다.
- 그러나, C 언어 표준에서는 재활용할 것을 요구하지 않습니다. 컴파일러를 구현하는 회사측에서 어떻게 구현할지 결정할 따름입니다.
- free()는 한번만 사용했습니다. realloc()를 세 번 호출했지만, 두 번째 호출부터는 내부적으로 free()를 호출하기 때문에 realloc() 호출 횟수만큼 free()를 호출해서는 안됩니다.
#include <stdio.h> #include <stdlib.h> void main() { int* array = NULL; const int size = 5; array = realloc( NULL, size*sizeof(int) ); printf( "[확장]\n" ); printf( "old - %d\n", array ); array = realloc( array, size*sizeof(int)*2 ); printf( "cur - %d\n", array ); printf( "\n[축소]\n" ); printf( "old - %d\n", array ); array = realloc( array, size*sizeof(int)/2 ); printf( "cur - %d\n", array ); free( array ); } [출력 결과] [확장] old - 4397872 cur - 4397776 [축소] old - 4397776 cur - 4397776 |
- realloc()의 기본 사용법을 보여줍니다.
- NULL 포인터를 전달해서 malloc()처럼 사용하는 방법과 사용중인 메모리를 두 배로 확장했을 때의 결과를 보여줍니다.
- malloc()처럼 바이트 단위로 동작하기 때문에 요소 개수와 요소 크기를 곱해서 전달합니다.
- 출력 결과를 보면, 사용중인 메모리를 20바이트에서 40바이트로 확장하면 사용중인 영역이 확장되는 것이 아니라 새로운 영역에 내용을 복사합니다. 사용중인 영역의 내용은 손상없이 복사되지만, 확장된 영역은 초기화되지 않고 쓰레기로 채워집니다.
- 동적 배열을 하나만 사용하고 크기도 20바이트를 확장했을 뿐인데, 새로운 주소를 반환한다는 것은 사용중인 영역의 연속된 구간을 할당하는 것이 얼마나 어려운지 보여줍니다. 출력 결과에서 []로 감싼 숫자가 할당받은 주소인데 서로 다릅니다. 절대 사용중인 주소를 계속해서 사용할 수 있을 거라고 기대해선 안됩니다.
- MultiArray()는 배열을 초기화하는 함수로, 0부터 시작해서 multi의 배수로 채웁니다.
#include <stdio.h> #include <stdlib.h> void MultiArray( int* array, int size, int multi ); void ShowArray( int* array, int size ); void main() { int* array = NULL; int size = 5; array = realloc( NULL, size*sizeof(int) ); MultiArray( array, size, 3 ); ShowArray( array, size ); size *= 2; array = realloc( array, size*sizeof(int) ); ShowArray( array, size ); free( array ); } void MultiArray( int* array, int size, int multi ) { int i; for( i = 0; i < size; i++ ) array[i] = i*multi; } void ShowArray( int* array, int size ) { int i; printf( "[%d]", array ); for( i = 0; i < size; i++ ) printf( " %d", array[i] ); printf( "\n" ); } [출력 결과] [4397776] 0 3 6 9 12 [4397680] 0 3 6 9 12 -842150451 -842150451 -842150451 -842150451 -842150451 |
- 사용중인 메모리의 크기를 늘려주는 realloc()를 만들어 봅니다. ExtendArray() 내부에서 calloc()로 새로 만든 메모리를 초기화합니다. 프로그램은 0보다 작거나 같은 정수를 입력하면 종료합니다.
- realloc()와 똑같이 만드는 것은 불가능합니다. 간단하게 realloc()의 특징일 수 있는 것만을 일부 구현합니다.
- realloc()를 호출하는 상황이 네 가지 있습니다. 이들 네 가지는 "최초 할당"과 "축소", "확장", "변화 없음"으로 부를 수 있습니다.
- "최초 할당"은 사용중인 메모리 주소 대신 NULL 포인터가 전달되는 경우로, 사용중인 메모리가 없다는 뜻이므로 별도의 코드없이 calloc()의 호출 결과를 반환합니다.
- "축소"와 "변화 없음"은 메모리 크기를 줄이는 것인데, 굳이 사용중인 메모리를 해제할 필요없이 기존에 사용하던 메모리를 재활용합니다. 크기를 줄이는 과정에서 남는 메모리를 해제할 수 있는 방법이 없으므로, 사용중이던 메모리를 해제할 때까지 그대로 둡니다. 일종의 낭비라고 생각할 수도 있겠습니다. ExtendArray() 내부적으로 메모리에 변화가 없으므로 매개 변수로 전달된 oldblock을 그대로 반환합니다.
- "확장"일 때 realloc()를 가장 많이 사용합니다. 연속된 메모리가 있다고 보장할 수 없기 때문에, 요청한 메모리를 새롭게 할당합니다. 사용중인 메모리의 내용을 새로운 메모리로 옮기고, 사용중인 메모리를 해제합니다.
- calloc()를 사용했으므로 "확장"된 메모리는 모두 0으로 초기화됩니다. 마지막 출력 결과를 보면, 요소가 3개에서 6개로 확장되었고, 확장된 요소에는 0이 들어갑니다. calloc()를 사용해서 할당했다는 증거입니다. 실제의 realloc()는 "확장" 메모리를 초기화하지 않는데, malloc()을 사용하는 것 같습니다.
- 출력 결과를 분석하기가 조금 어렵습니다. 첫 번째 결과는 "최초 할당"이고, 두 번째 결과는 "확장"이고 FillArray()를 호출했기 때문에 새로운 내용으로 채워졌습니다. 세 번째와 네 번째 결과는 "축소"이고, "축소"는 사용중이던 메모리를 해제하지 않기 때문에 이전 결과와 똑같이 나옵니다.
- FillArray()는 메모리를 확장할 때만 호출합니다. "축소"에서 사용중인 메모리를 유지한다는 것을 보여주기 위해 FillArray()는 "확장"일 때만 호출합니다. 그러나, "확장"에 대한 결과도 보여줘야 하므로 반복문을 벗어나면 무조건 2배로 확장합니다. 확장 메모리가 0으로 초기화된다는 것을 보여주기 위해, 이번에는 FillArray()를 호출하지 않습니다.
- 의도적으로 calloc()를 사용했지만, 메모리를 초기화하지 않는 malloc()를 사용해도 괜찮습니다.
- FillArray()와 ShowArray()는 배열을 난수로 채우고 화면에 출력하는 함수들입니다. 대단할 것이 없는 함수이므로 설명은 생략합니다.
#include <stdio.h> #include <stdlib.h> #include <string.h> void* ExtendArray( void* p, int cur, int want, int datasize ); void FillArray( int* array, int size ); void ShowArray( int* array, int size ); void main() { int* array = NULL; int want, use = 0; while( 1 ) { printf( "개수 : " ); scanf( "%d", &want ); if( want <= 0 ) break; array = ExtendArray( array, use, want, sizeof(int) ); if( want > use ) FillArray( array, want ); use = want; ShowArray( array, use ); } printf( "[calloc() 확인]\n" ); want = use * 2; array = ExtendArray( array, use, want, sizeof(int) ); ShowArray( array, want ); } void* ExtendArray( void* oldblock, int count, int want, int datasize ) { void* block; if( oldblock == NULL ) // 최초 할당 return calloc( want, datasize ); if( count >= want ) // 축소, 변화 없음 block = oldblock; else // 확장 { block = calloc( want, datasize ); memcpy( block, oldblock, count*datasize ); free( oldblock ); } return block; } void FillArray( int* array, int size ) { int i; for( i = 0; i < size; i++ ) array[i] = rand()%100; } void ShowArray( int* array, int size ) { int i; for( i = 0; i < size; i++ ) printf( "%d ", array[i] ); printf( "\n" ); } [출력 결과] 개수 : 4 41 67 34 0 개수 : 9 69 24 78 58 62 64 5 45 81 개수 : 7 69 24 78 58 62 64 5 개수 : 3 69 24 78 개수 : 0 [calloc() 확인] 69 24 78 0 0 0 |
static 함수
보통 함수 선언 앞에도 static을 붙일수 있습니다.
원래 static은 전역 변수를 선언할 때 많이 사용되는데
함수 앞에 static을 선언하면...
이 함수가 선언된 파일 안에서만 함수를 호출할 수 있다는 의미가 됩니다.
따라서 파일 3개에서 다음과 같은 사용이 가능해 지네요.
a.c 파일
static void func1(void)
{
......
}
b.c 파일
void func1(void)
{
......
}
main.c 파일
extern void func1(void)
void main(void)
{
func1();
......
}
원래 static으로 선언되지 않았으면 전체 프로젝트에서 같은 이름의 함수 func1()이 2개나 선언되었기 때문에 컴파일 에러가 발생합니다.
그러나 위처럼 a.c 파일의 func1()은 static으로 선언되면 컴파일 에러가 발생하지 않습니다.
단 a.c 파일의 func1()은 a.c 파일에서만 효력을 가지게 되며, 당연히 a.c 파일에서 호출하는 func1()은 a.c 파일에 선언된 함수를 Call 하는 것이고
main.c 처럼 다른 파일에서 호출하는 func1()은 b.c 파일에서 선언된 func1()을 사용하게 됩니다.
그런데 이거 여러사람이 작업할 때, 헷갈리면 정말 골때리는 경우도 나오겠네요.
모 디버깅을 하면 바로 알겠지만...
로봇을 만들어 봅시다.
프로젝트를 만들었으면 로봇도 만들어야 하겠져?
이제 로봇을 만들어 봅시다.
원문은 먼저 올라온 글과 멀리 떨어진 곳이 아니니 함 찾아보세요
클래스를 만듭니다. 프로젝트안에 들어갈 클래스를 만들기 위해 NEW->CLASS 를 클릭합니다.
패키지는 자신의 이름에 대한 이니셜을 넣고(IBM 권장사항),
클래스명엔 자신의 로봇이 갖길 원하는 이름을 넣습니다.
슈퍼클래스는 robocode.Robot 으로 넣어줍니다
성공하면 다음과 같은 화면이 나옵니다.
이제 원하는 코드를 넣을 수가 있습니다.
(영어 실력이 짧은지라 원문 그대로 갖고옵니다. 알아서 해석 하시길)
You can save your robot as often as you like by hitting CTRL-S, or selecting "File->Save". There's no need to select Compile anymore, since Eclipse takes care of it for you. Have fun playing around with Eclipse. Personally, I like to change all the colors, turn off bold print, move the windows around, and do all sort of things to customize it to my liking. Since there's no better way to learn than by playing around, I'll leave you to it! (Hint: Workbench -> Preferences -> Java -> Editor -> Colors)
The only thing left is to make sure Robocode sees your robot.
이클립스에서 만든 로봇을 불러와 봅시다
로보코드를 실행한 후
Options->Prefereces->Development Options 클릭 후 해당 '프로젝트' 의 위치를 기입합니다.
Finish 버튼을 누르고 Battle -> New 로 가면 패키지를 찾은것을 볼 수 있습니다.
아래는 이클립스에 대해 설명이 올라와있는데
원문에는 이클립스에 대한 찬사나 간단한 사용법을 쓰고 있습니다.
궁금하면 원문을 참고해주세요
이클립스 단축키
Ctrl - S 저장
Ctrl-Space 멤버 메소드 출력
이클립스 로보코드 환경 설정
1. File->New->Project 에서 프로젝트를 만듭니다.
자바 프로그램이기 때문에 그냥 "Java Project" 를 선택해줍니다.
Finish 버튼은 절대 누르지 마시고, Next 를 누릅니다.
이제 프로젝트 세팅을 할 차례입니다. robocode 패키지 안에는 라이브러리, JAVADoc 이 있기 떄문에
이것들도 링크를 해줘야 합니다.(뭐 JAVADOC 이 머리속에 있다면 라이브러리만 링크해줘도 상관 없습니다.)
위 화면처럼 만든 후 Add External JARs.... 를 클릭합니다.
로보코드가 설치되있는 폴더로 찾아가 robocode.jar 을 엽니다.
이제 Javadoc 을 세팅해줄 시간입니다.
Javadoc Location 에서 Edit 를 누르고
Browse를 이용해서 해당 경로를 지정해주거나, 경로를 외고 있다면 위 형식에 맞게 지정해주면 끝!!
이제 탱크를 만들 수 있습니다.
guda의 미투데이 - 2007년 8월 10일
- 으윽... 이넘의 두통.. ㅠㅠ 담배 때문인가 신경성인가 ~_~; 지끈지끈 식은땀 ㅠㅠ 오후 7시 58분
이 글은 guda님의 미투데이 2007년 8월 10일 내용입니다.
Robocode 마스터의 비밀: Factored wall avoidance (한글)
난이도 : 초급
McCoy David, Writer, Independent
2007 년 4 월 17 일
코너에 갇히거나 원하는 이동 방향에서 너무 많이 벗어나지 않으면서, 로봇과 벽 사이의 간격을 유지하는 알고리즘은 간단히 만들 수 없는 것 같습니다. 한 가지 간단한 솔루션으로, Factored wall avoidance가 있습니다. 이 글에서, David McCoy가 이를 구현하는 방법을 설명합니다.
With a few additions to the bot we built in "상대편의 움직임 추적하기"에서 구현했던 로봇에 몇 가지를 더 추가하여, 기존의 움직임 알고리즘 또는 문제가 많은 움직임 알고리즘에 Factored Wall Avoidance를 추가할 수 있다. Factored Wall Avoidance는 자신의 로봇과 벽의 근접성에 따라서 안전한 방향 설정(heading)으로 원하는 방향을 팩토링 함으로써 최상의 방향을 찾는 것이다.
우선 자주 사용되는 수학적 알고리즘에 헬퍼 메소드를 로봇에 추가한다.
calculateBearingToXYRadians()
메소드는 java.lang.Math
메소드 atan2()
를 사용하여 sourceX,sourceY
에서 targetX,targetY
까지 절대 위치(absolute bearing)를 계산한 다음, 이 값을 sourceHeading
에 관련된 위치로 변환한다.
그리고 normalizeAbsoluteAngleRadians()
메소드와 normalizeRelativeAngleRadians()
메소드도 필요하다.
Listing 1. 수학 헬퍼 메소드
private static final double DOUBLE_PI = (Math.PI * 2); private static final double HALF_PI = (Math.PI / 2); public double calculateBearingToXYRadians(double sourceX, double sourceY, double sourceHeading, double targetX, double targetY) { return normalizeRelativeAngleRadians( Math.atan2((targetX - sourceX), (targetY - sourceY)) - sourceHeading); } public double normalizeAbsoluteAngleRadians(double angle) { if (angle < 0) { return (DOUBLE_PI + (angle % DOUBLE_PI)); } else { return (angle % DOUBLE_PI); } } public static double normalizeRelativeAngleRadians(double angle) { double trimmedAngle = (angle % DOUBLE_PI); if (trimmedAngle > Math.PI) { return -(Math.PI - (trimmedAngle % Math.PI)); } else if (trimmedAngle < -Math.PI) { return (Math.PI + (trimmedAngle % Math.PI)); } else { return trimmedAngle; } } |
|
AdvancedRobot을 back-as-front 기능으로 확장하기
다음으로, 로봇을 반대로 이동시키기 위한 back-as-front 기능을 제공하기 위해 AdvancedRobot
클래스 기능을 몇 가지 헬퍼 메소드로 확장할 필요가 있다.
getRelativeHeading()
메소드는 로봇의 현재 위치와 관련하여 정확한 방향을 계산한다.
reverseDirection()
메소드는 매우 단순하다.direction
인스턴스 변수를 토글링(toggle) 하고 로봇의 방향을 바꾼다. 감속할 때에는 시간이 걸리기 때문에 로봇은 속도에 따라서, 방향을 바꾸기 전에 최대 네 개의 프레임까지 같은 방향으로 움직일 수도 있다.
setAhead()
와setBack()
메소드는AdvancedRobot
클래스에서 같은 이름의 메소드를 오버라이드 한다. 현재 방향에 대한 로봇의 속도를 설정하고,direction
인스턴스 변수를 필요에 따라 조정한다. 비례 연산은 로봇이 현재 움직이고 있는 방향과 관련이 있다.
setTurnLeftRadiansOptimal()
과setTurnRightRadiansOptimal()
메소드는(Math.PI / 2)
보다 크게 회전하여 로봇의 방향을 바꾼다.adjustHeadingForWalls
메소드를 사용할 때 이러한 메소드들을 사용해야 하는데, 나중에 설명하겠다.
주: getter와 setter 메소드를 사용하는 대신 direction
인스턴스 변수에 직접 액세스 한다. 이것은 좋은 방법은 아니지만, 나는 내 로봇 코드에 이를 수행하여 데이터 액세스 속도를 높이곤 한다.
Listing 2. 로봇 헬퍼 메소드
public double getRelativeHeadingRadians() { double relativeHeading = getHeadingRadians(); if (direction < 1) { relativeHeading = normalizeAbsoluteAngleRadians(relativeHeading + Math.PI); } return relativeHeading; } public void reverseDirection() { double distance = (getDistanceRemaining() * direction); direction *= -1; setAhead(distance); } public void setAhead(double distance) { double relativeDistance = (distance * direction); super.setAhead(relativeDistance); if (distance < 0) { direction *= -1; } } public void setBack(double distance) { double relativeDistance = (distance * direction); super.setBack(relativeDistance); if (distance > 0) { direction *= -1; } } public void setTurnLeftRadiansOptimal(double angle) { double turn = normalizeRelativeAngleRadians(angle); if (Math.abs(turn) > HALF_PI) { reverseDirection(); if (turn < 0) { turn = (HALF_PI + (turn % HALF_PI)); } else if (turn > 0) { turn = -(HALF_PI - (turn % HALF_PI)); } } setTurnLeftRadians(turn); } public void setTurnRightRadiansOptimal(double angle) { double turn = normalizeRelativeAngleRadians(angle); if (Math.abs(turn) > HALF_PI) { reverseDirection(); if (turn < 0) { turn = (HALF_PI + (turn % HALF_PI)); } else if (turn > 0) { turn = -(HALF_PI - (turn % HALF_PI)); } } setTurnRightRadians(turn); } |
|
|
우리가 추가할 마지막 메소드는 adjustHeadingForWalls()
이다.
이 메소드의 초반부는 벽과의 근접성에 기반하여 안전한 x,y 위치를 선택한다. (이것은 로봇의 현재 x 또는 y 좌표 또는 로봇이 벽에 가까이 있을 경우 중심점이 될 것이다.) 이 메소드의 후반부는 "안전한" 방향을 계산하고 로봇이 벽에 얼마나 근접해 있는가에 비례하여 원하는 방향으로 이를 팩토링 한다.
로봇이 벽으로 나아가는 정도는 WALL_AVOID_INTERVAL
과 WALL_AVOID_FACTORS
상수를 사용하여 조정될 수 있다.
Listing 3. 벽 피하기 메소드
private static final double WALL_AVOID_INTERVAL = 10; private static final double WALL_AVOID_FACTORS = 20; private static final double WALL_AVOID_DISTANCE = (WALL_AVOID_INTERVAL * WALL_AVOID_FACTORS); private double adjustHeadingForWalls(double heading) { double fieldHeight = getBattleFieldHeight(); double fieldWidth = getBattleFieldWidth(); double centerX = (fieldWidth / 2); double centerY = (fieldHeight / 2); double currentHeading = getRelativeHeadingRadians(); double x = getX(); double y = getY(); boolean nearWall = false; double desiredX; double desiredY; // If we are too close to a wall, calculate a course toward // the center of the battlefield. if ((y < WALL_AVOID_DISTANCE) || ((fieldHeight - y) < WALL_AVOID_DISTANCE)) { desiredY = centerY; nearWall = true; } else { desiredY = y; } if ((x < WALL_AVOID_DISTANCE) || ((fieldWidth - x) < WALL_AVOID_DISTANCE)) { desiredX = centerX; nearWall = true; } else { desiredX = x; } // Determine the safe heading and factor it in with the desired // heading if the bot is near a wall if (nearWall) { double desiredBearing = calculateBearingToXYRadians(x, y, currentHeading, desiredX, desiredY); double distanceToWall = Math.min( Math.min(x, (fieldWidth - x)), Math.min(y, (fieldHeight - y))); int wallFactor = (int)Math.min((distanceToWall / WALL_AVOID_INTERVAL), WALL_AVOID_FACTORS); return ((((WALL_AVOID_FACTORS - wallFactor) * desiredBearing) + (wallFactor * heading)) / WALL_AVOID_FACTORS); } else { return heading; } } |
|
나머지는 쉽다. 현재의 네비게이션 알고리즘을 사용하고 adjustHeadingForWalls()
메소드를 통해 그 결과를 제공함으로써 벽을 피할 수 있다.
단순하게 하기 위해 로봇 예제(다운로드)는 방향 변경에 0을 요청하여 직선 라인으로 움직일 것이다.
Listing 4. 벽 피하기 메소드
public void run() { while(true) { setTurnRightRadiansOptimal(adjustHeadingForWalls(0)); setAhead(100); execute(); } } |
이제 다 되었다. 간단하지만, 효과적이다.
<출처: http://www.ibm.com/developerworks/kr/library/j-fwa/>