質問するログイン新規登録

回答編集履歴

6

サロゲート判定範囲バグ修正

2021/11/04 14:44

投稿

jimbe
jimbe

スコア13382

answer CHANGED
@@ -59,25 +59,28 @@
59
59
 
60
60
  private static int getUTF16Length(char c) throws UnsupportedEncodingException {
61
61
  int v = c & 0xffff;
62
- if(v < 0xd800 || 0xe000 <= v) return 1;
63
- if(0xd800 <= v || v < 0xdc00) return 2;
62
+ if(isHighSurrogate(v)) return 2;
64
- throw new UnsupportedEncodingException(); //下位サロゲートは例外
63
+ if(isLowSurrogate(v)) throw new UnsupportedEncodingException();
64
+ return 1;
65
65
  }
66
66
 
67
67
  private static int getUTF8Length(char c) throws UnsupportedEncodingException {
68
68
  int v = c & 0xffff;
69
- if(v < 0xd800 || 0xe000 <= v) {
69
+ if(isHighSurrogate(v)) return 4;
70
+ if(isLowSurrogate(v)) throw new UnsupportedEncodingException();
70
- if(v < 128) return 1;
71
+ if(v < 128) return 1;
71
- if(v < 2048) return 2;
72
+ if(v < 2048) return 2;
72
- return 3;
73
+ return 3;
73
- }
74
- if(0xd800 <= v || v < 0xdc00) {
75
- return 4;
76
- }
77
- throw new UnsupportedEncodingException();
78
74
  }
75
+
76
+ private static boolean isHighSurrogate(int c) {
77
+ return 0xd800 <= c && c < 0xdc00;
78
+ }
79
+
80
+ private static boolean isLowSurrogate(int c) {
81
+ return 0xdc00 <= c && c < 0xe000;
82
+ }
79
83
  }
80
-
81
84
  ```
82
85
  ```plain
83
86
  'あいaうg????えhおjかkきlくlけこhdさjしuすuせそ'

5

コード一部整理

2021/11/04 14:44

投稿

jimbe
jimbe

スコア13382

answer CHANGED
@@ -48,12 +48,11 @@
48
48
  //UTF8 にした時に nbyte に収まる文字数を返す.
