回答編集履歴

6

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

2021/11/04 14:44

投稿

jimbe
jimbe

スコア13219

test CHANGED
@@ -120,11 +120,11 @@
120
120
 
121
121
  int v = c & 0xffff;
122
122
 
123
- if(v < 0xd800 || 0xe000 <= v) return 1;
124
-
125
- if(0xd800 <= v || v < 0xdc00) return 2;
123
+ if(isHighSurrogate(v)) return 2;
126
-
124
+
127
- throw new UnsupportedEncodingException(); //下位サロゲートは例外
125
+ if(isLowSurrogate(v)) throw new UnsupportedEncodingException();
126
+
127
+ return 1;
128
128
 
129
129
  }
130
130
 
@@ -134,30 +134,36 @@
134
134
 
135
135
  int v = c & 0xffff;
136
136
 
137
- if(v < 0xd800 || 0xe000 <= v) {
137
+ if(isHighSurrogate(v)) return 4;
138
+
138
-
139
+ if(isLowSurrogate(v)) throw new UnsupportedEncodingException();
140
+
139
- if(v < 128) return 1;
141
+ if(v < 128) return 1;
140
-
142
+
141
- if(v < 2048) return 2;
143
+ if(v < 2048) return 2;
142
-
144
+
143
- return 3;
145
+ return 3;
144
-
146
+
145
- }
147
+ }
148
+
149
+
150
+
146
-
151
+ private static boolean isHighSurrogate(int c) {
152
+
147
- if(0xd800 <= v || v < 0xdc00) {
153
+ return 0xd800 <= c && c < 0xdc00;
148
-
149
- return 4;
154
+
150
-
151
- }
155
+ }
152
-
156
+
157
+
158
+
153
- throw new UnsupportedEncodingException();
159
+ private static boolean isLowSurrogate(int c) {
160
+
161
+ return 0xdc00 <= c && c < 0xe000;
154
162
 
155
163
  }
156
164
 
157
165
  }
158
166
 
159
-
160
-
161
167
  ```
162
168
 
163
169
  ```plain

5

コード一部整理

2021/11/04 14:44

投稿

jimbe
jimbe

スコア13219

test CHANGED
@@ -98,17 +98,15 @@
98
98
 
99
99
  int last = start;
100
100
 