49
49
  private static int countChar(String src, int nbyte, int start) throws UnsupportedEncodingException {
50
50
  int last = start;
51
- for(int i=start, j=0; i<src.length(); ) {
51
+ for(int i=0; last<src.length(); ) {
52
- char c = src.charAt(i);
52
+ char c = src.charAt(last);
53
- j += getUTF8Length(c);
53
+ i += getUTF8Length(c);
54
- if(j > nbyte) break;
54
+ if(i > nbyte) break;
55
- i += getUTF16Length(c);
55
+ last += getUTF16Length(c);
56
- last = i;
57
56
  }
58
57
  return last - start;
59
58
  }

4

バグ修正

2021/11/04 12:24

投稿

jimbe
jimbe

スコア13382

answer CHANGED
@@ -38,7 +38,7 @@
38
38
 
39
39
  System.out.println("------------");
40
40
 
41
- for(int start=0, v=0; (v=countChar(data,10,start)) > 0; start+=v) {
41
+ for(int start=0, v=0; (v=countChar(data,13,start)) > 0; start+=v) {
42
42
  System.out.print("v="+v);
43
43
  String s = data.substring(start, start+v);
44
44
  System.out.println(": '"+s+"'="+s.getBytes("UTF8").length);
@@ -48,11 +48,12 @@
48
48
  //UTF8 にした時に nbyte に収まる文字数を返す.
49
49
  private static int countChar(String src, int nbyte, int start) throws UnsupportedEncodingException {
50
50
  int last = start;
51
- for(int i=start, j=0; i<src.length() && j<=nbyte; ) {
51
+ for(int i=start, j=0; i<src.length(); ) {
52
- last = i;
53
52
  char c = src.charAt(i);
53
+ j += getUTF8Length(c);
54
+ if(j > nbyte) break;
54
55
  i += getUTF16Length(c);
55
- j += getUTF8Length(c);
56
+ last = i;
56
57
  }
57
58
  return last - start;
58
59
  }
@@ -77,6 +78,7 @@
77
78
  throw new UnsupportedEncodingException();
78
79
  }
79
80
  }
81
+
80
82
  ```
81
83
  ```plain
82
84
  'あいaうg????えhおjかkきlくlけこhdさjしuすuせそ'
@@ -101,11 +103,10 @@
101
103
  i=18, v=8: 'あいaうg????え'=18
102
104
  i=19, v=9: 'あいaうg????えh'=19
103
105
  ------------
104
- v=4: 'あいaう'=10
106
+ v=5: 'あいaうg'=11
105
- v=5: 'g????えh'=9
107
+ v=6: '????えhおj'=12
106
- v=4: 'おjかk'=8
107
- v=4: 'きlくl'=8
108
+ v=6: 'かkきlくl'=12
108
- v=4: 'けこhd'=8
109
+ v=6: 'けこhdさj'=12
109
- v=4: 'さjしu'=8
110
- v=3: 'すuせ'=7
110
+ v=5: 'しuすuせ'=11
111
+ v=1: 'そ'=3
111
112
  ```

3

説明修正

2021/11/04 12:15

投稿

jimbe
jimbe

スコア13382

answer CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  ----
17
17
 
18
- ループはしますが内部で文字列編集はしないという方向で、 substring に使える値を返すメソッドにしてみました。
18
+ ループはしますが内部で文字列編集(や getBytes() 等)はしないという方向で、 substring に使える値を返すメソッドにしてみました。
19
19
 
20
20
  テスト文字列は shiketa さんの回答から頂きました。
21
21
 

2

countChar に start パラメータ追加、main に切り出しサンプル追加

2021/11/04 11:38

投稿

jimbe
jimbe

スコア13382

answer CHANGED
@@ -30,22 +30,31 @@
30
30
  System.out.println("'"+data+"'");
31
31
 
32
32
  for(int i=0; i<20; i++) {
33
- int v = countChar(data, i);
33
+ int v = countChar(data, i, 0);
34
34
  System.out.print("i="+i+", v="+v);
35
35
  String s = data.substring(0, v);
36
36
  System.out.println(": '"+s+"'="+s.getBytes("UTF8").length);
37
37
  }
38
+
39
+ System.out.println("------------");
40
+
41
+ for(int start=0, v=0; (v=countChar(data,10,start)) > 0; start+=v) {
42
+ System.out.print("v="+v);
43
+ String s = data.substring(start, start+v);
44
+ System.out.println(": '"+s+"'="+s.getBytes("UTF8").length);
45
+ }
38
46
  }
47
+
39
48
  //UTF8 にした時に nbyte に収まる文字数を返す.
40
- private static int countChar(String src, int nbyte) throws UnsupportedEncodingException {
49
+ private static int countChar(String src, int nbyte, int start) throws UnsupportedEncodingException {
41
- int length = 0;
50
+ int last = start;
42
- for(int i=0, j=0; i<src.length() && j<=nbyte; ) {
51
+ for(int i=start, j=0; i<src.length() && j<=nbyte; ) {
43
- length = i;
52
+ last = i;
44
53
  char c = src.charAt(i);
45
54
  i += getUTF16Length(c);
46
55
  j += getUTF8Length(c);
47
56
  }
48
- return length;
57
+ return last - start;
49
58
  }
50
59
 
51
60
  private static int getUTF16Length(char c) throws UnsupportedEncodingException {
@@ -91,4 +100,12 @@
91
100
  i=17, v=7: 'あいaうg????'=15
92
101
  i=18, v=8: 'あいaうg????え'=18
93
102
  i=19, v=9: 'あいaうg????えh'=19
103
+ ------------
104
+ v=4: 'あいaう'=10
105
+ v=5: 'g????えh'=9
106
+ v=4: 'おjかk'=8
107
+ v=4: 'きlくl'=8
108
+ v=4: 'けこhd'=8
109
+ v=4: 'さjしu'=8
110
+ v=3: 'すuせ'=7
94
111
  ```

1

コード追加

2021/11/04 10:37

投稿

jimbe
jimbe

スコア13382

answer CHANGED
@@ -11,4 +11,84 @@
11
11
  }
12
12
  ```
13
13
  といった感じで先頭のバイト値でその文字の必要バイト数が出せます。
14
- これを用いて文字数とバイト数を加算していけば、限界バイト数を超えた時点での文字数を得たりできると思いますが、ループなんですよね ^^;
14
+ これを用いて文字数とバイト数を加算していけば、限界バイト数を超えた時点での文字数を得たりできると思いますが、ループなんですよね ^^;
15
+
16
+ ----
17
+
18
+ ループはしますが内部で文字列編集はしないという方向で、 substring に使える値を返すメソッドにしてみました。
19
+
20
+ テスト文字列は shiketa さんの回答から頂きました。
21
+
22
+ ```java
23
+ package teratail_java.q367700;
24
+
25
+ import java.io.UnsupportedEncodingException;
26
+
27
+ public class Q367700 {
28
+ public static void main(String[] args) throws UnsupportedEncodingException {
29
+ final String data = "あいaうg\uD867\uDE3Dえhおjかkきlくlけこhdさjしuすuせそ";
30
+ System.out.println("'"+data+"'");
31
+
32
+ for(int i=0; i<20; i++) {
33
+ int v = countChar(data, i);
34
+ System.out.print("i="+i+", v="+v);
35
+ String s = data.substring(0, v);
36
+ System.out.println(": '"+s+"'="+s.getBytes("UTF8").length);
37
+ }
38
+ }
39
+ //UTF8 にした時に nbyte に収まる文字数を返す.
40
+ private static int countChar(String src, int nbyte) throws UnsupportedEncodingException {
41
+ int length = 0;
42
+ for(int i=0, j=0; i<src.length() && j<=nbyte; ) {
43
+ length = i;
44
+ char c = src.charAt(i);
45
+ i += getUTF16Length(c);
46
+ j += getUTF8Length(c);
47
+ }
48
+ return length;
49
+ }
50
+
51
+ private static int getUTF16Length(char c) throws UnsupportedEncodingException {
52
+ int v = c & 0xffff;
53
+ if(v < 0xd800 || 0xe000 <= v) return 1;
54
+ if(0xd800 <= v || v < 0xdc00) return 2;
55
+ throw new UnsupportedEncodingException(); //下位サロゲートは例外
56
+ }
57
+
58
+ private static int getUTF8Length(char c) throws UnsupportedEncodingException {
59
+ int v = c & 0xffff;
60
+ if(v < 0xd800 || 0xe000 <= v) {
61
+ if(v < 128) return 1;
62
+ if(v < 2048) return 2;
63
+ return 3;
64
+ }
65
+ if(0xd800 <= v || v < 0xdc00) {
66
+ return 4;
67
+ }
68
+ throw new UnsupportedEncodingException();
69
+ }
70
+ }
71
+ ```
72
+ ```plain
73
+ 'あいaうg????えhおjかkきlくlけこhdさjしuすuせそ'
74
+ i=0, v=0: ''=0
75
+ i=1, v=0: ''=0
76
+ i=2, v=0: ''=0
77
+ i=3, v=1: 'あ'=3
78
+ i=4, v=1: 'あ'=3
79
+ i=5, v=1: 'あ'=3
80
+ i=6, v=2: 'あい'=6
81
+ i=7, v=3: 'あいa'=7
82
+ i=8, v=3: 'あいa'=7
83
+ i=9, v=3: 'あいa'=7
84
+ i=10, v=4: 'あいaう'=10
85
+ i=11, v=5: 'あいaうg'=11
86
+ i=12, v=5: 'あいaうg'=11
87
+ i=13, v=5: 'あいaうg'=11
88
+ i=14, v=5: 'あいaうg'=11
89
+ i=15, v=7: 'あいaうg????'=15
90
+ i=16, v=7: 'あいaうg????'=15
91
+ i=17, v=7: 'あいaうg????'=15
92
+ i=18, v=8: 'あいaうg????え'=18
93
+ i=19, v=9: 'あいaうg????えh'=19
94
+ ```