101
- for(int i=start, j=0; i<src.length(); ) {
101
+ for(int i=0; last<src.length(); ) {
102
-
102
+
103
- char c = src.charAt(i);
103
+ char c = src.charAt(last);
104
-
104
+
105
- j += getUTF8Length(c);
105
+ i += getUTF8Length(c);
106
-
106
+
107
- if(j > nbyte) break;
107
+ if(i > nbyte) break;
108
-
108
+
109
- i += getUTF16Length(c);
109
+ last += getUTF16Length(c);
110
-
111
- last = i;
112
110
 
113
111
  }
114
112
 

4

バグ修正

2021/11/04 12:24

投稿

jimbe
jimbe

スコア13219

test CHANGED
@@ -78,7 +78,7 @@
78
78
 
79
79
 
80
80
 
81
- for(int start=0, v=0; (v=countChar(data,10,start)) > 0; start+=v) {
81
+ for(int start=0, v=0; (v=countChar(data,13,start)) > 0; start+=v) {
82
82
 
83
83
  System.out.print("v="+v);
84
84
 
@@ -98,16 +98,18 @@
98
98
 
99
99
  int last = start;
100
100
 
101
- for(int i=start, j=0; i<src.length() && j<=nbyte; ) {
101
+ for(int i=start, j=0; i<src.length(); ) {
102
+
103
+ char c = src.charAt(i);
104
+
105
+ j += getUTF8Length(c);
106
+
107
+ if(j > nbyte) break;
108
+
109
+ i += getUTF16Length(c);
102
110
 
103
111
  last = i;
104
112
 
105
- char c = src.charAt(i);
106
-
107
- i += getUTF16Length(c);
108
-
109
- j += getUTF8Length(c);
110
-
111
113
  }
112
114
 
113
115
  return last - start;
@@ -156,6 +158,8 @@
156
158
 
157
159
  }
158
160
 
161
+
162
+
159
163
  ```
160
164
 
161
165
  ```plain
@@ -204,18 +208,16 @@
204
208
 
205
209
  ------------
206
210
 
207
- v=4: 'あいaう'=10
211
+ v=5: 'あいaうg'=11
208
-
212
+
209
- v=5: 'g????えh'=9
213
+ v=6: '????えhおj'=12
210
-
211
- v=4: 'おjかk'=8
214
+
212
-
213
- v=4: 'きlくl'=8
215
+ v=6: 'かkきlくl'=12
214
-
216
+
215
- v=4: 'けこhd'=8
217
+ v=6: 'けこhdさj'=12
216
-
217
- v=4: 'さjしu'=8
218
+
218
-
219
- v=3: 'すuせ'=7
219
+ v=5: 'しuすuせ'=11
220
+
221
+ v=1: 'そ'=3
220
222
 
221
223
  ```

3

説明修正

2021/11/04 12:15

投稿

jimbe
jimbe

スコア13219

test CHANGED
@@ -32,7 +32,7 @@
32
32
 
33
33
 
34
34
 
35
- ループはしますが内部で文字列編集はしないという方向で、 substring に使える値を返すメソッドにしてみました。
35
+ ループはしますが内部で文字列編集(や getBytes() 等)はしないという方向で、 substring に使える値を返すメソッドにしてみました。
36
36
 
37
37
 
38
38
 

2

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

2021/11/04 11:38

投稿

jimbe
jimbe

スコア13219

test CHANGED
@@ -62,7 +62,7 @@
62
62
 
63
63
  for(int i=0; i<20; i++) {
64
64
 
65
- int v = countChar(data, i);
65
+ int v = countChar(data, i, 0);
66
66
 
67
67
  System.out.print("i="+i+", v="+v);
68
68
 
@@ -72,17 +72,35 @@
72
72
 
73
73
  }
74
74
 
75
+
76
+
77
+ System.out.println("------------");
78
+
79
+
80
+
81
+ for(int start=0, v=0; (v=countChar(data,10,start)) > 0; start+=v) {
82
+
83
+ System.out.print("v="+v);
84
+
85
+ String s = data.substring(start, start+v);
86
+
87
+ System.out.println(": '"+s+"'="+s.getBytes("UTF8").length);
88
+
75
- }
89
+ }
90
+
91
+ }
92
+
93
+
76
94
 
77
95
  //UTF8 にした時に nbyte に収まる文字数を返す.
78
96
 
79
- private static int countChar(String src, int nbyte) throws UnsupportedEncodingException {
97
+ private static int countChar(String src, int nbyte, int start) throws UnsupportedEncodingException {
80
-
98
+
81
- int length = 0;
99
+ int last = start;
82
-
100
+
83
- for(int i=0, j=0; i<src.length() && j<=nbyte; ) {
101
+ for(int i=start, j=0; i<src.length() && j<=nbyte; ) {
84
-
102
+
85
- length = i;
103
+ last = i;
86
104
 
87
105
  char c = src.charAt(i);
88
106
 
@@ -92,7 +110,7 @@
92
110
 
93
111
  }
94
112
 
95
- return length;
113
+ return last - start;
96
114
 
97
115
  }
98
116
 
@@ -184,4 +202,20 @@
184
202
 
185
203
  i=19, v=9: 'あいaうg????えh'=19
186
204
 
205
+ ------------
206
+
207
+ v=4: 'あいaう'=10
208
+
209
+ v=5: 'g????えh'=9
210
+
211
+ v=4: 'おjかk'=8
212
+
213
+ v=4: 'きlくl'=8
214
+
215
+ v=4: 'けこhd'=8
216
+
217
+ v=4: 'さjしu'=8
218
+
219
+ v=3: 'すuせ'=7
220
+
187
221
  ```

1

コード追加

2021/11/04 10:37

投稿

jimbe
jimbe

スコア13219

test CHANGED
@@ -25,3 +25,163 @@
25
25
  といった感じで先頭のバイト値でその文字の必要バイト数が出せます。
26
26
 
27
27
  これを用いて文字数とバイト数を加算していけば、限界バイト数を超えた時点での文字数を得たりできると思いますが、ループなんですよね ^^;
28
+
29
+
30
+
31
+ ----
32
+
33
+
34
+
35
+ ループはしますが内部で文字列編集はしないという方向で、 substring に使える値を返すメソッドにしてみました。
36
+
37
+
38
+
39
+ テスト文字列は shiketa さんの回答から頂きました。
40
+
41
+
42
+
43
+ ```java
44
+
45
+ package teratail_java.q367700;
46
+
47
+
48
+
49
+ import java.io.UnsupportedEncodingException;
50
+
51
+
52
+
53
+ public class Q367700 {
54
+
55
+ public static void main(String[] args) throws UnsupportedEncodingException {
56
+
57
+ final String data = "あいaうg\uD867\uDE3Dえhおjかkきlくlけこhdさjしuすuせそ";
58
+
59
+ System.out.println("'"+data+"'");
60
+
61
+
62
+
63
+ for(int i=0; i<20; i++) {
64
+
65
+ int v = countChar(data, i);
66
+
67
+ System.out.print("i="+i+", v="+v);
68
+
69
+ String s = data.substring(0, v);
70
+
71
+ System.out.println(": '"+s+"'="+s.getBytes("UTF8").length);
72
+
73
+ }
74
+
75
+ }
76
+
77
+ //UTF8 にした時に nbyte に収まる文字数を返す.
78
+
79
+ private static int countChar(String src, int nbyte) throws UnsupportedEncodingException {
80
+
81
+ int length = 0;
82
+
83
+ for(int i=0, j=0; i<src.length() && j<=nbyte; ) {
84
+
85
+ length = i;
86
+
87
+ char c = src.charAt(i);
88
+
89
+ i += getUTF16Length(c);
90
+
91
+ j += getUTF8Length(c);
92
+
93
+ }
94
+
95
+ return length;
96
+
97
+ }
98
+
99
+
100
+
101
+ private static int getUTF16Length(char c) throws UnsupportedEncodingException {
102
+
103
+ int v = c & 0xffff;
104
+
105
+ if(v < 0xd800 || 0xe000 <= v) return 1;
106
+
107
+ if(0xd800 <= v || v < 0xdc00) return 2;
108
+
109
+ throw new UnsupportedEncodingException(); //下位サロゲートは例外
110
+
111
+ }
112
+
113
+
114
+
115
+ private static int getUTF8Length(char c) throws UnsupportedEncodingException {
116
+
117
+ int v = c & 0xffff;
118
+
119
+ if(v < 0xd800 || 0xe000 <= v) {
120
+
121
+ if(v < 128) return 1;
122
+
123
+ if(v < 2048) return 2;
124
+
125
+ return 3;
126
+
127
+ }
128
+
129
+ if(0xd800 <= v || v < 0xdc00) {
130
+
131
+ return 4;
132
+
133
+ }
134
+
135
+ throw new UnsupportedEncodingException();
136
+
137
+ }
138
+
139
+ }
140
+
141
+ ```
142
+
143
+ ```plain
144
+
145
+ 'あいaうg????えhおjかkきlくlけこhdさjしuすuせそ'
146
+
147
+ i=0, v=0: ''=0
148
+
149
+ i=1, v=0: ''=0
150
+
151
+ i=2, v=0: ''=0
152
+
153
+ i=3, v=1: 'あ'=3
154
+
155
+ i=4, v=1: 'あ'=3
156
+
157
+ i=5, v=1: 'あ'=3
158
+
159
+ i=6, v=2: 'あい'=6
160
+
161
+ i=7, v=3: 'あいa'=7
162
+
163
+ i=8, v=3: 'あいa'=7
164
+
165
+ i=9, v=3: 'あいa'=7
166
+
167
+ i=10, v=4: 'あいaう'=10
168
+
169
+ i=11, v=5: 'あいaうg'=11
170
+
171
+ i=12, v=5: 'あいaうg'=11
172
+
173
+ i=13, v=5: 'あいaうg'=11
174
+
175
+ i=14, v=5: 'あいaうg'=11
176
+
177
+ i=15, v=7: 'あいaうg????'=15
178
+
179
+ i=16, v=7: 'あいaうg????'=15
180
+
181
+ i=17, v=7: 'あいaうg????'=15
182
+
183
+ i=18, v=8: 'あいaうg????え'=18
184
+
185
+ i=19, v=9: 'あいaうg????えh'=19
186
+
187
+ ```